Maintainers
Pricing
Enterprise
More
More
Documentation
Try Live
Rankings
Install
Blog
Docs7
Ask
New
Add Docs
Nemo
https://github.com/z4nr34l/nemo
Admin
Missing polyfill for multiple next.js middlewares
Tokens:
18,535
Snippets:
153
Trust Score:
8.9
License:
MIT
Update:
1 year ago
Context
Chat
Benchmark
59
Latest
Show doc for...
Code
Info
Show Results
Tokens:
Raw
Copy
Link
Tokens:
Raw
Copy
Link
### Install NEMO package Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/index.mdx Paste the installation command into your terminal to install the @rescale/nemo package. ```bash @rescale/nemo ``` -------------------------------- ### Install NEMO Package Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/index.mdx Install the NEMO package using a package manager to begin integrating it into your Next.js project. ```shell npm install @rescale/nemo ``` -------------------------------- ### Complete middleware.ts File Example Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/index.mdx This is the complete `middleware.ts` file, combining all previous steps including imports, middleware configuration, and execution optimization. ```typescript import { createMiddleware, type MiddlewareFunctionProps } from '@rescale/nemo'; const middlewares = { '/': [ async ({ request }: MiddlewareFunctionProps) => { console.log('There is NEMO', request.nextUrl.pathname); }, ], }; export const middleware = createMiddleware(middlewares); export const config = { matcher: ['/((?!_next/|_static|_vercel|[\w-]+\.\w+).*)'], }; ``` -------------------------------- ### Complete middleware.ts file example Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/index.mdx This snippet shows the complete middleware.ts file combining all previous steps: importing NEMO, defining middleware functions, and configuring the matcher for optimized execution. ```typescript import { createNEMO, type MiddlewareConfig } from '@rescale/nemo'; const middlewares = { '/': [ async (request) => { console.log('There is NEMO', request.nextUrl.pathname); }, ], } satisfies MiddlewareConfig; export const middleware = createNEMO(middlewares); export const config = { matcher: ['/((?!_next/|_static|_vercel|[\w-]+\.\w+).*)'], }; ``` -------------------------------- ### Run Next.js Development Server Source: https://github.com/z4nr34l/nemo/blob/canary/examples/headers/README.md Commands to start the local development server for a Next.js application. After running, the application will be accessible at `http://localhost:3000` in your browser. ```bash npm run dev # or yarn dev # or pnpm dev # or bun dev ``` -------------------------------- ### Define and apply middleware configuration Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/index.mdx Define your middleware configuration using the MiddlewareConfig type and pass it to the createNEMO function. This example logs a message for requests to the root path. ```typescript import { createNEMO, type MiddlewareConfig } from '@rescale/nemo'; const middlewares: MiddlewareConfig = { '/': [ async (request) => { console.log('There is NEMO', request.nextUrl.pathname); }, ], }; export const middleware = createNEMO(middlewares); ``` -------------------------------- ### Run Next.js Development Server Source: https://github.com/z4nr34l/nemo/blob/canary/examples/next-auth/README.md Commands to start the Next.js development server using npm, yarn, pnpm, or bun. The server will typically run on http://localhost:3000 and auto-update as files are edited. ```bash npm run dev # or yarn dev # or pnpm dev # or bun dev ``` -------------------------------- ### Run Next.js Development Server Source: https://github.com/z4nr34l/nemo/blob/canary/examples/basic/README.md Commands to start the local development server for a Next.js application, making it accessible via http://localhost:3000. The page will auto-update as files are edited. ```bash npm run dev # or yarn dev # or pnpm dev # or bun dev ``` -------------------------------- ### Import createMiddleware Function Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/index.mdx Import the `createMiddleware` function from the `@rescale/nemo` package into your `middleware.ts` file. Create the file if it doesn't exist. ```typescript import { createMiddleware } from '@rescale/nemo'; ``` -------------------------------- ### Initialize NEMO Middleware Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/index.mdx Use the `createMiddleware` function to initialize the NEMO middleware helper, preparing it to accept your custom middleware functions. ```typescript export const middleware = createMiddleware(/* your functions will go here */); ``` -------------------------------- ### Run Next.js Development Server Source: https://github.com/z4nr34l/nemo/blob/canary/examples/cookies/README.md This command starts the Next.js development server, making the application accessible locally. It provides options for different package managers like npm, yarn, pnpm, and bun. ```bash npm run dev # or yarn dev # or pnpm dev # or bun dev ``` -------------------------------- ### Import createNEMO in middleware.ts Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/index.mdx Import the createNEMO function from the @rescale/nemo package into your middleware.ts file. Create the file if it doesn't exist. ```typescript import { createNEMO } from '@rescale/nemo'; ``` -------------------------------- ### Create a middleware helper with createNEMO Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/index.mdx Use the createNEMO function to initialize your Next.js middleware helper. Initially, it can be called without parameters. ```typescript import { createNEMO } from '@rescale/nemo'; export const middleware = createNEMO(/* your functions will go here */); ``` -------------------------------- ### Define Middleware Configuration Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/index.mdx Define your middleware functions within a `middlewares` object, mapping paths to an array of asynchronous functions. Update the `createMiddleware` call to use this configuration. ```typescript import { createMiddleware, type MiddlewareFunctionProps } from '@rescale/nemo'; const middlewares = { '/': [ async ({ request }: MiddlewareFunctionProps) => { console.log('There is NEMO', request.nextUrl.pathname); }, ], }; export const middleware = createMiddleware(middlewares); ``` -------------------------------- ### Install @rescale/nemo Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Instructions for installing the @rescale/nemo library using npm, pnpm, or bun package managers. ```bash npm install @rescale/nemo ``` ```bash pnpm add @rescale/nemo ``` ```bash bun add @rescale/nemo ``` -------------------------------- ### Complete `createNEMO` setup with middleware and configuration (TypeScript) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx Presents a comprehensive TypeScript example demonstrating the full integration of NEMO. It shows how to use `createNEMO` to combine route-specific middleware, global `before` and `after` middleware, and a complete `NemoConfig` object, along with Next.js `matcher` configuration. ```ts import { createNEMO, type MiddlewareConfig, type GlobalMiddlewareConfig, type NemoConfig, } from "@rescale/nemo"; const middlewares = { "/:path*": async (request) => { // middleware functions for /api route }, } satisfies MiddlewareConfig; const globalMiddlewares = { before: async () => { // global middleware function that will be executed before any route middleware }, after: async () => { // global middleware function that will be executed after any route middleware }, } satisfies GlobalMiddlewareConfig; const nemoConfig = { debug: true, // default is false silent: true, // default is false enableTiming: true, // default is false errorHandler: (error, metadata) => { // custom error handler }, } satisfies NemoConfig; export const middleware = createNEMO( middlewares, globalMiddlewares, nemoConfig, ); export const config = { matcher: ["/((?!_next/|_static|_vercel|[\\w-]+\\.\\w+).*)"], }; ``` -------------------------------- ### Install @rescale/nemo via npm, pnpm, or bun Source: https://github.com/z4nr34l/nemo/blob/canary/packages/nemo/README.md Instructions for installing the @rescale/nemo library using different package managers. ```bash npm install @rescale/nemo ``` ```bash pnpm add @rescale/nemo ``` ```bash bun add @rescale/nemo ``` -------------------------------- ### Optimize Middleware Execution with Matcher Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/index.mdx Optimize your middleware by defining a `config.matcher` object. This prevents the middleware from executing on internal Next.js routes, static files, or Vercel internals, improving performance. ```typescript export const config = { matcher: ['/((?!_next/|_static|_vercel|[\w-]+\.\w+).*)'], }; ``` -------------------------------- ### Optimize middleware execution with matcher Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/index.mdx Prevent your middleware from executing on every single request by defining a config object with a matcher. This optimizes performance by excluding Next.js internals, static files, and Vercel internals. ```typescript export const config = { matcher: ['/((?!_next/|_static|_vercel|[\w-]+\.\w+).*)'], }; ``` -------------------------------- ### Matcher Example: Simple Route Pattern Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Illustrates a simple route pattern that matches the `/dashboard` URL exactly. ```plaintext /dashboard ``` -------------------------------- ### Defining Global Before and After Middlewares Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx This example demonstrates how to configure global middleware functions that execute for all routes. It shows the `before` middleware running before any route-specific middleware and the `after` middleware running after all route middleware. ```ts import { type GlobalMiddlewareConfig } from "@rescale/nemo"; const globalMiddlewares = { before: async (request, event) => { console.log("Before any route middleware"); }, after: async (request, event) => { console.log("After all route middleware"); }, } satisfies GlobalMiddlewareConfig; ``` -------------------------------- ### Creating a Simple Route-Specific Middleware Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx This example shows how to create a basic middleware for a specific route (`/api`) using `createNEMO`. The middleware is a single asynchronous function that logs API requests. ```ts import { createNEMO } from "@rescale/nemo"; const middleware = createNEMO({ // Simple middleware for /api route "/api": async (request, event) => { console.log("API request:", request.nextUrl.pathname); } }); ``` -------------------------------- ### Example `NemoConfig` object definition (TypeScript) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx Provides a TypeScript example of defining the `NemoConfig` object. It demonstrates setting boolean flags for debug, silent, and timing modes, and includes a custom error handler function for centralized error management within the NEMO framework. ```ts import { type NemoConfig } from "@rescale/nemo"; const config = { debug: true, silent: true, enableTiming: true, errorHandler: (error, metadata) => { console.error(error, metadata); }, } satisfies NemoConfig; ``` -------------------------------- ### Middleware Function Schema Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/functions.mdx This example shows all props that are provided for your usage in a middleware function, defining the standard signature for custom middleware. ```TypeScript async ({ request, response, context, event, forward, params }: MiddlewareFunctionProps) => { // function body } ``` -------------------------------- ### Chained Global Middleware Functions (Before) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/configuration.mdx Example of defining multiple global middleware functions in an array that execute sequentially before any route-specific middleware. Each function receives `request` and `context`. ```ts const globalMiddlewares = { // you can also define `after:` here before: [ async ({ request, context }: MiddlewareFunctionProps) => { // middleware functions for /api route }, async ({ request, context }: MiddlewareFunctionProps) => { // middleware functions for /api route } ] }; ``` -------------------------------- ### Chaining Multiple Global Before Middleware Functions Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx This example demonstrates how to define an array of asynchronous functions for the global `before` middleware. These functions will execute in sequence before any route-specific middleware, allowing for multiple pre-processing steps. ```ts const globalMiddlewares = { before: [ async (request, event) => { event.storage.set("startTime", Date.now()); }, async (request, event) => { // Another before middleware } ] }; ``` -------------------------------- ### Single Global Middleware Function (Before) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/configuration.mdx Example of defining a single global middleware function that executes before any route-specific middleware, receiving `request` as part of `MiddlewareFunctionProps`. ```ts const globalMiddlewares = { // you can also define `after:` here before: async ({ request }: MiddlewareFunctionProps) => { // middleware functions for /api route }, }; ``` -------------------------------- ### Implement Basic Path-Based Middleware with NEMO Source: https://github.com/z4nr34l/nemo/blob/canary/packages/nemo/README.md Provides examples of defining simple, parameterized, optional, and regex-matched routes for middleware functions using `createNEMO`, demonstrating how to access path parameters. This showcases the versatility of NEMO's routing capabilities for different use cases. ```typescript import { createNEMO } from '@rescale/nemo'; export const middleware = createNEMO({ // Simple route '/api': async (request) => { // Handle API routes }, // With parameter '/users/:userId': async (request, event) => { // Access parameter console.log(`User ID: ${event.params.userId}`); }, // Optional pattern with custom prefix '/product{-:version}?': async (request, event) => { // event.params.version will be undefined for '/product' // or the version value for '/product-v1' console.log(`Version: ${event.params.version || 'latest'}`); }, // Pattern with custom matching '/files/:filename(.*\.pdf)': async (request, event) => { // Only matches PDF files console.log(`Processing PDF: ${event.params.filename}`); } }); ``` -------------------------------- ### Chained Middleware Functions Definition Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/configuration.mdx Example of defining multiple middleware functions in an array for a single route, which will be executed sequentially. Each function receives `request` and `context`. ```ts const middlewares = { '/api': [ async ({ request, context }: MiddlewareFunctionProps) => { // middleware functions for /api route }, async ({ request, context }: MiddlewareFunctionProps) => { // middleware functions for /api route } ] }; ``` -------------------------------- ### Single Middleware Function Definition Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/configuration.mdx Example of defining a single asynchronous middleware function for a specific route, receiving `request` as part of `MiddlewareFunctionProps`. ```ts const middlewares = { '/api': async ({ request }: MiddlewareFunctionProps) => { // middleware functions for /api route }, }; ``` -------------------------------- ### Configuring a Single Global Before Middleware Function Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx This snippet shows how to define a single asynchronous function as a global `before` middleware. This function executes before any route-specific middleware and can be used to set up initial request data, such as a start time. ```ts const globalMiddlewares = { before: async (request, event) => { console.log("Before route middleware"); event.storage.set("startTime", Date.now()); } }; ``` -------------------------------- ### Initial Supabase SSR Middleware (`_middleware.ts`) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/api-reference/supabase.mdx This code snippet provides the initial setup for a Supabase SSR middleware function (`updateSession`) in `_middleware.ts`. It handles refreshing authentication tokens and managing cookies for server-side rendering with Supabase, as typically copied from Supabase documentation. ```typescript import { createServerClient } from '@supabase/ssr' import { NextResponse } from 'next/server' export async function updateSession(request) { let supabaseResponse = NextResponse.next({ request, }) const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, { cookies: { getAll() { return request.cookies.getAll() }, setAll(cookiesToSet) { cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value)) supabaseResponse = NextResponse.next({ request, }) cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options) ) }, }, } ) // refreshing the auth token await supabase.auth.getUser() return supabaseResponse } ``` -------------------------------- ### Defining Nested Middleware Routes Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx This example demonstrates NEMO's support for nested route definitions. It shows how to apply a parent middleware (`/admin`) that executes before its child routes (`/users`, `/settings`), each with their own specific middleware. ```ts import { createNEMO } from "@rescale/nemo"; const middleware = createNEMO({ "/admin": { // Middleware for /admin route middleware: async (request) => { console.log("Admin section accessed"); }, // Nested routes under /admin "/users": async (request) => { console.log("Admin users section"); }, "/settings": async (request) => { console.log("Admin settings section"); } } }); ``` -------------------------------- ### Implement Structured Logging in NEMO Middleware Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/best-practices.mdx This example shows how to create a structured logging middleware in NEMO. It captures request details, generates a unique request ID for correlation, and logs request start, completion, or failure with relevant context like duration and error information. The `storage` object is used to pass the `requestId` across middleware. ```TypeScript import { createNEMO, NextMiddleware } from '@rescale/nemo'; import { logger } from '@/lib/logger'; const loggingMiddleware: NextMiddleware = async (request, { next, storage }) => { const requestId = crypto.randomUUID(); const start = Date.now(); // Add request ID to storage for cross-middleware correlation storage.set('requestId', requestId); logger.info({ message: 'Request received', requestId, method: request.method, path: request.nextUrl.pathname, userAgent: request.headers.get('user-agent') }); try { const response = await next(); logger.info({ message: 'Request completed', requestId, status: response.status, duration: Date.now() - start }); return response; } catch (error) { logger.error({ message: 'Request failed', requestId, error: error.message, stack: error.stack, duration: Date.now() - start }); throw error; } }; export const middleware = createNEMO([loggingMiddleware, ...otherMiddlewares]); ``` -------------------------------- ### Using NEMO Storage in Middleware Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/context.mdx This example demonstrates how to access and utilize the 'storage' object within a NextMiddleware function. It shows how to check for existing data, fetch it if not present, and then store it for subsequent access, effectively implementing a basic caching pattern for user data. ```TypeScript import type { NextMiddleware } from "@rescale/nemo"; const example: NextMiddleware = async (req, { storage }) => { let user = undefined; if(!storage.has('user')) { user = await fetchUser(); storage.set('user', user); } else { user = storage.get('user'); } if(!user) { return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }); } } ``` -------------------------------- ### Test NEMO Authentication Middleware Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/best-practices.mdx This snippet provides examples of writing comprehensive tests for a NEMO authentication middleware using `NextRequest` and `createMockStorage`. It includes tests for redirecting unauthenticated requests and for successfully processing requests with valid tokens, demonstrating how to mock dependencies like `verifyToken`. ```TypeScript import { NextRequest } from 'next/server'; import { auth } from './app/auth/_middleware'; import { createMockStorage } from '@/lib/test-utils'; describe('Auth middleware', () => { it('should redirect to login when no token is present', async () => { // Arrange const request = new NextRequest('https://example.com/dashboard'); const storage = createMockStorage(); // Act const response = await auth(request, { storage, next: async () => new Response() }); // Assert expect(response.status).toBe(307); expect(response.headers.get('Location')).toBe('/login'); }); it('should proceed and store user when token is valid', async () => { // Arrange const request = new NextRequest('https://example.com/dashboard'); request.cookies.set('auth-token', 'valid-token'); const storage = createMockStorage(); const mockUser = { id: '123', name: 'Test User' }; // Mock verifyToken function jest.mock('@/lib/auth', () => ({ verifyToken: jest.fn().mockResolvedValue(mockUser) })); // Act const response = await auth(request, { storage, next: async () => new Response() }); // Assert expect(response).toBeUndefined(); // No response means middleware passes through expect(storage.get('user')).toEqual(mockUser); }); }); ``` -------------------------------- ### Apply Rate Limiting in Next.js Middleware Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/best-practices.mdx Provides an example of implementing global or route-specific rate limiting in Next.js middleware to protect against abuse and DoS attacks, returning a 429 status for excessive requests. ```ts import { createNEMO, NextMiddleware } from '@rescale/nemo'; import { RateLimiter } from '@/lib/rate-limiter'; const rateLimiter: NextMiddleware = async (request, { storage }) => { const ip = request.ip || request.headers.get('x-forwarded-for') || 'unknown'; const limiter = new RateLimiter(); const { success, limit, remaining, reset } = await limiter.check(ip); if (!success) { return new Response('Too Many Requests', { status: 429, headers: { 'X-RateLimit-Limit': limit.toString(), 'X-RateLimit-Remaining': remaining.toString(), 'X-RateLimit-Reset': reset.toString() } }); } }; export const middleware = createNEMO([rateLimiter, ...otherMiddlewares]); ``` -------------------------------- ### NEMO Storage API Usage Source: https://github.com/z4nr34l/nemo/blob/canary/packages/nemo/README.md Demonstrates how to use NEMO's Storage API to set, get, check existence, and delete data shared between middleware executions. It highlights type-safe retrieval and basic operations. ```typescript import { createNEMO } from '@rescale/nemo'; export default createNEMO({ '/': [ async (req, { storage }) => { // Set values storage.set('counter', 1); storage.set('items', ['a', 'b']); storage.set('user', { id: 1, name: 'John' }); // Check if key exists if (storage.has('counter')) { // Get values (with type safety) const count = storage.get<number>('counter'); const items = storage.get<string[]>('items'); const user = storage.get<{id: number, name: string}>('user'); // Delete a key storage.delete('counter'); } } ] }); ``` -------------------------------- ### Chaining Middleware with Forward Function (TypeScript) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/forward-functions.mdx This example demonstrates the use of the `forward` function within NEMO middleware. It shows how `firstMiddleware` logs a message and forwards its response to `secondMiddleware`, enabling complex middleware logic and chaining. ```TypeScript import { forward, type MiddlewareFunctionProps } from '@rescale/nemo'; const firstMiddleware = async ({ request, response, forward }: MiddlewareFunctionProps) => { console.log('First middleware'); forward(response); }; const secondMiddleware = async ({ request }: MiddlewareFunctionProps) => { console.log('Second middleware'); }; const middlewares = { '/api': [firstMiddleware, secondMiddleware], }; export const middleware = createMiddleware(middlewares); ``` -------------------------------- ### Descriptive Naming for Middleware Functions Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/conventions/functions-naming.mdx Demonstrates the proposal to use descriptive names for middleware functions instead of generic ones, improving code clarity and maintainability. Examples show naming conventions for different application paths. ```typescript export const internal = async ({ request }: MiddlewareFunctionProps) => { // function body } ``` ```typescript export const team = async ({ request }: MiddlewareFunctionProps) => { // function body } ``` ```typescript export const analytics = async ({ request }: MiddlewareFunctionProps) => { // function body } ``` -------------------------------- ### Implementing a Custom NEMO Storage Adapter Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/context.mdx This code provides a template for creating a custom storage adapter for NEMO. To extend the default in-memory storage, you must create a class that inherits from 'StorageAdapter' and implements all its required methods, such as 'get', 'set', 'has', 'delete', 'clear', and iterators. ```TypeScript import { StorageAdapter } from "@rescale/nemo"; export class CustomStorageAdapter extends StorageAdapter { // Implement required methods async get<T>(key: string): T | undefined { // Your implementation return undefined; } async set<T>(key: string, value: T): void { // Your implementation } async has(key: string): boolean { // Your implementation return false; } async delete(key: string): boolean { // Your implementation return false; } async clear(): void { // Your implementation } // Implement other required methods entries(): IterableIterator<[string, unknown]> { // Your implementation return [][Symbol.iterator](); } keys(): IterableIterator<string> { // Your implementation return [][Symbol.iterator](); } values(): IterableIterator<unknown> { // Your implementation return [][Symbol.iterator](); } get size(): number { // Your implementation return 0; } } ``` -------------------------------- ### Manage Shared Data with NEMO Storage API Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Demonstrates how to use the NEMO Storage API to set, get, check existence, and delete data shared between middleware executions. It highlights type-safe retrieval and basic CRUD operations. ```typescript import { createNEMO } from '@rescale/nemo'; export default createNEMO({ '/': [ async (req, { storage }) => { // Set values storage.set('counter', 1); storage.set('items', ['a', 'b']); storage.set('user', { id: 1, name: 'John' }); // Check if key exists if (storage.has('counter')) { // Get values (with type safety) const count = storage.get<number>('counter'); const items = storage.get<string[]>('items'); const user = storage.get<{id: number, name: string}>('user'); // Delete a key storage.delete('counter'); } } ] }); ``` -------------------------------- ### Accessing Route Parameters in Middleware Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/functions.mdx This example shows how to retrieve route parameters from the URL path using the `params` property, similar to Next.js pages/routes/layouts but without awaiting. It logs the 'slug' parameter from a URL like '/team/123', providing immediate access to dynamic route segments. ```TypeScript // Example URL: /team/123 // Matcher: '/team/:slug' async ({ params }: MiddlewareFunctionProps) => { console.log(params().slug); // Output: 123 } ``` -------------------------------- ### NEMO Route Matching: Language-Specific Blog Routes (TypeScript) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/advanced-matching.mdx An example demonstrating how to create routes that only match specific language codes (e.g., 'en' or 'cn') for a blog section. This uses the specific value constraint to filter `lang`. ```typescript const nemo = new NEMO({ "/:lang(en|cn)/blog/:postId": [ // Only matches English and Chinese blog pages (req) => { const { lang, postId } = req.params; // lang will be either 'en' or 'cn' return NextResponse.next(); }, ], }); ``` -------------------------------- ### NEMO Route Matching: Combining Multiple Parameter Constraints (TypeScript) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/advanced-matching.mdx Shows how to apply multiple parameter constraints within a single NEMO route. This example combines specific value matching for `env` and exclusion for `resource`, allowing fine-grained control over route parameters. ```typescript const nemo = new NEMO({ "/:project/:env(dev|staging|prod)/:resource(!secrets)": [ (req) => { const { project, env, resource } = req.params; // env will be either 'dev', 'staging', or 'prod' // resource will be anything EXCEPT 'secrets' return NextResponse.next(); }, ], }); ``` -------------------------------- ### NEMO Route Matching: Excluding Admin/Settings Areas (TypeScript) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/advanced-matching.mdx An example showing how to apply middleware to routes, excluding specific administrative or settings areas using the exclusion constraint (`!admin|!settings`). This ensures the middleware runs for all areas except the specified ones. ```typescript const nemo = new NEMO({ "/:area(!admin|!settings)/:page": [ // This middleware runs for any routes EXCEPT in admin or settings areas (req) => { const { area, page } = req.params; return NextResponse.next(); }, ], }); ``` -------------------------------- ### Basic Middleware Helper Construction Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/configuration.mdx This snippet demonstrates the basic structure for creating a middleware helper using `createMiddleware`, defining both route-specific and global middleware functions for a Next.js application. ```ts const middlewares = { '/api': async ({ request }: MiddlewareFunctionProps) => { // middleware functions for /api route }, }; const globalMiddlewares = { before: async () => { // global middleware function that will be executed before any route middleware }, after: async () => { // global middleware function that will be executed after any route middleware } } export const middleware = createMiddleware(middlewares, globalMiddlewares); ``` -------------------------------- ### Defining a Catch-All Middleware with MiddlewareConfig Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx This snippet demonstrates how to define a catch-all middleware using the `MiddlewareConfig` type, applying a single asynchronous function to all paths (`/:path*`) to log the request pathname. ```ts import { type MiddlewareConfig } from "@rescale/nemo"; const middlewares = { "/:path*": async (request, event) => { console.log("There is NEMO", request.nextUrl.pathname); }, } satisfies MiddlewareConfig; ``` -------------------------------- ### Proposed Project File Structure Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/conventions/project-structure.mdx This snippet illustrates the recommended directory and file organization for the project. It details the `app` folder's division into `(internal)` and `(public)` sections, the `components` directory for UI elements, and the placement of global files like `middleware.ts` and `package.json`. It highlights the use of `_middleware.ts` for route-specific logic and `layout.tsx`/`page.tsx` for page definitions. ```JSX <Files> <Folder name="app" defaultOpen> <Folder name="(internal)" defaultOpen> <File name="_middleware.ts" icon={<SquareFunctionIcon className="text-amber-500"/>}/> <Folder name="[slug]" defaultOpen> <File name="_middleware.ts" icon={<SquareFunctionIcon className="text-amber-500"/>}/> <File name="layout.tsx" /> <File name="page.tsx" /> </Folder> </Folder> <Folder name="(public)" defaultOpen> <File name="layout.tsx" /> <File name="page.tsx" /> </Folder> <File name="_middleware.ts" icon={<SquareFunctionIcon className="text-amber-500"/>}/> <File name="layout.tsx" /> <File name="page.tsx" /> <File name="global.css" /> </Folder> <Folder name="components"> <File name="button.tsx" /> <File name="tabs.tsx" /> <File name="dialog.tsx" /> </Folder> <File name="middleware.ts" icon={<SquareFunctionIcon className="text-amber-500"/>}/> <File name="package.json" /> </Files> ``` -------------------------------- ### Implement Basic Path-Based Middleware with NEMO Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Demonstrates how to define and use basic path-based middleware functions in NEMO, including simple routes, routes with parameters, optional patterns, and custom matching patterns. ```typescript import { createNEMO } from '@rescale/nemo'; export const middleware = createNEMO({ // Simple route '/api': async (request) => { // Handle API routes }, // With parameter '/users/:userId': async (request, event) => { // Access parameter console.log(`User ID: ${event.params.userId}`); }, // Optional pattern with custom prefix '/product{-:version}?': async (request, event) => { // event.params.version will be undefined for '/product' // or the version value for '/product-v1' console.log(`Version: ${event.params.version || 'latest'}`); }, // Pattern with custom matching '/files/:filename(.*\.pdf)': async (request, event) => { // Only matches PDF files console.log(`Processing PDF: ${event.params.filename}`); } }); ``` -------------------------------- ### NEMO API Reference: NemoConfig Interface Source: https://github.com/z4nr34l/nemo/blob/canary/packages/nemo/README.md Details the configuration options available for `createNEMO`, including debug mode, error handling, and storage adapters. ```APIDOC NemoConfig: interface NemoConfig { debug?: boolean; silent?: boolean; errorHandler?: ErrorHandler; enableTiming?: boolean; storage?: StorageAdapter | (() => StorageAdapter); } ``` -------------------------------- ### NEMO API Reference: createNEMO Function Source: https://github.com/z4nr34l/nemo/blob/canary/packages/nemo/README.md Documents the `createNEMO` function, which is the primary entry point for composing middleware, detailing its parameters and return type. ```APIDOC createNEMO: function createNEMO( middlewares: MiddlewareConfig, globalMiddleware?: GlobalMiddlewareConfig, config?: NemoConfig ): NextMiddleware description: Creates a composed middleware function with enhanced features: - Executes middleware in order (global before → path-matched middleware → global after) - Provides shared storage context between middleware functions - Handles errors with custom error handlers - Supports custom storage adapters ``` -------------------------------- ### Simple Route Matching for Middlewares Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/configuration.mdx Illustrates a simple route matcher for the `middlewares` object, where a specific path like `/api` is used to define middleware functions. ```ts const middlewares = { '/api': // middleware functions for /api route }; ``` -------------------------------- ### Constrain NEMO Route Parameter to Specific Values Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Example demonstrating how to constrain a route parameter to match only a predefined set of values using a regular expression. The middleware will only execute if the :lang parameter is 'en' or 'cn'. ```typescript // Match only if :lang is either 'en' or 'cn' const nemo = new NEMO({ "/:lang(en|cn)/settings": [ // This middleware only runs for /en/settings or /cn/settings (req) => { const { lang } = req.params; // lang will be either 'en' or 'cn' return NextResponse.next(); }, ], }); ``` -------------------------------- ### Implementing Deeply Nested Routes with URL Parameters Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx This snippet showcases how to create complex, deeply nested middleware structures that can capture URL parameters. It outlines a `/shop` route with nested categories and products, where each level can have its own middleware. ```ts import { createNEMO } from "@rescale/nemo"; const middleware = createNEMO({ "/shop": { middleware: shopMiddleware, "/categories": { middleware: categoriesMiddleware, "/:categoryId": { middleware: categoryMiddleware, "/products": { middleware: productsMiddleware, "/:productId": productMiddleware } } } } }); ``` -------------------------------- ### API Reference: forward Prop Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/1.4/forward-functions.mdx Documentation for the `forward` function prop available in middleware, detailing its type and functionality. ```APIDOC Prop: `forward` Type: `(response: MiddlewareReturn) => void` Description: The `forward` function allows you to forward the response from one middleware function to another. This can be useful for chaining middleware functions together and creating more complex middleware logic. ``` -------------------------------- ### NEMO Built-in Logging Capabilities Source: https://github.com/z4nr34l/nemo/blob/canary/packages/nemo/README.md Shows how to use NEMO's integrated logger via the `event` object for different log levels: `log` (debug), `error`, and `warn`. It explains how debug logs are conditionally displayed based on configuration and notes the consistent `[NEMO]` prefix. ```typescript import { createNEMO } from '@rescale/nemo'; import { NextResponse } from 'next/server'; export default createNEMO({ '/api': async (request, event) => { // Debug logs (only shown when debug: true in config) event.log('Processing API request', request.nextUrl.pathname); try { // Your API logic const result = await processRequest(request); event.log('Request processed successfully', result); return NextResponse.json(result); } catch (error) { // Error logs (always shown) event.error('Failed to process request', error); // Warning logs (always shown) event.warn('This endpoint will be deprecated soon'); return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); } } }, undefined, { debug: true }); ``` -------------------------------- ### Constrain NEMO Route Parameter to Exclude Specific Values Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Example demonstrating how to constrain a route parameter to exclude specific values using a negative lookahead regular expression. The middleware will not execute if the :path parameter is 'api'. ```typescript // Exclude specific values from matching const nemo = new NEMO({ "/:path(!api)/:subpath": [ // This middleware runs for any /:path/:subpath EXCEPT when path is 'api' // e.g., /docs/intro will match, but /api/users will not (req) => { const { path, subpath } = req.params; return NextResponse.next(); }, ], }); ``` -------------------------------- ### NEMO Route Matcher: Simple Path Source: https://github.com/z4nr34l/nemo/blob/canary/packages/nemo/README.md Illustrates a simple route pattern that exactly matches a specific URL path. ```plaintext /dashboard ``` -------------------------------- ### Chaining Multiple Middleware Functions for a Route Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx This snippet illustrates how to assign an array of middleware functions to a single route (`/auth`). The functions execute sequentially, demonstrating how to store and retrieve data in `event.storage` to measure request processing time. ```ts import { createNEMO } from "@rescale/nemo"; const middleware = createNEMO({ // Multiple middleware functions for /auth route "/auth": [ // First function in chain async (request, event) => { event.storage.set("startTime", Date.now()); }, // Second function in chain async (request, event) => { const startTime = event.storage.get("startTime"); console.log(`Request processing time: ${Date.now() - startTime}ms`); } ] }); ``` -------------------------------- ### Configure Global Middleware with NEMO Source: https://github.com/z4nr34l/nemo/blob/canary/packages/nemo/README.md Shows how to define global `before` and `after` middleware functions in NEMO, allowing common logic like logging, authentication, and cleanup to be applied across multiple routes. This provides a centralized way to manage cross-cutting concerns. ```typescript import { createNEMO } from '@rescale/nemo'; export default createNEMO({ '/api{/*path}': apiMiddleware }, { before: [loggerMiddleware, authMiddleware], after: cleanupMiddleware }); ``` -------------------------------- ### API Reference: createNEMO Function Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Documents the `createNEMO` function, which creates a composed middleware function. It details its parameters (`middlewares`, `globalMiddleware`, `config`) and its enhanced features like execution order, shared storage, error handling, and custom storage adapters. ```APIDOC function createNEMO( middlewares: MiddlewareConfig, globalMiddleware?: GlobalMiddlewareConfig, config?: NemoConfig ): NextMiddleware ``` -------------------------------- ### Define OR Patterns for Path Matching Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Shows how to use parentheses and the pipe character (|) to define multiple alternative patterns that a route can match, providing flexibility in routing. ```plaintext /(auth|login) ``` -------------------------------- ### Configure Cross-Request Caching with Redis Adapter Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/best-practices.mdx Illustrates how to set up custom cross-request caching for Next.js middleware using a Redis adapter, emphasizing the need for fast execution to maintain low TTFB. ```ts import { createNEMO } from '@rescale/nemo'; import { RedisAdapter } from "@/lib/nemo/redis"; export const middleware = createNEMO(middlewares, globalMiddleware, { storage: () => new RedisAdapter() }); ``` -------------------------------- ### Understand Parent Route Matching with Nested Children in NEMO Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/nesting.mdx Clarifies that parent routes which contain nested children in NEMO middleware will match both their exact base path and any of their defined child paths. For example, a route configured for `/parent` with a `/child` nested under it will activate for both `/parent` and `/parent/child`. ```ts // This matches both /parent and /parent/child const middleware = createNEMO({ "/parent": { middleware: parentMiddleware, "/child": childMiddleware } }); ``` -------------------------------- ### Compose Next.js Middleware with NEMO Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Demonstrates how to use `createNEMO` to define and compose middleware functions for different URL patterns, including simple routes, nested routes with parameters, and pattern matching. It shows context sharing via storage and hierarchical execution. ```typescript import { createNEMO } from '@rescale/nemo'; export default createNEMO({ // Simple route with middleware chain '/api': [ // First middleware in the chain async (request, { storage }) => { storage.set('timestamp', Date.now()); // Continues to the next middleware }, // Second middleware accesses shared storage async (request, { storage }) => { const timestamp = storage.get('timestamp'); console.log(`Request started at: ${timestamp}`); } ], // Nested routes using object notation '/dashboard': { // This middleware runs on /dashboard middleware: async (request) => { console.log('Dashboard root'); }, // Nested route with parameter '/:teamId': { // This middleware runs on /dashboard/:teamId middleware: async (request, { params }) => { console.log(`Team dashboard: ${params.teamId}`); }, // Further nesting with additional parameter '/users/:userId': async (request, { params }) => { console.log(`Team user: ${params.teamId}, User: ${params.userId}`); } }, // Another nested route under /dashboard '/settings': async (request) => { console.log('Dashboard settings'); } }, // Pattern matching multiple routes '/(auth|login)': async (request) => { console.log('Auth page'); } }); ``` -------------------------------- ### NEMO Route Matching: Using Regular Expressions for ID Constraints (TypeScript) Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/advanced-matching.mdx Demonstrates how to use full regular expressions within NEMO route parameter constraints. This example constrains the `id` parameter to match exactly 5 digits using `[0-9]{5}`. ```typescript const nemo = new NEMO({ "/:id([0-9]{5})/profile": [ // Only matches if id consists of exactly 5 digits (req) => { const { id } = req.params; return NextResponse.next(); }, ], }); ``` -------------------------------- ### API Reference: NemoConfig Options Interface Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Defines the `NemoConfig` interface, outlining available options for configuring NEMO, including debug mode, silent mode, error handler, timing, and storage adapter. ```APIDOC interface NemoConfig { debug?: boolean; silent?: boolean; errorHandler?: ErrorHandler; enableTiming?: boolean; storage?: StorageAdapter | (() => StorageAdapter); } ``` -------------------------------- ### Utilize NEMO's Built-in Logging Capabilities Source: https://github.com/z4nr34l/nemo/blob/canary/README.md Shows how to use NEMO's event.log, event.error, and event.warn methods for consistent logging. It explains debug-conditional logs and always-shown error/warning logs, maintaining the '[NEMO]' prefix. ```typescript import { createNEMO } from '@rescale/nemo'; export default createNEMO({ '/api': async (request, event) => { // Debug logs (only shown when debug: true in config) event.log('Processing API request', request.nextUrl.pathname); try { // Your API logic const result = await processRequest(request); event.log('Request processed successfully', result); return NextResponse.json(result); } catch (error) { // Error logs (always shown) event.error('Failed to process request', error); // Warning logs (always shown) event.warn('This endpoint will be deprecated soon'); return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); } } }, undefined, { debug: true }); ``` -------------------------------- ### APIDOC: GlobalMiddlewareConfig Object Structure Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx Defines the structure for global middleware configuration in NEMO. It includes `before` and `after` properties, which can be single middleware functions or arrays of functions, executed for all routes. ```APIDOC GlobalMiddlewareConfig: Type: object Properties: before: MiddlewareFunction | MiddlewareFunction[] after: MiddlewareFunction | MiddlewareFunction[] MiddlewareFunction: (request: Request, event: Event) => Promise<void> | void ``` -------------------------------- ### Configuring NEMO with a Custom Storage Instance Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/context.mdx After creating a custom storage adapter, you can integrate it with NEMO by passing a direct instance of your 'CustomStorageAdapter' to the 'storage' option within the 'createNEMO' function. This sets up NEMO to use your custom storage solution. ```TypeScript import { NEMO } from "@rescale/nemo"; import { CustomStorageAdapter } from "./StorageAdapter"; export const middleware = createNEMO(middlewares, globalMiddleware, { storage: new CustomStorageAdapter() }); ``` -------------------------------- ### APIDOC: GlobalMiddlewareConfig.before Property Source: https://github.com/z4nr34l/nemo/blob/canary/apps/docs/content/2.0/configuration.mdx The `before` property of `GlobalMiddlewareConfig` defines middleware functions that execute before any route-specific middleware. It can be a single `MiddlewareFunction` or an array of `MiddlewareFunction`s. ```APIDOC GlobalMiddlewareConfig.before: Type: MiddlewareFunction | MiddlewareFunction[] Description: Executes before any route-specific middleware. ``` -------------------------------- ### Define Named Path Parameters in NEMO Source: https://github.com/z4nr34l/nemo/blob/canary/packages/nemo/README.md Demonstrates how to define a basic named path parameter using the `:paramName` syntax, which captures a segment of the URL path. This allows for dynamic routing based on specific parts of the URL. ```plaintext /dashboard/:team ```