next-form-request 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +294 -0
- package/dist/ZodAdapter-D7D3Sc-a.d.mts +95 -0
- package/dist/ZodAdapter-D7D3Sc-a.d.ts +95 -0
- package/dist/adapters/validators/ZodAdapter.d.mts +3 -0
- package/dist/adapters/validators/ZodAdapter.d.ts +3 -0
- package/dist/adapters/validators/ZodAdapter.js +56 -0
- package/dist/adapters/validators/ZodAdapter.js.map +1 -0
- package/dist/adapters/validators/ZodAdapter.mjs +54 -0
- package/dist/adapters/validators/ZodAdapter.mjs.map +1 -0
- package/dist/index.d.mts +365 -0
- package/dist/index.d.ts +365 -0
- package/dist/index.js +497 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +486 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { NextApiRequest, NextApiResponse } from 'next';
|
|
2
|
+
import { S as SupportedRequest, V as ValidatorAdapter, a as ValidationErrors } from './ZodAdapter-D7D3Sc-a.js';
|
|
3
|
+
export { A as AppRouterHandler, P as PagesRouterHandler, R as RequestData, c as ValidationConfig, b as ValidationResult, Z as ZodAdapter, i as isAppRouterRequest, d as isPagesRouterRequest } from './ZodAdapter-D7D3Sc-a.js';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Abstract base class for form request validation.
|
|
8
|
+
* Inspired by Laravel's Form Request pattern.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { FormRequest, ZodAdapter } from 'next-request';
|
|
13
|
+
* import { z } from 'zod';
|
|
14
|
+
*
|
|
15
|
+
* const schema = z.object({
|
|
16
|
+
* email: z.string().email(),
|
|
17
|
+
* password: z.string().min(8),
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* export class LoginRequest extends FormRequest<z.infer<typeof schema>> {
|
|
21
|
+
* rules() {
|
|
22
|
+
* return new ZodAdapter(schema);
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* async authorize() {
|
|
26
|
+
* return true; // Allow all requests
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* beforeValidation() {
|
|
30
|
+
* this.body.email = this.body.email?.toLowerCase().trim();
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare abstract class FormRequest<TValidated = unknown> {
|
|
36
|
+
/**
|
|
37
|
+
* The original request object
|
|
38
|
+
*/
|
|
39
|
+
protected request: SupportedRequest;
|
|
40
|
+
/**
|
|
41
|
+
* Parsed request body (mutable for beforeValidation hook)
|
|
42
|
+
*/
|
|
43
|
+
protected body: Record<string, unknown>;
|
|
44
|
+
/**
|
|
45
|
+
* Query parameters from the URL
|
|
46
|
+
*/
|
|
47
|
+
protected query: Record<string, string | string[] | undefined>;
|
|
48
|
+
/**
|
|
49
|
+
* Route parameters (e.g., /users/[id])
|
|
50
|
+
*/
|
|
51
|
+
protected params: Record<string, string>;
|
|
52
|
+
/**
|
|
53
|
+
* Request headers
|
|
54
|
+
*/
|
|
55
|
+
protected headers: Record<string, string | string[] | undefined>;
|
|
56
|
+
/**
|
|
57
|
+
* Validated data (populated after successful validation)
|
|
58
|
+
*/
|
|
59
|
+
private _validated;
|
|
60
|
+
/**
|
|
61
|
+
* Partial validated data (fields that passed validation)
|
|
62
|
+
*/
|
|
63
|
+
private _safe;
|
|
64
|
+
/**
|
|
65
|
+
* Define the validation rules for this request.
|
|
66
|
+
* Return a ValidatorAdapter instance (e.g., ZodAdapter, YupAdapter).
|
|
67
|
+
*/
|
|
68
|
+
abstract rules(): ValidatorAdapter<TValidated>;
|
|
69
|
+
/**
|
|
70
|
+
* Determine if the user is authorized to make this request.
|
|
71
|
+
* Override this method to add authorization logic.
|
|
72
|
+
*
|
|
73
|
+
* @returns true if authorized, false otherwise
|
|
74
|
+
*/
|
|
75
|
+
authorize(): boolean | Promise<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Called before validation runs.
|
|
78
|
+
* Use this to normalize or transform input data.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* beforeValidation() {
|
|
83
|
+
* this.body.email = this.body.email?.toLowerCase().trim();
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
beforeValidation(): void | Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Called after validation succeeds.
|
|
90
|
+
* Use this for logging, analytics, or post-processing.
|
|
91
|
+
*
|
|
92
|
+
* @param data The validated data
|
|
93
|
+
*/
|
|
94
|
+
afterValidation(_data: TValidated): void | Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Called when validation fails.
|
|
97
|
+
* Use this for logging or custom error handling.
|
|
98
|
+
*
|
|
99
|
+
* @param errors The validation errors
|
|
100
|
+
*/
|
|
101
|
+
onValidationFailed(_errors: ValidationErrors): void | Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Called when authorization fails.
|
|
104
|
+
* Use this for logging or custom error handling.
|
|
105
|
+
*/
|
|
106
|
+
onAuthorizationFailed(): void | Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Custom validation error messages.
|
|
109
|
+
* Keys can be field names or "field.rule" patterns.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* messages() {
|
|
114
|
+
* return {
|
|
115
|
+
* 'email.email': 'Please provide a valid email address',
|
|
116
|
+
* 'password.min': 'Password must be at least 8 characters',
|
|
117
|
+
* };
|
|
118
|
+
* }
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
messages(): Record<string, string>;
|
|
122
|
+
/**
|
|
123
|
+
* Custom attribute names for error messages.
|
|
124
|
+
* Used to replace field names in error messages.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* attributes() {
|
|
129
|
+
* return {
|
|
130
|
+
* 'email': 'email address',
|
|
131
|
+
* 'dob': 'date of birth',
|
|
132
|
+
* };
|
|
133
|
+
* }
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
attributes(): Record<string, string>;
|
|
137
|
+
/**
|
|
138
|
+
* Create a FormRequest instance from an App Router Request.
|
|
139
|
+
*
|
|
140
|
+
* @param request The incoming Request object
|
|
141
|
+
* @param params Route parameters (from params in route handler)
|
|
142
|
+
*/
|
|
143
|
+
static fromAppRouter<T extends FormRequest>(this: new () => T, request: Request, params?: Record<string, string>): Promise<T>;
|
|
144
|
+
/**
|
|
145
|
+
* Create a FormRequest instance from a Pages Router NextApiRequest.
|
|
146
|
+
*
|
|
147
|
+
* @param request The incoming NextApiRequest object
|
|
148
|
+
* @param params Route parameters
|
|
149
|
+
*/
|
|
150
|
+
static fromPagesRouter<T extends FormRequest>(this: new () => T, request: NextApiRequest, params?: Record<string, string>): Promise<T>;
|
|
151
|
+
/**
|
|
152
|
+
* Run validation and return the validated data.
|
|
153
|
+
* Throws ValidationError if validation fails.
|
|
154
|
+
* Throws AuthorizationError if authorization fails.
|
|
155
|
+
*
|
|
156
|
+
* @returns The validated data with full type inference
|
|
157
|
+
*/
|
|
158
|
+
validate(): Promise<TValidated>;
|
|
159
|
+
/**
|
|
160
|
+
* Get the validated data (after calling validate()).
|
|
161
|
+
* Throws if validate() hasn't been called successfully.
|
|
162
|
+
*/
|
|
163
|
+
validated(): TValidated;
|
|
164
|
+
/**
|
|
165
|
+
* Get only the fields that passed validation.
|
|
166
|
+
* Safe to call even if validation hasn't completed.
|
|
167
|
+
*/
|
|
168
|
+
safe(): Partial<TValidated>;
|
|
169
|
+
/**
|
|
170
|
+
* Get raw input data (body merged with query for GET requests).
|
|
171
|
+
*/
|
|
172
|
+
all(): Record<string, unknown>;
|
|
173
|
+
/**
|
|
174
|
+
* Get a specific input value.
|
|
175
|
+
*/
|
|
176
|
+
input<T = unknown>(key: string, defaultValue?: T): T | undefined;
|
|
177
|
+
/**
|
|
178
|
+
* Check if input has a specific key.
|
|
179
|
+
*/
|
|
180
|
+
has(key: string): boolean;
|
|
181
|
+
/**
|
|
182
|
+
* Get only specified keys from input.
|
|
183
|
+
*/
|
|
184
|
+
only<K extends string>(...keys: K[]): Pick<Record<string, unknown>, K>;
|
|
185
|
+
/**
|
|
186
|
+
* Get all input except specified keys.
|
|
187
|
+
*/
|
|
188
|
+
except(...keys: string[]): Record<string, unknown>;
|
|
189
|
+
/**
|
|
190
|
+
* Get the original request object.
|
|
191
|
+
*/
|
|
192
|
+
getRequest(): SupportedRequest;
|
|
193
|
+
/**
|
|
194
|
+
* Check if this is an App Router request.
|
|
195
|
+
*/
|
|
196
|
+
isAppRouter(): boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Get a header value.
|
|
199
|
+
*/
|
|
200
|
+
header(name: string): string | string[] | undefined;
|
|
201
|
+
/**
|
|
202
|
+
* Get a route parameter.
|
|
203
|
+
*/
|
|
204
|
+
param(name: string): string | undefined;
|
|
205
|
+
/**
|
|
206
|
+
* Get the data that will be validated.
|
|
207
|
+
* Override this to customize what data is passed to the validator.
|
|
208
|
+
*/
|
|
209
|
+
protected getDataForValidation(): unknown;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Error thrown when request validation fails
|
|
214
|
+
*/
|
|
215
|
+
declare class ValidationError extends Error {
|
|
216
|
+
readonly errors: ValidationErrors;
|
|
217
|
+
constructor(errors: ValidationErrors);
|
|
218
|
+
/**
|
|
219
|
+
* Get all error messages as a flat array
|
|
220
|
+
*/
|
|
221
|
+
getAllMessages(): string[];
|
|
222
|
+
/**
|
|
223
|
+
* Get errors for a specific field
|
|
224
|
+
*/
|
|
225
|
+
getFieldErrors(field: string): string[];
|
|
226
|
+
/**
|
|
227
|
+
* Check if a specific field has errors
|
|
228
|
+
*/
|
|
229
|
+
hasFieldError(field: string): boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Convert to JSON-serializable object
|
|
232
|
+
*/
|
|
233
|
+
toJSON(): {
|
|
234
|
+
name: string;
|
|
235
|
+
message: string;
|
|
236
|
+
errors: ValidationErrors;
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Error thrown when request authorization fails
|
|
241
|
+
*/
|
|
242
|
+
declare class AuthorizationError extends Error {
|
|
243
|
+
constructor(message?: string);
|
|
244
|
+
toJSON(): {
|
|
245
|
+
name: string;
|
|
246
|
+
message: string;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Type for FormRequest constructor
|
|
252
|
+
*/
|
|
253
|
+
type FormRequestClass<TValidated> = {
|
|
254
|
+
new (): FormRequest<TValidated>;
|
|
255
|
+
fromAppRouter(request: Request, params?: Record<string, string>): Promise<FormRequest<TValidated>>;
|
|
256
|
+
fromPagesRouter(request: NextApiRequest, params?: Record<string, string>): Promise<FormRequest<TValidated>>;
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* Handler function for App Router
|
|
260
|
+
*/
|
|
261
|
+
type AppRouterHandler<TValidated> = (validated: TValidated, request: Request, formRequest: FormRequest<TValidated>) => Response | Promise<Response>;
|
|
262
|
+
/**
|
|
263
|
+
* Handler function for Pages Router
|
|
264
|
+
*/
|
|
265
|
+
type PagesRouterHandler<TValidated> = (validated: TValidated, req: NextApiRequest, res: NextApiResponse, formRequest: FormRequest<TValidated>) => void | Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Context parameter for App Router (Next.js 13+)
|
|
268
|
+
*/
|
|
269
|
+
interface AppRouterContext {
|
|
270
|
+
params?: Record<string, string> | Promise<Record<string, string>>;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Wrap an App Router route handler with form request validation.
|
|
274
|
+
*
|
|
275
|
+
* The handler receives validated data and only executes if:
|
|
276
|
+
* 1. Authorization passes (authorize() returns true)
|
|
277
|
+
* 2. Validation passes (rules() validates successfully)
|
|
278
|
+
*
|
|
279
|
+
* Errors are thrown, not auto-handled - catch ValidationError and
|
|
280
|
+
* AuthorizationError in your handler or error boundary.
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```typescript
|
|
284
|
+
* // app/api/users/route.ts
|
|
285
|
+
* import { withRequest } from 'next-request';
|
|
286
|
+
* import { CreateUserRequest } from '@/requests/CreateUserRequest';
|
|
287
|
+
*
|
|
288
|
+
* export const POST = withRequest(CreateUserRequest, async (data, request) => {
|
|
289
|
+
* const user = await db.users.create({ data });
|
|
290
|
+
* return Response.json({ user }, { status: 201 });
|
|
291
|
+
* });
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
declare function withRequest<TValidated>(RequestClass: FormRequestClass<TValidated>, handler: AppRouterHandler<TValidated>): (request: Request, context?: AppRouterContext) => Promise<Response>;
|
|
295
|
+
/**
|
|
296
|
+
* Wrap a Pages Router API handler with form request validation.
|
|
297
|
+
*
|
|
298
|
+
* The handler receives validated data and only executes if:
|
|
299
|
+
* 1. Authorization passes (authorize() returns true)
|
|
300
|
+
* 2. Validation passes (rules() validates successfully)
|
|
301
|
+
*
|
|
302
|
+
* Errors are thrown, not auto-handled - catch ValidationError and
|
|
303
|
+
* AuthorizationError in your handler.
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```typescript
|
|
307
|
+
* // pages/api/users.ts
|
|
308
|
+
* import { withApiRequest } from 'next-request';
|
|
309
|
+
* import { CreateUserRequest } from '@/requests/CreateUserRequest';
|
|
310
|
+
*
|
|
311
|
+
* export default withApiRequest(CreateUserRequest, async (data, req, res) => {
|
|
312
|
+
* const user = await db.users.create({ data });
|
|
313
|
+
* res.status(201).json({ user });
|
|
314
|
+
* });
|
|
315
|
+
* ```
|
|
316
|
+
*/
|
|
317
|
+
declare function withApiRequest<TValidated>(RequestClass: FormRequestClass<TValidated>, handler: PagesRouterHandler<TValidated>): (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
|
|
318
|
+
/**
|
|
319
|
+
* Create a wrapper with custom error handling for App Router.
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```typescript
|
|
323
|
+
* import { createAppRouterWrapper, ValidationError, AuthorizationError } from 'next-request';
|
|
324
|
+
*
|
|
325
|
+
* const withValidation = createAppRouterWrapper({
|
|
326
|
+
* onValidationError: (error) => Response.json({ errors: error.errors }, { status: 422 }),
|
|
327
|
+
* onAuthorizationError: () => Response.json({ message: 'Forbidden' }, { status: 403 }),
|
|
328
|
+
* });
|
|
329
|
+
*
|
|
330
|
+
* export const POST = withValidation(CreateUserRequest, async (data) => {
|
|
331
|
+
* const user = await db.users.create({ data });
|
|
332
|
+
* return Response.json({ user }, { status: 201 });
|
|
333
|
+
* });
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
declare function createAppRouterWrapper(options: {
|
|
337
|
+
onValidationError?: (error: ValidationError) => Response;
|
|
338
|
+
onAuthorizationError?: (error: AuthorizationError) => Response;
|
|
339
|
+
onError?: (error: unknown) => Response;
|
|
340
|
+
}): <TValidated>(RequestClass: FormRequestClass<TValidated>, handler: AppRouterHandler<TValidated>) => (request: Request, context?: AppRouterContext) => Promise<Response>;
|
|
341
|
+
/**
|
|
342
|
+
* Create a wrapper with custom error handling for Pages Router.
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* ```typescript
|
|
346
|
+
* import { createPagesRouterWrapper, ValidationError, AuthorizationError } from 'next-request';
|
|
347
|
+
*
|
|
348
|
+
* const withValidation = createPagesRouterWrapper({
|
|
349
|
+
* onValidationError: (error, req, res) => res.status(422).json({ errors: error.errors }),
|
|
350
|
+
* onAuthorizationError: (error, req, res) => res.status(403).json({ message: 'Forbidden' }),
|
|
351
|
+
* });
|
|
352
|
+
*
|
|
353
|
+
* export default withValidation(CreateUserRequest, async (data, req, res) => {
|
|
354
|
+
* const user = await db.users.create({ data });
|
|
355
|
+
* res.status(201).json({ user });
|
|
356
|
+
* });
|
|
357
|
+
* ```
|
|
358
|
+
*/
|
|
359
|
+
declare function createPagesRouterWrapper(options: {
|
|
360
|
+
onValidationError?: (error: ValidationError, req: NextApiRequest, res: NextApiResponse) => void | Promise<void>;
|
|
361
|
+
onAuthorizationError?: (error: AuthorizationError, req: NextApiRequest, res: NextApiResponse) => void | Promise<void>;
|
|
362
|
+
onError?: (error: unknown, req: NextApiRequest, res: NextApiResponse) => void | Promise<void>;
|
|
363
|
+
}): <TValidated>(RequestClass: FormRequestClass<TValidated>, handler: PagesRouterHandler<TValidated>) => (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
|
|
364
|
+
|
|
365
|
+
export { AuthorizationError, FormRequest, SupportedRequest, ValidationError, ValidationErrors, ValidatorAdapter, createAppRouterWrapper, createPagesRouterWrapper, withApiRequest, withRequest };
|