Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 7
version: 9
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
Expand Down
2 changes: 1 addition & 1 deletion apps/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"jsdom": "24.0.0",
"next-rest-framework": "workspace:*",
"tsx": "4.7.2",
"zod-form-data": "2.0.2"
"zod-form-data": "3.0.1"
},
"devDependencies": {
"@types/jsdom": "^21.1.6",
Expand Down
75 changes: 36 additions & 39 deletions apps/example/public/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,19 +998,18 @@
"components": {
"schemas": {
"CreateTodo201ResponseBody": {
"type": "string",
"description": "New TODO created message."
"description": "New TODO created message.",
"type": "string"
},
"CreateTodo401ResponseBody": {
"type": "string",
"description": "Unauthorized."
"description": "Unauthorized.",
"type": "string"
},
"CreateTodoRequestBody": {
"description": "New TODO's name.",
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"],
"additionalProperties": false,
"description": "New TODO's name."
"required": ["name"]
},
"CreateTodoResponseBody": {
"type": "object",
Expand All @@ -1026,18 +1025,18 @@
"DeleteTodo404ResponseBody": { "type": "string" },
"DeleteTodoRequestBody": { "type": "string" },
"DeleteTodoResponseBody": {
"description": "TODO not found.",
"type": "object",
"properties": { "error": { "type": "string" } },
"required": ["error"],
"additionalProperties": false,
"description": "TODO not found."
"additionalProperties": false
},
"DeleteTodoResponseBody2": {
"description": "TODO deleted message.",
"type": "object",
"properties": { "message": { "type": "string" } },
"required": ["message"],
"additionalProperties": false,
"description": "TODO deleted message."
"additionalProperties": false
},
"ErrorMessage": {
"type": "object",
Expand All @@ -1058,77 +1057,77 @@
"format": "binary"
},
"FormDataUrlEncodedRequestBody": {
"description": "Test form description.",
"type": "object",
"properties": { "text": { "type": "string" } },
"required": ["text"],
"additionalProperties": false,
"description": "Test form description."
"required": ["text"]
},
"FormDataUrlEncodedResponseBody": {
"description": "Test form response.",
"type": "object",
"properties": { "text": { "type": "string" } },
"required": ["text"],
"additionalProperties": false,
"description": "Test form response."
"additionalProperties": false
},
"GetParams200ResponseBody": {
"description": "Parameters response.",
"type": "object",
"properties": {
"slug": { "type": "string", "enum": ["foo", "bar", "baz"] },
"total": { "type": "string" }
},
"required": ["slug", "total"],
"additionalProperties": false,
"description": "Parameters response."
"additionalProperties": false
},
"GetPathParams200ResponseBody": {
"description": "Parameters response.",
"type": "object",
"properties": {
"slug": { "type": "string", "enum": ["foo", "bar", "baz"] },
"total": { "type": "string" }
},
"required": ["slug", "total"],
"additionalProperties": false,
"description": "Parameters response."
"additionalProperties": false
},
"GetTodoById200ResponseBody": {
"description": "TODO response.",
"type": "object",
"properties": {
"id": { "type": "number" },
"name": { "type": "string" },
"completed": { "type": "boolean" }
},
"required": ["id", "name", "completed"],
"additionalProperties": false,
"description": "TODO response."
"additionalProperties": false
},
"GetTodoById404ResponseBody": {
"type": "string",
"description": "TODO not found."
"description": "TODO not found.",
"type": "string"
},
"GetTodoByIdRequestBody": {
"type": "string",
"description": "TODO name."
"description": "TODO name.",
"type": "string"
},
"GetTodoByIdResponseBody": {
"description": "TODO not found.",
"type": "object",
"properties": { "error": { "type": "string" } },
"required": ["error"],
"additionalProperties": false,
"description": "TODO not found."
"additionalProperties": false
},
"GetTodoByIdResponseBody2": {
"description": "TODO response.",
"type": "object",
"properties": {
"id": { "type": "number" },
"name": { "type": "string" },
"completed": { "type": "boolean" }
},
"required": ["id", "name", "completed"],
"additionalProperties": false,
"description": "TODO response."
"additionalProperties": false
},
"GetTodos200ResponseBody": {
"description": "List of TODOs.",
"type": "array",
"items": {
"type": "object",
Expand All @@ -1139,10 +1138,10 @@
},
"required": ["id", "name", "completed"],
"additionalProperties": false
},
"description": "List of TODOs."
}
},
"GetTodosResponseBody": {
"description": "List of TODOs.",
"type": "array",
"items": {
"type": "object",
Expand All @@ -1153,8 +1152,7 @@
},
"required": ["id", "name", "completed"],
"additionalProperties": false
},
"description": "List of TODOs."
}
},
"MessageWithErrors": {
"type": "object",
Expand Down Expand Up @@ -1204,18 +1202,17 @@
},
"RouteWithExternalDep200ResponseBody": { "type": "string" },
"UrlEncodedFormData200ResponseBody": {
"description": "Test form response.",
"type": "object",
"properties": { "text": { "type": "string" } },
"required": ["text"],
"additionalProperties": false,
"description": "Test form response."
"additionalProperties": false
},
"UrlEncodedFormDataRequestBody": {
"description": "Test form description.",
"type": "object",
"properties": { "text": { "type": "string" } },
"required": ["text"],
"additionalProperties": false,
"description": "Test form description."
"required": ["text"]
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"next": "15.1.6",
"prettier": "3.0.2",
"typescript": "5.2.2",
"zod": "3.22.2"
"zod": "^4.1.13"
},
"prettier": {
"semi": true,
Expand Down
7 changes: 3 additions & 4 deletions packages/next-rest-framework/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@
"formidable": "^3.5.1",
"lodash": "4.17.21",
"prettier": "3.0.2",
"qs": "6.11.2",
"zod-to-json-schema": "3.21.4"
"qs": "6.11.2"
},
"devDependencies": {
"@types/formidable": "^3.4.5",
Expand All @@ -58,7 +57,7 @@
"ts-node": "10.9.1",
"tsup": "8.0.1",
"typescript": "*",
"zod": "*",
"zod-form-data": "*"
"zod": "^4.1.13",
"zod-form-data": "3.0.1"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
type ContentTypesThatSupportInputValidation
} from '../types';
import { NextResponse, type NextRequest } from 'next/server';
import { type ZodSchema, type z } from 'zod';
import { type ZodType, type z } from 'zod';
import { type ValidMethod } from '../constants';
import { type I18NConfig } from 'next/dist/server/config-shared';
import { type NextURL } from 'next/dist/server/web/next-url';
Expand Down Expand Up @@ -200,14 +200,14 @@ interface InputObject<
body?: ContentType extends ContentTypesThatSupportInputValidation
? ContentType extends FormDataContentType
? ZodFormSchema<Body>
: ZodSchema<Body>
: ZodType<Body>
: never;
/*! If defined, this will override the body schema for the OpenAPI spec. */
bodySchema?: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject;
query?: ZodSchema<Query>;
query?: ZodType<Query>;
/*! If defined, this will override the query schema for the OpenAPI spec. */
querySchema?: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject;
params?: ZodSchema<Params>;
params?: ZodType<Params>;
/*! If defined, this will override the params schema for the OpenAPI spec. */
paramsSchema?: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject;
}
Expand Down
32 changes: 21 additions & 11 deletions packages/next-rest-framework/src/app-router/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
let middlewareOptions: BaseOptions = {};

if (middleware1) {
const res = await middleware1(reqClone, {...context, params: await context.params}, middlewareOptions);
const res = await middleware1(
reqClone,
{ ...context, params: await context.params },
middlewareOptions
);

const isOptionsResponse = (res: unknown): res is BaseOptions =>
typeof res === 'object';
Expand All @@ -77,7 +81,11 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
}

if (middleware2) {
const res2 = await middleware2(reqClone, {...context, params: await context.params}, middlewareOptions);
const res2 = await middleware2(
reqClone,
{ ...context, params: await context.params },
middlewareOptions
);

if (res2 instanceof Response) {
return res2;
Expand All @@ -88,7 +96,7 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
if (middleware3) {
const res3 = await middleware3(
reqClone,
{...context, params: await context.params},
{ ...context, params: await context.params },
middlewareOptions
);

Expand Down Expand Up @@ -165,16 +173,16 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
try {
const formData = await reqClone.formData();

const { valid, errors, data } = validateSchema({
const result = validateSchema({
schema: bodySchema,
obj: formData
});

if (!valid) {
if (!result.valid) {
return NextResponse.json(
{
message: DEFAULT_ERRORS.invalidRequestBody,
errors
errors: result.errors
},
{
status: 400
Expand All @@ -186,14 +194,16 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
reqClone = new NextRequest(reqClone.url, {
method: reqClone.method,
headers: reqClone.headers,
body: JSON.stringify(data)
body: JSON.stringify(result.data)
});

// Return parsed form data.
reqClone.formData = async () => {
const formData = new FormData();

for (const [key, value] of Object.entries(data)) {
for (const [key, value] of Object.entries(
result.data as Record<string, unknown>
)) {
formData.append(key, value as string | Blob);
}

Expand Down Expand Up @@ -239,7 +249,7 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
url.searchParams.delete(key);

if (data[key]) {
url.searchParams.append(key, data[key]);
url.searchParams.append(key, data[key] as string);
}
});

Expand Down Expand Up @@ -268,13 +278,13 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
);
}

context.params = data;
context.params = Promise.resolve(data);
}
}

const res = await handler?.(
reqClone as TypedNextRequest,
{...context, params: await context.params},
{ ...context, params: await context.params },
middlewareOptions
);

Expand Down
Loading
Loading