Skip to content

Commit e17c34a

Browse files
committed
feat: Enhance route handler with form data and response handling
1 parent d227b99 commit e17c34a

File tree

6 files changed

+280
-45
lines changed

6 files changed

+280
-45
lines changed

.cursor/rules/00-agent.mdc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
description: EXPLAIN what is the project
3+
globs:
4+
---
5+
## Context
6+
7+
* `next-zod-route` is a library that help NextJS developers to create route handler with `Zod` validation.
8+
* The library is only designed to be used with `zod`.
9+
* The library is only designed to be used with `NextJS`.
10+
11+
## Stack
12+
13+
* TypeScript for the language
14+
* Husky for automations
15+
* Vitest to create and handle test

README.md

Lines changed: 105 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<h1 align="center">next-zod-route</h1>
22

3-
A fork from [next-safe-route](https://github.com/richardsolomou/next-safe-route) that uses [zod](https://github.com/colinhacks/zod) instead of [typeschema](https://github.com/typeschema/main).
3+
A fork from [next-safe-route](https://github.com/richardsolomou/next-safe-route) that uses [zod](https://github.com/colinhacks/zod) for schema validation.
44

55
<p align="center">
66
<a href="https://www.npmjs.com/package/next-zod-route"><img src="https://img.shields.io/npm/v/next-zod-route?style=for-the-badge&logo=npm" /></a>
@@ -15,16 +15,24 @@ A fork from [next-safe-route](https://github.com/richardsolomou/next-safe-route)
1515
- **✅ Schema Validation:** Automatically validates request parameters, query strings, and body content with built-in error handling.
1616
- **🧷 Type-Safe:** Works with full TypeScript type safety for parameters, query strings, and body content.
1717
- **😌 Easy to Use:** Simple and intuitive API that makes defining route handlers a breeze.
18-
- **🔗 Extensible:** Compatible with any validation library supported by [TypeSchema](https://typeschema.com).
18+
- **🔄 Flexible Response Handling:** Return Response objects directly or return plain objects that are automatically converted to JSON responses.
1919
- **🧪 Fully Tested:** Extensive test suite to ensure everything works reliably.
2020

2121
## Installation
2222

2323
```sh
24-
npm install next-zod-route
24+
npm install next-zod-route zod
2525
```
2626

27-
The library only works with [zod](https://zod.dev) for schema validation.
27+
Or using your preferred package manager:
28+
29+
```sh
30+
pnpm add next-zod-route zod
31+
```
32+
33+
```sh
34+
yarn add next-zod-route zod
35+
```
2836

2937
## Usage
3038

@@ -46,6 +54,21 @@ const bodySchema = z.object({
4654
});
4755

4856
export const GET = createZodRoute()
57+
.params(paramsSchema)
58+
.query(querySchema)
59+
.handler((request, context) => {
60+
// Next.js passes params as a promise, but next-zod-route unwraps it for you
61+
const { id } = context.params;
62+
const { search } = context.query;
63+
64+
// Return a Response object directly
65+
return Response.json({ id, search }), { status: 200 };
66+
67+
// Or return a plain object that will be converted to a JSON response
68+
// return { id, search };
69+
});
70+
71+
export const POST = createZodRoute()
4972
.params(paramsSchema)
5073
.query(querySchema)
5174
.body(bodySchema)
@@ -61,71 +84,117 @@ export const GET = createZodRoute()
6184

6285
To define a route handler in Next.js:
6386

64-
1. Import `createZodRoute` and your validation library (default, `zod`).
87+
1. Import `createZodRoute` and `zod`.
6588
2. Define validation schemas for params, query, and body as needed.
6689
3. Use `createZodRoute()` to create a route handler, chaining `params`, `query`, and `body` methods.
6790
4. Implement your handler function, accessing validated and type-safe params, query, and body through `context`.
6891

92+
## Supported Body Formats
93+
94+
`next-zod-route` supports multiple request body formats out of the box:
95+
96+
- **JSON:** Automatically parses and validates JSON bodies.
97+
- **URL Encoded:** Supports `application/x-www-form-urlencoded` data.
98+
- **Multipart Form Data:** Supports `multipart/form-data`, enabling file uploads and complex form data parsing.
99+
100+
The library automatically detects the content type and parses the body accordingly. For GET and DELETE requests, body parsing is skipped.
101+
102+
## Response Handling
103+
104+
You can return responses in two ways:
105+
106+
1. **Return a Response object directly:**
107+
108+
```ts
109+
return Response.json({ data: 'value' }, { status: 200 });
110+
```
111+
112+
2. **Return a plain object** that will be automatically converted to a JSON response with status 200:
113+
114+
```ts
115+
return { data: 'value' };
116+
```
117+
69118
## Advanced Usage
70119

71120
### Middleware
72121

73-
You can add middleware to your route handler with the `use` method.
122+
You can add middleware to your route handler with the `use` method. Middleware functions can add data to the context that will be available in your handler.
74123

75124
```ts
76-
const safeRoute = createZodRoute()
77-
.use(async (request, context) => {
78-
return { user: { id: 'user-123', role: 'admin' } };
79-
})
125+
const authMiddleware = async ({ request }) => {
126+
// Get the token from the request headers
127+
const token = request.headers.get('authorization')?.split(' ')[1];
128+
129+
// Validate the token and get the user
130+
const user = await validateToken(token);
131+
132+
// Return the user to be added to the context
133+
return { user };
134+
};
135+
136+
const permissionsMiddleware = async () => {
137+
return { permissions: ['read', 'write'] };
138+
};
139+
140+
export const GET = createZodRoute()
141+
.use(authMiddleware)
142+
.use(permissionsMiddleware)
80143
.handler((request, context) => {
81-
const user = context.data.user;
82-
return Response.json({ user }, { status: 200 });
144+
// Access middleware data from context.data
145+
const { user, permissions } = context.data;
146+
147+
return Response.json({ user, permissions });
83148
});
84149
```
85150

86-
Ensure that the middleware returns an object. The returned object will be merged with the context object.
151+
Middleware functions should return an object. The returned object will be merged with the context's data property.
87152

88153
### Custom Error Handler
89154

90-
You can specify a custom error handler function with the `handleServerError` method.
91-
92-
To achieve this, define a custom error handler when creating the `safeRoute`:
93-
94-
- Create a custom error class that extends `Error` and a `safeRoute` instance with a custom error handler:
155+
You can specify a custom error handler function to handle errors thrown in your route handler:
95156

96157
```ts
97158
import { createZodRoute } from 'next-zod-route';
98-
import { NextResponse } from 'next/server';
99159

100-
export class RouteError extends Error {
101-
status?: number;
102-
constructor(message: string, status?: number) {
160+
// Create a custom error class
161+
class CustomError extends Error {
162+
constructor(
163+
message: string,
164+
public status: number = 400,
165+
) {
103166
super(message);
104-
this.name = 'RouteError';
105-
this.message = message;
106-
this.status = status || 400;
167+
this.name = 'CustomError';
107168
}
108169
}
109170

110-
export const safeRoute = createZodRoute({
171+
// Create a route with a custom error handler
172+
const safeRoute = createZodRoute({
111173
handleServerError: (error: Error) => {
112-
if (error instanceof RouteError) return NextResponse.json({ message: error.message }, { status: error.status });
174+
if (error instanceof CustomError) {
175+
return new Response(JSON.stringify({ message: error.message }), { status: error.status });
176+
}
113177

114-
return NextResponse.json({ message: 'Something went wrong' }, { status: 400 });
178+
// Default error response
179+
return new Response(JSON.stringify({ message: 'Internal server error' }), { status: 500 });
115180
},
116181
});
117-
```
118182

119-
- Use the `handleServerError` method to define a custom error handler.:
120-
121-
```ts
122-
const GET = safeRoute.handler((request, context) => {
123-
// This error will be handled by the custom error handler with a 500 status code
124-
throw new RouteError('Test error', 500);
183+
export const GET = safeRoute.handler((request, context) => {
184+
// This error will be caught by the custom error handler
185+
throw new CustomError('Something went wrong', 400);
125186
});
126187
```
127188

128-
By default, to avoid any information leakage, the error handler will always return a generic error message.
189+
By default, if no custom error handler is provided, the library will return a generic "Internal server error" message with a 500 status code to avoid information leakage.
190+
191+
## Validation Errors
192+
193+
When validation fails, the library returns appropriate error responses:
194+
195+
- Invalid params: `{ message: 'Invalid params' }` with status 400
196+
- Invalid query: `{ message: 'Invalid query' }` with status 400
197+
- Invalid body: `{ message: 'Invalid body' }` with status 400
129198

130199
## Tests
131200

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"homepage": "https://github.com/melvynx/next-zod-route#readme",
1818
"bugs": {
1919
"url": "https://github.com/melvynx/next-zod-route/issues",
20-
"email": "melvyn@melvynx.com"
20+
"email": "help@melvynx.com"
2121
},
2222
"repository": {
2323
"type": "git",

0 commit comments

Comments
 (0)