### Install next-zod-route Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Commands to install the library and its Zod dependency using common package managers. ```bash npm install next-zod-route zod ``` ```bash pnpm add next-zod-route zod ``` ```bash yarn add next-zod-route zod ``` -------------------------------- ### Migration Guide: Middleware Changes from v0.1.x to v0.2.0 Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Highlights the changes in the middleware system between v0.1.x and v0.2.0 of `next-zod-route`. The example shows the 'before' syntax for v0.1.x, where middleware returned context data directly. ```typescript // Before (v0.1.x) const authMiddleware = async () => { return { user: { id: 'user-123' } }; }; const route = createZodRoute() .use(authMiddleware) .handler((req, ctx) => { const { user } = ctx.data; return { data: user.id }; }); ``` -------------------------------- ### Install and Run Official Codemod Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Installs and executes the official codemod to automate the migration from Zod v3 to v4. This is the recommended approach for most users. ```bash npx zod-v3-to-v4@latest src/ ``` ```bash npm install -g zod-v3-to-v4 zod-v3-to-v4 src/ ``` -------------------------------- ### Install Zod v4 using npm, yarn, or pnpm Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Commands to install Zod version 4.0.0 or higher using different package managers. ```bash npm install zod@^4.0.0 # Or with yarn yarn add zod@^4.0.0 # Or with pnpm pnpm add zod@^4.0.0 ``` -------------------------------- ### Middleware: Pre/Post Handler Execution Example in TypeScript Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Illustrates how middleware functions can execute code both before and after the main route handler. This example uses `performance.now()` to measure and log the request duration. ```typescript import { type MiddlewareFunction } from 'next-zod-route'; const timingMiddleware: MiddlewareFunction = async ({ next }) => { console.log('Starting request...'); const start = performance.now(); const response = await next(); const duration = performance.now() - start; console.log(`Request took ${duration}ms`); return response; }; ``` -------------------------------- ### Create Reusable Route Client Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Example of creating a centralized route configuration or middleware-enabled route instance. ```typescript import { createZodRoute } from 'next-zod-route'; const route = createZodRoute(); // Create other re-usable route const authRoute = route.use(...); ``` -------------------------------- ### Install Zod v3 for Rollback Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Installs a specific version of Zod v3, allowing for a temporary rollback if issues are encountered after migrating to Zod v4. ```bash npm install zod@^3.23.8 ``` -------------------------------- ### Import Zod v3 and v4 simultaneously during migration Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Example demonstrating how to import both Zod v3 (legacy) and Zod v4 (new) in the same file, useful for incremental migration. ```typescript // You can import both versions if needed during migration import * as z3 from "zod/v3" // Zod v3 (legacy) import * as z4 from "zod/v4" // Zod v4 (new) ``` -------------------------------- ### Implementing Logging, Auth, and Permissions Middleware in TypeScript Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Demonstrates how to create and chain multiple middleware functions (logging, authentication, permissions) using `next-zod-route`. Middleware can add data to the context, modify responses, and access metadata. The example shows how to define metadata with Zod schemas and access context and metadata within the handler. ```typescript import { type MiddlewareFunction, createZodRoute } from 'next-zod-route'; import { z } from 'zod'; const loggingMiddleware: MiddlewareFunction = async ({ next }) => { console.log('Before handler'); const startTime = performance.now(); const response = await next(); const endTime = performance.now() - startTime; console.log(`After handler - took ${Math.round(endTime)}ms`); return response; }; const authMiddleware: MiddlewareFunction = async ({ request, metadata, next }) => { try { const token = request.headers.get('authorization')?.split(' ')[1]; if (metadata?.role !== 'admin') { throw new Error('Unauthorized'); } const user = await validateToken(token); const response = await next({ context: { user }, }); return new Response(response.body, { status: response.status, headers: { ...Object.fromEntries(response.headers.entries()), 'X-User-Id': user.id, }, }); } catch (error) { throw error; } }; const permissionsMiddleware: MiddlewareFunction = async ({ metadata, next }) => { const response = await next({ context: { permissions: metadata?.permissions ?? ['read'] }, }); return response; }; export const GET = createZodRoute() .defineMetadata( z.object({ role: z.enum(['admin', 'user']), permissions: z.array(z.string()).optional(), }), ) .use(loggingMiddleware) .use(authMiddleware) .use(permissionsMiddleware) .handler((request, context) => { const { user, permissions } = context.data; const { role } = context.metadata!; return Response.json({ user, permissions, role }); }); // Dummy function for demonstration async function validateToken(token: string | undefined) { if (token === 'valid-token') { return { id: 'user-123', name: 'Test User' }; } throw new Error('Invalid token'); } ``` -------------------------------- ### Create Reusable Route Builders with Middleware (TypeScript) Source: https://context7.com/melvynx/next-zod-route/llms.txt Shows how to build reusable route builders using `createZodRoute` and middleware for common patterns like error handling and authentication. This example defines a base route with server error handling, an authentication middleware, and an admin check middleware, composing them into `authRoute` and `adminRoute` builders for use in API endpoints. ```typescript // lib/route.ts import { createZodRoute, type MiddlewareFunction } from 'next-zod-route'; import { z } from 'zod'; // Base route with error handling export const route = createZodRoute({ handleServerError: (error) => { console.error('Server error:', error); return Response.json( { error: 'Internal server error', requestId: crypto.randomUUID() }, { status: 500 } ); }, }); // Auth middleware const authMiddleware: MiddlewareFunction< Record, { user: { id: string; role: 'admin' | 'user' } } > = async ({ request, next }) => { const token = request.headers.get('authorization')?.replace('Bearer ', ''); if (!token) { return Response.json({ error: 'Unauthorized' }, { status: 401 }); } // Simplified auth - in reality, verify JWT const user = { id: 'user-123', role: 'user' as const }; return next({ ctx: { user } }); }; // Authenticated route builder export const authRoute = route.use(authMiddleware); // Admin-only route builder const adminCheckMiddleware: MiddlewareFunction< { user: { id: string; role: 'admin' | 'user' } }, { isAdmin: true } > = async ({ ctx, next }) => { if (ctx.user.role !== 'admin') { return Response.json({ error: 'Admin access required' }, { status: 403 }); } return next({ ctx: { isAdmin: true as const } }); }; export const adminRoute = authRoute.use(adminCheckMiddleware); // Usage in app/api/admin/settings/route.ts import { adminRoute } from '@/lib/route'; export const GET = adminRoute.handler((request, context) => { // context.ctx is { user: {...}, isAdmin: true } return { settings: { theme: 'dark', notifications: true } }; }); export const PUT = adminRoute .body(z.object({ theme: z.string(), notifications: z.boolean() })) .handler((request, context) => { const { theme, notifications } = context.body; return { updated: true, settings: { theme, notifications } }; }); ``` -------------------------------- ### Middleware: Early Return (Short-Circuiting) Example in TypeScript Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Illustrates how a middleware can immediately return a `Response` object, effectively short-circuiting the middleware chain and the main handler. This is commonly used for authentication or authorization checks. ```typescript import { type MiddlewareFunction } from 'next-zod-route'; const authMiddleware: MiddlewareFunction = async ({ next }) => { const isAuthed = false; if (!isAuthed) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }); } return next(); }; ``` -------------------------------- ### Define Type-Safe Route Handlers Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Demonstrates how to use createZodRoute to define GET and POST handlers with Zod schemas for params, query, and body validation. ```typescript import { createZodRoute } from 'next-zod-route'; import { z } from 'zod'; const paramsSchema = z.object({ id: z.string() }); const querySchema = z.object({ search: z.string().optional() }); const bodySchema = z.object({ field: z.string() }); export const GET = createZodRoute() .params(paramsSchema) .query(querySchema) .handler((request, context) => { const { id } = context.params; const { search } = context.query; return { id, search }; }); export const POST = createZodRoute() .params(paramsSchema) .query(querySchema) .body(bodySchema) .handler((request, context) => { const { id } = context.params; const { search } = context.query; const { field } = context.body; return NextResponse.json({ id, search, field }), { status: 400 }; }); ``` -------------------------------- ### Define Typed Middleware with Context Inference (TypeScript) Source: https://context7.com/melvynx/next-zod-route/llms.txt Demonstrates how to create type-safe middleware functions using `MiddlewareFunction` from `next-zod-route`. This example shows defining middleware that adds a database connection to the context and another that loads user data based on the existing database connection and request headers. It illustrates context chaining and dependency management between middleware. ```typescript import { createZodRoute, type MiddlewareFunction } from 'next-zod-route'; import { z } from 'zod'; // Define typed middleware that adds database connection to context type DbConnection = { query: (sql: string) => Promise }; const dbMiddleware: MiddlewareFunction< Record, { db: DbConnection } > = async ({ next }) => { const db: DbConnection = { query: async (sql) => { console.log('Executing:', sql); return [{ id: 1 }]; }, }; const response = await next({ ctx: { db } }); // Cleanup after handler completes console.log('Closing database connection'); return response; }; // Typed middleware that depends on previous context const userLoaderMiddleware: MiddlewareFunction< { db: DbConnection }, { currentUser: { id: number } } > = async ({ ctx, request, next }) => { const userId = request.headers.get('x-user-id'); if (!userId) { return new Response('Unauthorized', { status: 401 }); } // Use db from previous middleware const users = await ctx.db.query(`SELECT * FROM users WHERE id = ${userId}`); return next({ ctx: { currentUser: { id: parseInt(userId) } } }); }; // app/api/data/route.ts export const GET = createZodRoute() .use(dbMiddleware) .use(userLoaderMiddleware) .handler((request, context) => { // context.ctx is typed as { db: DbConnection; currentUser: { id: number } } const { db, currentUser } = context.ctx; return { userId: currentUser.id, message: 'Data retrieved successfully', }; }); ``` -------------------------------- ### Middleware: Context Chaining Example in TypeScript Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Demonstrates how middleware functions can pass data down the chain using the `context` object. `middleware1` adds `value1` to the context, which is then accessed and extended with `value2` by `middleware2`. ```typescript import { type MiddlewareFunction } from 'next-zod-route'; const middleware1: MiddlewareFunction = async ({ next }) => { const response = await next({ context: { value1: 'first' }, }); return response; }; const middleware2: MiddlewareFunction = async ({ context, next }) => { console.log(context.value1); // 'first' const response = await next({ context: { value2: 'second' }, }); return response; }; ``` -------------------------------- ### Middleware: Response Modification Example in TypeScript Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Shows how a middleware function can intercept and modify the response returned by the route handler. This example adds a custom header ('X-Custom': 'value') to the response. ```typescript import { type MiddlewareFunction } from 'next-zod-route'; const headerMiddleware: MiddlewareFunction = async ({ next }) => { const response = await next(); return new Response(response.body, { status: response.status, headers: { ...Object.fromEntries(response.headers.entries()), 'X-Custom': 'value', }, }); }; ``` -------------------------------- ### GET /api/profile Source: https://context7.com/melvynx/next-zod-route/llms.txt Retrieves the authenticated user profile using middleware for authentication and logging. ```APIDOC ## GET /api/profile ### Description Fetches the current user profile. Requires a valid Authorization header and includes response timing metrics. ### Method GET ### Endpoint /api/profile ### Parameters #### Headers - **Authorization** (string) - Required - Bearer token for authentication. ### Response #### Success Response (200) - **profile** (object) - User profile details. #### Response Example { "profile": { "id": "user-123", "email": "user@example.com", "name": "John Doe" } } ``` -------------------------------- ### GET /api/admin/users Source: https://context7.com/melvynx/next-zod-route/llms.txt Retrieves a list of users, protected by permission-based middleware using defined metadata. ```APIDOC ## GET /api/admin/users ### Description Fetches a list of users. This endpoint requires specific permissions defined in the route metadata, which are validated by a custom middleware. ### Method GET ### Endpoint /api/admin/users ### Parameters #### Request Headers - **x-user-permissions** (string) - Required - Comma-separated list of user permissions. ### Response #### Success Response (200) - **users** (array) - List of user objects. - **meta** (object) - Metadata including authorization status and rate limits. #### Response Example { "users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}], "meta": {"authorized": true, "permissions": ["read:users"], "rateLimit": 100} } ``` -------------------------------- ### next-zod-route Middleware with Zod v4 Validation Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Example of a `next-zod-route` middleware function utilizing Zod v4 for request validation. It demonstrates efficient UUID validation and improved error handling. ```typescript import { type MiddlewareFunction } from 'next-zod-route'; import { z } from 'zod'; const validationMiddleware: MiddlewareFunction = async ({ request, next }) => { try { const userSchema = z.object({ userId: z.uuid(), // Updated for v4 efficiency role: z.enum(['admin', 'user'], { error: "Invalid role" }) }); // Your validation logic const userData = userSchema.parse(await request.json()); return next({ ctx: { user: userData } }); } catch (error) { return Response.json({ error: 'Validation failed' }, { status: 400 }); } }; ``` -------------------------------- ### POST /api/examples Source: https://context7.com/melvynx/next-zod-route/llms.txt Demonstrates handling POST requests with body validation and custom Response objects. ```APIDOC ## POST /api/examples ### Description Processes an action based on the request body. Returns a JSON response with custom headers and status codes. ### Method POST ### Endpoint /api/examples ### Parameters #### Request Body - **action** (string) - Required - The action to be performed (e.g., 'process', 'redirect', 'stream'). ### Response #### Success Response (201) - **action** (string) - The processed action. - **processed** (boolean) - Confirmation status. #### Response Example { "action": "process", "processed": true } ``` -------------------------------- ### Run Test Suite Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Executes the project's test suite using common package managers to verify the migration's success and ensure no regressions were introduced. ```bash npm test ``` ```bash yarn test ``` ```bash pnpm test ``` -------------------------------- ### Implement Pre and Post Handler Logic Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/01-middleware-next.md Shows how to wrap the next() call to execute code before and after the route handler, useful for logging or performance tracking. ```typescript const loggingMiddleware = async ({ next }) => { console.log('Starting request...'); const start = performance.now(); const response = await next(); const duration = performance.now() - start; console.log(`Request took ${duration}ms`); return response; }; ``` -------------------------------- ### Run Project Test Suite Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Command to execute the project test suite using the Vitest runner. ```shell pnpm test ``` -------------------------------- ### createZodRoute Factory Source: https://context7.com/melvynx/next-zod-route/llms.txt Initializes the route handler builder with optional custom server-side error handling. ```APIDOC ## createZodRoute ### Description Initializes a new route handler builder instance. Allows for custom error handling logic to be injected into the route lifecycle. ### Method N/A (Factory Function) ### Parameters #### Request Body - **handleServerError** (function) - Optional - A function that receives an Error and returns a Response object. ### Request Example const route = createZodRoute({ handleServerError: (err) => new Response('Error', { status: 500 }) }); ``` -------------------------------- ### Implement Route Handlers Source: https://context7.com/melvynx/next-zod-route/llms.txt Shows how to define route handlers that return plain objects for automatic JSON serialization or full Response objects for custom control. ```typescript import { createZodRoute } from 'next-zod-route'; import { z } from 'zod'; export const GET = createZodRoute() .query(z.object({ format: z.enum(['json', 'simple']).default('json') })) .handler((request, context) => { if (context.query.format === 'simple') { return { message: 'Hello, World!' }; } return { data: { greeting: 'Hello, World!' }, timestamp: new Date().toISOString() }; }); export const POST = createZodRoute() .body(z.object({ action: z.string() })) .handler((request, context) => { const { action } = context.body; if (action === 'redirect') { return Response.redirect('https://example.com', 302); } return Response.json({ action, processed: true }, { status: 201 }); }); ``` -------------------------------- ### Define Metadata and Middleware for Route Protection Source: https://context7.com/melvynx/next-zod-route/llms.txt Demonstrates how to define a Zod schema for metadata, create a middleware function to enforce permissions, and apply these to routes using defineMetadata. ```typescript import { createZodRoute, type MiddlewareFunction } from 'next-zod-route'; import { z } from 'zod'; const permissionsMetadataSchema = z.object({ requiredPermissions: z.array(z.string()).optional(), rateLimit: z.number().optional(), }); type PermissionsMetadata = z.infer; const permissionMiddleware: MiddlewareFunction< Record, { authorized: boolean }, PermissionsMetadata > = async ({ request, metadata, next }) => { const userPermissions = request.headers.get('x-user-permissions')?.split(',') || []; if (!metadata?.requiredPermissions?.length) { return next({ ctx: { authorized: true } }); } const hasPermissions = metadata.requiredPermissions.every( (perm) => userPermissions.includes(perm) ); if (!hasPermissions) { return new Response( JSON.stringify({ error: 'Forbidden', required: metadata.requiredPermissions }), { status: 403, headers: { 'Content-Type': 'application/json' } } ); } return next({ ctx: { authorized: true } }); }; const protectedRoute = createZodRoute() .defineMetadata(permissionsMetadataSchema) .use(permissionMiddleware); export const GET = protectedRoute .metadata({ requiredPermissions: ['read:users'], rateLimit: 100 }) .handler((request, context) => { const { authorized } = context.ctx; const { requiredPermissions, rateLimit } = context.metadata!; return { users: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }], meta: { authorized, permissions: requiredPermissions, rateLimit } }; }); ``` -------------------------------- ### Import Zod v4 for new projects or clean migrations Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Recommended import statement for Zod v4 in new projects or after a successful migration. ```typescript import { z } from "zod" // Zod v4 (recommended) ``` -------------------------------- ### Migrate Context Middleware to v0.2.0 Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/01-middleware-next.md Demonstrates the transition from v0.1.x implicit context returns to the v0.2.0 explicit next() function call pattern. ```typescript const authMiddleware = async ({ next }) => { const response = await next({ ctx: { user: { id: 'user-123' } }, }); return response; }; const route = createZodRoute() .use(authMiddleware) .handler((req, { ctx }) => { const { user } = ctx; return { data: user.id }; }); ``` -------------------------------- ### Configure Custom Server Error Handler Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Shows how to define a custom error handler within createZodRoute to catch specific error types and return appropriate HTTP responses instead of generic 500 errors. ```typescript import { createZodRoute } from 'next-zod-route'; class CustomError extends Error { constructor(message: string, public status: number = 400) { super(message); this.name = 'CustomError'; } } const safeRoute = createZodRoute({ handleServerError: (error: Error) => { if (error instanceof CustomError) { return new Response(JSON.stringify({ message: error.message }), { status: error.status }); } return new Response(JSON.stringify({ message: 'Internal server error' }), { status: 500 }); }, }); export const GET = safeRoute .use(async () => { throw new CustomError('Middleware error', 400); }) .handler((request, context) => { throw new CustomError('Handler error', 400); }); ``` -------------------------------- ### POST /api/organizations/[organizationId]/projects Source: https://context7.com/melvynx/next-zod-route/llms.txt Creates a project within an organization, validating path parameters, query strings, and the request body. ```APIDOC ## POST /api/organizations/[organizationId]/projects ### Description Creates a new project for a specific organization with comprehensive request validation. ### Method POST ### Endpoint /api/organizations/{organizationId}/projects ### Parameters #### Path Parameters - **organizationId** (uuid) - Required - The ID of the organization. #### Query Parameters - **notify** (boolean) - Optional - Whether to send a notification (default: false). #### Request Body - **name** (string) - Required - Project name. - **settings** (object) - Required - Project configuration settings. ### Request Example { "name": "New Project", "settings": { "isPublic": true, "maxMembers": 50 } } ### Response #### Success Response (201) - **project** (object) - The created project object. - **notified** (boolean) - Status of the notification process. #### Response Example { "project": { "id": "uuid-456", "name": "New Project" }, "notified": true } ``` -------------------------------- ### Implement Middleware with Context and Response Modification Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Demonstrates how to define a middleware function that executes logic before and after the route handler, modifies the request context, and updates the final response headers. ```typescript import { type MiddlewareFunction } from 'next-zod-route'; const authMiddleware: MiddlewareFunction = async ({ next }) => { console.log('Checking auth...'); const response = await next({ context: { user: { id: 'user-123' } }, }); return new Response(response.body, { headers: { ...Object.fromEntries(response.headers.entries()), 'X-User-Id': 'user-123', }, }); }; const route = createZodRoute() .use(authMiddleware) .handler((req, ctx) => { const { user } = ctx.data; return { data: user.id }; }); ``` -------------------------------- ### Short-circuit Requests with Early Returns Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/01-middleware-next.md Shows how to return a Response object directly from middleware to stop the chain execution, such as for authentication failures. ```typescript const authMiddleware = async ({ next }) => { const isAuthed = false; if (!isAuthed) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }); } return next(); }; ``` -------------------------------- ### Chaining Multiple Middleware Functions Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/01-middleware-next.md Shows how to chain multiple middleware components by passing the context forward. Each middleware receives the previous context and merges its own data before calling next. ```typescript const middleware1 = async ({ next }) => { return next({ ctx: { value1: 'first' } }); }; const middleware2 = async ({ context, next }) => { return next({ ctx: { ...context, value2: 'second' } }); }; ``` -------------------------------- ### Accessing Request Data in Middleware Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/01-middleware-next.md Demonstrates how to extract headers from the request object and pass them into the context using the next function. This replaces older return-based patterns with a more robust context-injection approach. ```typescript const middleware = async ({ request, next }) => { const token = request.headers.get('authorization'); return next({ ctx: { token } }); }; ``` -------------------------------- ### Enforce Route Permissions via Metadata Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/01-middleware-next.md A robust pattern using Zod schemas to define route metadata and a middleware to validate user permissions against that metadata. ```typescript const permissionsMetadataSchema = z.object({ requiredPermissions: z.array(z.string()).optional(), }); const permissionCheckMiddleware = async ({ next, metadata, request }) => { const token = request.headers.get('authorization')?.split(' ')[1]; const userPermissions = await getUserPermissionsFromToken(token); if (!metadata?.requiredPermissions || metadata.requiredPermissions.length === 0) { return next({ ctx: { authorized: true } }); } const hasAllPermissions = metadata.requiredPermissions.every((permission) => userPermissions.includes(permission)); if (!hasAllPermissions) { return new Response(JSON.stringify({ error: 'Forbidden' }), { status: 403 }); } return next({ ctx: { authorized: true } }); }; const route = createZodRoute() .defineMetadata(permissionsMetadataSchema) .use(permissionCheckMiddleware) .metadata({ requiredPermissions: ['read:users'] }) .handler((request, context) => { return Response.json({ data: 'Protected data' }); }); ``` -------------------------------- ### Handle API Responses Source: https://github.com/melvynx/next-zod-route/blob/main/README.md Shows the two ways to return responses: returning a standard Response object or a plain object that is automatically serialized to JSON. ```typescript // Return a Response object directly return NextResponse.json({ data: 'value' }, { status: 200 }); // Return a plain object (auto-serialized to JSON with 200 status) return { data: 'value' }; ``` -------------------------------- ### Chain Context Across Middleware Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/01-middleware-next.md Illustrates how multiple middleware functions can pass and accumulate context values through the chain. ```typescript const middleware1 = async ({ next }) => { const response = await next({ ctx: { value1: 'first' }, }); return response; }; const middleware2 = async ({ context, next }) => { console.log(context.value1); const response = await next({ ctx: { value2: 'second' }, }); return response; }; ``` -------------------------------- ### Implement Middleware with next-zod-route Source: https://context7.com/melvynx/next-zod-route/llms.txt This snippet demonstrates how to use the `.use()` method to add middleware functions in Next.js API routes. Middleware can execute before the main handler to perform tasks like authentication, logging, or modifying the request/response context. The `context.ctx` object merges context from all applied middleware. ```typescript import { createZodRoute, type MiddlewareFunction } from 'next-zod-route'; import { z } from 'zod'; // Authentication middleware - adds user to context const authMiddleware: MiddlewareFunction< Record, { user: { id: string; email: string } } > = async ({ request, next }) => { const token = request.headers.get('authorization')?.split(' ')[1]; if (!token) { return new Response( JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' } } ); } // Validate token and get user (simplified) const user = { id: 'user-123', email: 'user@example.com' }; return next({ ctx: { user } }); }; // Logging middleware - executes before and after handler const loggingMiddleware: MiddlewareFunction = async ({ request, next }) => { console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`); const startTime = performance.now(); const response = await next(); const duration = Math.round(performance.now() - startTime); console.log(`[${new Date().toISOString()}] Completed in ${duration}ms`); // Add timing header to response return new Response(response.body, { status: response.status, headers: { ...Object.fromEntries(response.headers.entries()), 'X-Response-Time': `${duration}ms`, }, }); }; // app/api/profile/route.ts export const GET = createZodRoute() .use(loggingMiddleware) .use(authMiddleware) .handler((request, context) => { // context.ctx contains merged context from all middleware const { user } = context.ctx; return { profile: { id: user.id, email: user.email, name: 'John Doe', }, }; }); // curl http://localhost:3000/api/profile -H "Authorization: Bearer token123" // Returns: { "profile": { "id": "user-123", "email": "user@example.com", "name": "John Doe" } } // Response includes X-Response-Time header ``` -------------------------------- ### Create Zod Route Handler Builder Source: https://context7.com/melvynx/next-zod-route/llms.txt The `createZodRoute` function is the main factory for building route handlers. It can be used with basic configuration or extended with a custom server-side error handler to manage exceptions gracefully. The returned builder can then be used to define route logic. ```typescript import { createZodRoute } from 'next-zod-route'; // Basic usage - creates a route builder const route = createZodRoute(); // With custom error handling class CustomError extends Error { constructor(message: string, public status: number = 400) { super(message); this.name = 'CustomError'; } } const routeWithErrorHandler = createZodRoute({ handleServerError: (error: Error) => { if (error instanceof CustomError) { return new Response( JSON.stringify({ message: error.message }), { status: error.status } ); } return new Response( JSON.stringify({ message: 'Internal server error' }), { status: 500 } ); }, }); // Use in app/api/users/route.ts export const GET = routeWithErrorHandler.handler(() => { throw new CustomError('User not found', 404); }); ``` -------------------------------- ### Check for Deprecated Zod v3 Imports Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Uses grep to search the source code for any remaining references to Zod v3 imports, helping to ensure a clean migration to v4. ```bash grep -r "zod/v3" src/ ``` -------------------------------- ### POST /api/users Source: https://context7.com/melvynx/next-zod-route/llms.txt Creates a new user by validating the request body against a Zod schema. ```APIDOC ## POST /api/users ### Description Creates a new user record. Automatically validates JSON, URL-encoded, or multipart form data against the provided Zod schema. ### Method POST ### Endpoint /api/users ### Parameters #### Request Body - **name** (string) - Required - Must be at least 2 characters. - **email** (string) - Required - Must be a valid email address. - **role** (enum) - Optional - 'admin' or 'user' (default: 'user'). - **metadata** (object) - Optional - Contains source information. ### Request Example { "name": "John Doe", "email": "john@example.com", "role": "admin" } ### Response #### Success Response (201) - **id** (string) - Unique identifier of the created user. - **createdAt** (string) - ISO timestamp of creation. #### Response Example { "id": "uuid-123", "name": "John Doe", "email": "john@example.com", "role": "admin", "createdAt": "2023-10-27T10:00:00Z" } ``` -------------------------------- ### Route Parameters Validation Source: https://context7.com/melvynx/next-zod-route/llms.txt Validates dynamic route parameters using Zod schemas, automatically unwrapping Next.js 15 async params. ```APIDOC ## .params(schema) ### Description Defines a Zod schema to validate dynamic route segments. If validation fails, it returns a 400 status code. ### Method GET/POST/PUT/DELETE ### Parameters #### Path Parameters - **schema** (ZodObject) - Required - A Zod schema defining the expected structure of the route parameters. ### Response #### Success Response (200) - **context.params** (object) - The validated and typed parameters object. #### Error Response (400) - **message** (string) - "Invalid params" ``` -------------------------------- ### next-zod-route Route Handler: Zod v3 vs v4 Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Compares route handler definitions using `next-zod-route` with Zod v3 and Zod v4. Demonstrates the use of more efficient UUID validation and unified error parameters in v4. ```typescript // Before (with Zod v3) import { createZodRoute } from 'next-zod-route'; import { z } from 'zod'; const route = createZodRoute() .input({ body: z.object({ id: z.string().uuid(), name: z.string().min(1, { message: "Name is required" }) }) }) .handler(async (request, { input }) => { return Response.json({ success: true, data: input.body }); }); export { route as POST }; ``` ```typescript // After (with Zod v4) import { createZodRoute } from 'next-zod-route'; import { z } from 'zod'; const route = createZodRoute() .input({ body: z.object({ id: z.uuid(), // More efficient UUID validation name: z.string().min(1, { error: "Name is required" }) // Unified error param }) }) .handler(async (request, { input }) => { return Response.json({ success: true, data: input.body }); }); export { route as POST }; ``` -------------------------------- ### Combine Params, Query, and Body Validation with next-zod-route Source: https://context7.com/melvynx/next-zod-route/llms.txt This snippet shows how to chain `params`, `query`, and `body` validation methods for comprehensive request validation in Next.js API routes. It ensures type safety across all parts of the request, providing typed `context.params`, `context.query`, and `context.body` to the handler. ```typescript import { createZodRoute } from 'next-zod-route'; import { z } from 'zod'; const paramsSchema = z.object({ organizationId: z.uuid(), }); const querySchema = z.object({ notify: z.coerce.boolean().default(false), }); const bodySchema = z.object({ name: z.string(), description: z.string().optional(), settings: z.object({ isPublic: z.boolean().default(false), maxMembers: z.number().min(1).max(100).default(10), }), }); // app/api/organizations/[organizationId]/projects/route.ts export const POST = createZodRoute() .params(paramsSchema) .query(querySchema) .body(bodySchema) .handler((request, context) => { const { organizationId } = context.params; const { notify } = context.query; const { name, description, settings } = context.body; const project = { id: crypto.randomUUID(), organizationId, name, description, settings, createdAt: new Date().toISOString(), }; if (notify) { // Send notification logic... } return Response.json({ project, notified: notify }, { status: 201 }); }); // curl -X POST "http://localhost:3000/api/organizations/550e8400-e29b-41d4-a716-446655440000/projects?notify=true" \ // -H "Content-Type: application/json" \ // -d '{"name": "New Project", "settings": {"isPublic": true, "maxMembers": 50}}' ``` -------------------------------- ### Clear TypeScript Cache and Rebuild Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Removes the TypeScript cache and performs a clean build, which can resolve TypeScript errors that may occur after upgrading Zod. ```bash rm -rf node_modules/.cache npx tsc --build --clean ``` -------------------------------- ### Validate Route Parameters with Zod Schema Source: https://context7.com/melvynx/next-zod-route/llms.txt The `.params()` method allows defining a Zod schema for validating route parameters. This ensures that dynamic segments in your API routes match the expected types, such as UUIDs or specific string formats. Next.js 15's async params handling is automatically supported, and `context.params` will be fully typed. ```typescript import { createZodRoute } from 'next-zod-route'; import { z } from 'zod'; const paramsSchema = z.object({ id: z.uuid(), slug: z.string().min(1), }); // app/api/posts/[id]/[slug]/route.ts export const GET = createZodRoute() .params(paramsSchema) .handler((request, context) => { // context.params is fully typed: { id: string; slug: string } const { id, slug } = context.params; return Response.json({ post: { id, slug, title: 'My Post' } }); }); // Valid request: /api/posts/550e8400-e29b-41d4-a716-446655440000/hello-world // Returns: { "post": { "id": "550e8400-e29b-41d4-a716-446655440000", "slug": "hello-world", "title": "My Post" } } // Invalid request: /api/posts/invalid-uuid/hello-world // Returns: { "message": "Invalid params" } with status 400 ``` -------------------------------- ### Update next-zod-route to the latest version Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Command to update the next-zod-route package to its latest version, ensuring compatibility with Zod v4. ```bash npm install next-zod-route@latest ``` -------------------------------- ### Zod v3 vs v4 Import Statements Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Illustrates the change in import statements from Zod v3 to Zod v4. Zod v4 allows direct import of 'zod' for the latest version or explicit import from 'zod/v4'. ```typescript // Before (Zod v3) import { z } from 'zod'; ``` ```typescript // After (Zod v4) // Recommended for new code import { z } from 'zod'; // Or explicit v4 import during migration import { z } from 'zod/v4'; ``` -------------------------------- ### Validate Query String Parameters with Zod Schema Source: https://context7.com/melvynx/next-zod-route/llms.txt Use the `.query()` method to define a Zod schema for validating query string parameters. This method supports complex types like arrays for repeated query parameters and provides default values. The validated query parameters are available in `context.query` with full TypeScript typing. ```typescript import { createZodRoute } from 'next-zod-route'; import { z } from 'zod'; const querySchema = z.object({ search: z.string().min(1), page: z.coerce.number().default(1), status: z.string().array().optional(), sortBy: z.enum(['date', 'title', 'author']).default('date'), }); // app/api/articles/route.ts export const GET = createZodRoute() .query(querySchema) .handler((request, context) => { // context.query is typed: { search: string; page: number; status?: string[]; sortBy: 'date' | 'title' | 'author' } const { search, page, status, sortBy } = context.query; return { query: { search, page, status, sortBy }, articles: [ { id: 1, title: `Results for: ${search}` } ] }; }); // Request: /api/articles?search=typescript&page=2&status=draft&status=published // Returns: { // "query": { "search": "typescript", "page": 2, "status": ["draft", "published"], "sortBy": "date" }, // "articles": [{ "id": 1, "title": "Results for: typescript" }] // } ``` -------------------------------- ### TypeScript Middleware for Route Permissions Source: https://github.com/melvynx/next-zod-route/blob/main/README.md This TypeScript code defines a middleware function to check user permissions against metadata defined for a route. It extracts user permissions, compares them with required permissions from route metadata, and either allows access or returns a 403 Forbidden response. It's designed to be used with `next-zod-route`. ```typescript import { type MiddlewareFunction } from 'next-zod-route'; // Define a schema for permissions metadata const permissionsMetadataSchema = z.object({ requiredPermissions: z.array(z.string()).optional(), }); // Create a middleware that checks permissions const permissionCheckMiddleware: MiddlewareFunction = async ({ next, metadata, request }) => { // Get user permissions from auth header, token, or session const userPermissions = getUserPermissions(request); // If no required permissions in metadata, allow access if (!metadata?.requiredPermissions || metadata.requiredPermissions.length === 0) { return next({ context: { authorized: true } }); } // Check if user has all required permissions const hasAllPermissions = metadata.requiredPermissions.every((permission) => userPermissions.includes(permission)); if (!hasAllPermissions) { // Short-circuit with 403 Forbidden response return new Response( JSON.stringify({ error: 'Forbidden', message: 'You do not have the required permissions', }), { status: 403, headers: { 'Content-Type': 'application/json' }, }, ); } // Continue with authorized context return next({ context: { authorized: true } }); }; // Use in your route handlers export const GET = createZodRoute() .defineMetadata(permissionsMetadataSchema) .use(permissionCheckMiddleware) .metadata({ requiredPermissions: ['read:users'] }) .handler((request, context) => { // Only executed if user has 'read:users' permission return Response.json({ data: 'Protected data' }); }); export const POST = createZodRoute() .defineMetadata(permissionsMetadataSchema) .use(permissionCheckMiddleware) .metadata({ requiredPermissions: ['write:users'] }) .handler((request, context) => { // Only executed if user has 'write:users' permission return Response.json({ success: true }); }); export const DELETE = createZodRoute() .defineMetadata(permissionsMetadataSchema) .use(permissionCheckMiddleware) .metadata({ requiredPermissions: ['admin:users'] }) .handler((request, context) => { // Only executed if user has 'admin:users' permission return Response.json({ success: true }); }); ``` -------------------------------- ### Transforming Middleware Responses Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/01-middleware-next.md Illustrates how to intercept and modify the response object after calling next. This allows for post-processing of data before it is sent to the client. ```typescript const middleware = async ({ next }) => { const response = await next({ ctx: { transform: true } }); const data = await response.json(); return new Response(JSON.stringify({ ...data, transformed: true, }), response); }; ``` -------------------------------- ### Modify Response Headers in Middleware Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/01-middleware-next.md Demonstrates how to intercept the response from the next() handler to inject custom headers or modify the response body. ```typescript const headerMiddleware = async ({ next }) => { const response = await next(); return new Response(response.body, { status: response.status, headers: { ...Object.fromEntries(response.headers.entries()), 'X-Custom': 'value', }, }); }; ``` -------------------------------- ### Check for Old Zod Message Parameters Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Identifies usage of the deprecated 'message:' parameter in Zod schemas, excluding console logs, to facilitate migration to the 'error' parameter. ```bash grep -r "message:" src/ | grep -v "console\|log" ``` -------------------------------- ### Zod v3 vs v4 Error Customization Source: https://github.com/melvynx/next-zod-route/blob/main/docs/migrations/02-zod-v4-upgrade.md Demonstrates the change in error customization APIs from Zod v3 to Zod v4. Zod v4 unifies error handling using a single 'error' parameter. ```typescript // Before (Zod v3) const schema = z.string({ message: "Invalid string", invalid_type_error: "Must be a string", required_error: "This field is required" }); ``` ```typescript // After (Zod v4) const schema = z.string({ error: "Invalid string" // Unified error parameter }); // Or more specific error handling const schema = z.string().describe("Must be a valid string"); ```