Skip to content
Open
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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"scripts": {
"dev": "pnpm --filter example run dev",
"build": "pnpm --filter next-rest-framework run build",
"build": "pnpm --filter next-rest-framework-patch-hugo run build",
"test": "pnpm --filter next-rest-framework run test",
"test:watch": "pnpm --filter next-rest-framework run test:watch",
"format": "prettier --write '**/*.{ts,json}' && eslint --fix --max-warnings=0 --ext=.ts .",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-rest-framework/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "next-rest-framework",
"version": "6.0.6",
"version": "6.0.7",
"description": "Next REST Framework - Type-safe, self-documenting APIs for Next.js",
"keywords": [
"nextjs",
Expand Down
34 changes: 28 additions & 6 deletions packages/next-rest-framework/src/app-router/route-operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import {
type BaseContentType,
type ZodFormSchema,
type FormDataContentType,
type ContentTypesThatSupportInputValidation
type ContentTypesThatSupportInputValidation,
type BaseHeaders
} from '../types';
import { NextResponse, type NextRequest } from 'next/server';
import { type ZodSchema, type z } from 'zod';
Expand Down Expand Up @@ -168,6 +169,7 @@ type TypedRouteHandler<
Body = unknown,
Query extends BaseQuery = BaseQuery,
Params extends BaseParams = BaseParams,
Headers extends BaseHeaders = BaseHeaders,
Options extends BaseOptions = BaseOptions,
ResponseBody = unknown,
Status extends BaseStatus = BaseStatus,
Expand All @@ -185,15 +187,16 @@ type TypedRouteHandler<
| void
> = (
req: TypedNextRequest<Method, ContentType, Body, Query>,
context: { params: Params },
context: { params: Params; headers: Headers },
options: Options
) => Promise<TypedResponse> | TypedResponse;

interface InputObject<
ContentType = BaseContentType,
Body = unknown,
Query = BaseQuery,
Params = BaseParams
Params = BaseParams,
Headers = BaseHeaders
> {
contentType?: ContentType;
/*! Body schema is supported only for certain content types that support input validation. */
Expand All @@ -210,6 +213,7 @@ interface InputObject<
params?: ZodSchema<Params>;
/*! If defined, this will override the params schema for the OpenAPI spec. */
paramsSchema?: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject;
headers?: ZodSchema<Headers>;
}

export interface RouteOperationDefinition<
Expand Down Expand Up @@ -246,7 +250,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
middleware1?: RouteMiddleware<any, any>;
middleware2?: RouteMiddleware<any, any>;
middleware3?: RouteMiddleware<any, any>;
handler?: TypedRouteHandler<any, any, any, any, any, any>;
handler?: TypedRouteHandler<any, any, any, any, any, any, any>;
}): RouteOperationDefinition<Method> => ({
openApiOperation,
method,
Expand All @@ -263,9 +267,10 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
ContentType extends BaseContentType,
Body,
Query extends BaseQuery,
Params extends BaseParams
Params extends BaseParams,
Headers extends BaseHeaders
>(
input: InputObject<ContentType, Body, Query, Params>
input: InputObject<ContentType, Body, Query, Params, Headers>
) => ({
outputs: <
ResponseBody,
Expand Down Expand Up @@ -314,6 +319,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
Body,
Query,
Params,
Headers,
Options3,
ResponseBody,
Status,
Expand All @@ -337,6 +343,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
Body,
Query,
Params,
Headers,
Options2,
ResponseBody,
Status,
Expand All @@ -359,6 +366,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
Body,
Query,
Params,
Headers,
Options1,
ResponseBody,
Status,
Expand All @@ -374,6 +382,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
Body,
Query,
Params,
Headers,
BaseOptions,
ResponseBody,
Status,
Expand Down Expand Up @@ -408,6 +417,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
Body,
Query,
Params,
Headers,
Options3,
ResponseBody,
Status,
Expand All @@ -431,6 +441,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
Body,
Query,
Params,
Headers,
Options2
>
) => createOperation({ input, middleware1, middleware2, handler })
Expand All @@ -452,6 +463,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
Body,
Query,
Params,
Headers,
Options2,
ResponseBody,
Status,
Expand All @@ -473,6 +485,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
ContentType,
Body,
Query,
Headers,
Params,
Options2
>
Expand All @@ -495,6 +508,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
Body,
Query,
Params,
Headers,
Options1,
ResponseBody,
Status,
Expand All @@ -510,6 +524,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
Body,
Query,
Params,
Headers,
Options1
>
) => createOperation({ input, middleware1, handler })
Expand Down Expand Up @@ -565,6 +580,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
unknown,
BaseQuery,
BaseParams,
BaseHeaders,
Options3,
ResponseBody,
Status,
Expand All @@ -587,6 +603,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
unknown,
BaseQuery,
BaseParams,
BaseHeaders,
Options2,
ResponseBody,
Status,
Expand All @@ -602,6 +619,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
unknown,
BaseQuery,
BaseParams,
BaseHeaders,
Options1,
ResponseBody,
Status,
Expand All @@ -617,6 +635,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
unknown,
BaseQuery,
BaseParams,
BaseHeaders,
BaseOptions,
ResponseBody,
Status,
Expand All @@ -641,6 +660,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
unknown,
BaseQuery,
BaseParams,
BaseHeaders,
Options3
>
) =>
Expand All @@ -653,6 +673,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
unknown,
BaseQuery,
BaseParams,
BaseHeaders,
Options2
>
) => createOperation({ middleware1, middleware2, handler })
Expand All @@ -664,6 +685,7 @@ export const routeOperation = <Method extends keyof typeof ValidMethod>({
unknown,
BaseQuery,
BaseParams,
BaseHeaders,
Options1
>
) => createOperation({ middleware1, handler })
Expand Down
46 changes: 39 additions & 7 deletions packages/next-rest-framework/src/app-router/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
type FormDataContentType,
type BaseOptions,
type BaseParams,
type OpenApiPathItem
type OpenApiPathItem,
type BaseHeaders
} from '../types';
import {
type RouteOperationDefinition,
Expand All @@ -28,7 +29,7 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
) => {
const handler = async (
_req: NextRequest,
context: { params: Promise<BaseParams> }
context: { params: Promise<BaseParams>; headers: BaseHeaders }
) => {
try {
const operation = Object.entries(operations).find(
Expand All @@ -48,6 +49,7 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
}
);
}
context.headers = Object.fromEntries(_req.headers.entries());

const { input, handler, middleware1, middleware2, middleware3 } =
operation;
Expand All @@ -65,7 +67,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 +83,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 +98,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 All @@ -106,7 +116,8 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(
body: bodySchema,
query: querySchema,
contentType: contentTypeSchema,
params: paramsSchema
params: paramsSchema,
headers: headersSchema
} = input;

const contentType = reqClone.headers.get('content-type')?.split(';')[0];
Expand Down Expand Up @@ -270,11 +281,32 @@ export const route = <T extends Record<string, RouteOperationDefinition>>(

context.params = data;
}

if (headersSchema) {
const { valid, errors, data } = validateSchema({
schema: headersSchema,
obj: context.headers
});

if (!valid) {
return NextResponse.json(
{
message: DEFAULT_ERRORS.invalidHeaders,
errors
},
{
status: 400
}
);
}

context.headers = data;
}
}

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

Expand Down
Empty file modified packages/next-rest-framework/src/cli/generate.ts
100644 → 100755
Empty file.
Empty file modified packages/next-rest-framework/src/cli/index.ts
100644 → 100755
Empty file.
6 changes: 5 additions & 1 deletion packages/next-rest-framework/src/cli/utils.ts
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,11 @@ export const generateOpenApiSpec = async ({
};

const components = Object.keys(schemas).length
? { components: { schemas: sortObjectByKeys(schemas) } }
? {
components: {
schemas: sortObjectByKeys(schemas)
}
}
: {};

const spec: OpenAPIV3_1.Document = merge(
Expand Down
Empty file modified packages/next-rest-framework/src/cli/validate.ts
100644 → 100755
Empty file.
3 changes: 2 additions & 1 deletion packages/next-rest-framework/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export const DEFAULT_ERRORS = {
operationNotAllowed: 'Operation not allowed.',
invalidRequestBody: 'Invalid request body.',
invalidQueryParameters: 'Invalid query parameters.',
invalidPathParameters: 'Invalid path parameters.'
invalidPathParameters: 'Invalid path parameters.',
invalidHeaders: 'Invalid headers.'
};

export enum ValidMethod {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
type ZodFormSchema,
type ContentTypesThatSupportInputValidation,
type FormDataContentType,
type BaseParams
type BaseParams,
type BaseHeaders
} from '../types';
import { type NextApiRequest, type NextApiResponse } from 'next/types';
import { type ZodSchema, type z } from 'zod';
Expand Down Expand Up @@ -125,7 +126,8 @@ interface InputObject<
ContentType = BaseContentType,
Body = unknown,
Query = BaseQuery,
Params = BaseParams
Params = BaseParams,
Headers = BaseHeaders
> {
contentType?: ContentType;
/*!
Expand All @@ -145,6 +147,8 @@ interface InputObject<
params?: ZodSchema<Params>;
/*! If defined, this will override the params schema for the OpenAPI spec. */
paramsSchema?: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject;

headers?: ZodSchema<Headers>;
}

export interface ApiRouteOperationDefinition<
Expand Down
6 changes: 5 additions & 1 deletion packages/next-rest-framework/src/shared/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ export const logGenerateErrorForRoute = (path: string, error: unknown) => {
Error while importing ${path}, skipping path...`)
);

console.error(chalk.red(error));
if (error instanceof Error && error.stack) {
console.error(chalk.red(error.stack));
} else {
console.error(chalk.red(error));
}

console.info(
chalk.yellow(
Expand Down
Loading