forked from zenstackhq/zenstack
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: make nextjs adapter support next 13 app dir (zenstackhq#483)
- Loading branch information
Showing
6 changed files
with
339 additions
and
320 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* eslint-disable @typescript-eslint/no-non-null-assertion */ | ||
|
||
import { DbClientContract } from '@zenstackhq/runtime'; | ||
import { ModelZodSchema, getModelZodSchemas } from '@zenstackhq/runtime/zod'; | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
import { AppRouteRequestHandlerOptions } from '.'; | ||
import RPCAPIHandler from '../api/rpc'; | ||
import { buildUrlQuery, marshalToObject, unmarshalFromObject } from '../utils'; | ||
|
||
type Context = { params: { path: string[] } }; | ||
|
||
/** | ||
* Creates a Next.js 13 "app dir" API route request handler which encapsulates Prisma CRUD operations. | ||
* | ||
* @param options Options for initialization | ||
* @returns An API route request handler | ||
*/ | ||
export default function factory( | ||
options: AppRouteRequestHandlerOptions | ||
): (req: NextRequest, context: Context) => Promise<NextResponse> { | ||
let zodSchemas: ModelZodSchema | undefined; | ||
if (typeof options.zodSchemas === 'object') { | ||
zodSchemas = options.zodSchemas; | ||
} else if (options.zodSchemas === true) { | ||
zodSchemas = getModelZodSchemas(); | ||
} | ||
|
||
const requestHandler = options.handler || RPCAPIHandler(); | ||
const useSuperJson = options.useSuperJson === true; | ||
|
||
return async (req: NextRequest, context: Context) => { | ||
const prisma = (await options.getPrisma(req)) as DbClientContract; | ||
if (!prisma) { | ||
return NextResponse.json( | ||
marshalToObject({ message: 'unable to get prisma from request context' }, useSuperJson), | ||
{ status: 500 } | ||
); | ||
} | ||
|
||
const url = new URL(req.url); | ||
let query: Record<string, string | string[]> = Object.fromEntries(url.searchParams); | ||
try { | ||
query = buildUrlQuery(query, useSuperJson); | ||
} catch { | ||
return NextResponse.json(marshalToObject({ message: 'invalid query parameters' }, useSuperJson), { | ||
status: 400, | ||
}); | ||
} | ||
|
||
if (!context.params.path) { | ||
return NextResponse.json(marshalToObject({ message: 'missing path parameter' }, useSuperJson), { | ||
status: 400, | ||
}); | ||
} | ||
const path = context.params.path.join('/'); | ||
|
||
let requestBody: unknown; | ||
if (req.body) { | ||
try { | ||
requestBody = await req.json(); | ||
} catch { | ||
// noop | ||
} | ||
} | ||
|
||
try { | ||
const r = await requestHandler({ | ||
method: req.method!, | ||
path, | ||
query, | ||
requestBody: unmarshalFromObject(requestBody, useSuperJson), | ||
prisma, | ||
modelMeta: options.modelMeta, | ||
zodSchemas, | ||
logger: options.logger, | ||
}); | ||
return NextResponse.json(marshalToObject(r.body, useSuperJson), { status: r.status }); | ||
} catch (err) { | ||
return NextResponse.json( | ||
marshalToObject({ message: `An unhandled error occurred: ${err}` }, useSuperJson), | ||
{ status: 500 } | ||
); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,52 @@ | ||
export { default as NextRequestHandler } from './request-handler'; | ||
export * from './request-handler'; | ||
import { NextApiRequest, NextApiResponse } from 'next'; | ||
import type { NextRequest } from 'next/server'; | ||
import type { AdapterBaseOptions } from '../types'; | ||
import { default as AppRouteHandler } from './app-route-handler'; | ||
import { default as PagesRouteHandler } from './pages-route-handler'; | ||
|
||
/** | ||
* Options for initializing a Next.js API endpoint request handler. | ||
*/ | ||
export interface PagesRouteRequestHandlerOptions extends AdapterBaseOptions { | ||
/** | ||
* Callback method for getting a Prisma instance for the given request/response pair. | ||
*/ | ||
getPrisma: (req: NextApiRequest, res: NextApiResponse) => Promise<unknown> | unknown; | ||
|
||
/** | ||
* Use Next.js 13 app dir or not | ||
*/ | ||
useAppDir?: false | undefined; | ||
} | ||
|
||
/** | ||
* Options for initializing a Next.js 13 app dir API route handler. | ||
*/ | ||
export interface AppRouteRequestHandlerOptions extends AdapterBaseOptions { | ||
/** | ||
* Callback method for getting a Prisma instance for the given request. | ||
*/ | ||
getPrisma: (req: NextRequest) => Promise<unknown> | unknown; | ||
|
||
/** | ||
* Use Next.js 13 app dir or not | ||
*/ | ||
useAppDir: true; | ||
} | ||
|
||
/** | ||
* Creates a Next.js API route handler. | ||
* @see https://zenstack.dev/docs/reference/server-adapters/next | ||
*/ | ||
export function NextRequestHandler(options: PagesRouteRequestHandlerOptions): ReturnType<typeof PagesRouteHandler>; | ||
export function NextRequestHandler(options: AppRouteRequestHandlerOptions): ReturnType<typeof AppRouteHandler>; | ||
export function NextRequestHandler(options: PagesRouteRequestHandlerOptions | AppRouteRequestHandlerOptions) { | ||
if (options.useAppDir === true) { | ||
return AppRouteHandler(options); | ||
} else { | ||
return PagesRouteHandler(options); | ||
} | ||
} | ||
|
||
// for backward compatibility | ||
export { PagesRouteRequestHandlerOptions as RequestHandlerOptions }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.