Skip to content

Commit da048dd

Browse files
authored
feat(server): migrate nuxt adapter (#342)
* feat(server): migrate nuxt adapter * addressing PR comments * addressing PR comments * fix
1 parent 8119298 commit da048dd

File tree

8 files changed

+7749
-1970
lines changed

8 files changed

+7749
-1970
lines changed

packages/server/package.json

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,56 @@
4848
"types": "./dist/express.d.cts",
4949
"default": "./dist/express.cjs"
5050
}
51+
},
52+
"./elysia": {
53+
"import": {
54+
"types": "./dist/elysia.d.ts",
55+
"default": "./dist/elysia.js"
56+
},
57+
"require": {
58+
"types": "./dist/elysia.d.cts",
59+
"default": "./dist/elysia.cjs"
60+
}
61+
},
62+
"./fastify": {
63+
"import": {
64+
"types": "./dist/fastify.d.ts",
65+
"default": "./dist/fastify.js"
66+
},
67+
"require": {
68+
"types": "./dist/fastify.d.cts",
69+
"default": "./dist/fastify.cjs"
70+
}
71+
},
72+
"./next": {
73+
"import": {
74+
"types": "./dist/next.d.ts",
75+
"default": "./dist/next.js"
76+
},
77+
"require": {
78+
"types": "./dist/next.d.cts",
79+
"default": "./dist/next.cjs"
80+
}
81+
},
82+
"./hono": {
83+
"import": {
84+
"types": "./dist/hono.d.ts",
85+
"default": "./dist/hono.js"
86+
},
87+
"require": {
88+
"types": "./dist/hono.d.cts",
89+
"default": "./dist/hono.cjs"
90+
}
91+
},
92+
"./nuxt": {
93+
"import": {
94+
"types": "./dist/nuxt.d.ts",
95+
"default": "./dist/nuxt.js"
96+
},
97+
"require": {
98+
"types": "./dist/nuxt.d.cts",
99+
"default": "./dist/nuxt.cjs"
100+
}
51101
}
52102
},
53103
"dependencies": {
@@ -69,22 +119,25 @@
69119
"@zenstackhq/typescript-config": "workspace:*",
70120
"@zenstackhq/vitest-config": "workspace:*",
71121
"body-parser": "^2.2.0",
122+
"elysia": "^1.3.1",
72123
"express": "^5.0.0",
73-
"next": "^15.0.0",
74124
"fastify": "^5.6.1",
75125
"fastify-plugin": "^5.1.0",
76-
"elysia": "^1.3.1",
126+
"h3": "^1.15.4",
77127
"hono": "^4.6.3",
128+
"next": "^15.0.0",
129+
"nuxt": "^4.2.0",
78130
"supertest": "^7.1.4",
79131
"zod": "~3.25.0"
80132
},
81133
"peerDependencies": {
134+
"elysia": "^1.3.0",
82135
"express": "^5.0.0",
83-
"next": "^15.0.0",
84136
"fastify": "^5.0.0",
85137
"fastify-plugin": "^5.0.0",
86-
"elysia": "^1.3.0",
87138
"hono": "^4.6.0",
139+
"next": "^15.0.0",
140+
"nuxt": "^4.0.0",
88141
"zod": "catalog:"
89142
},
90143
"peerDependenciesMeta": {
@@ -105,6 +158,9 @@
105158
},
106159
"hono": {
107160
"optional": true
161+
},
162+
"nuxt": {
163+
"optional": true
108164
}
109165
}
110166
}

packages/server/src/adapter/hono/handler.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { ClientContract } from '@zenstackhq/orm';
22
import type { SchemaDef } from '@zenstackhq/orm/schema';
33
import type { Context, MiddlewareHandler } from 'hono';
4-
import { routePath } from 'hono/route';
54
import type { ContentfulStatusCode } from 'hono/utils/http-status';
65
import { logInternalError, type CommonAdapterOptions } from '../common';
76

@@ -25,7 +24,7 @@ export function createHonoHandler<Schema extends SchemaDef>(options: HonoOptions
2524
const url = new URL(ctx.req.url);
2625
const query = Object.fromEntries(url.searchParams);
2726

28-
const path = ctx.req.path.substring(routePath(ctx).length - 1);
27+
const path = ctx.req.path.substring(ctx.req.routePath.length - 1);
2928
if (!path) {
3029
return ctx.json({ message: 'missing path parameter' }, 400);
3130
}

packages/server/src/adapter/next/app-route-handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { SchemaDef } from '@zenstackhq/orm/schema';
22
import { NextRequest, NextResponse } from 'next/server';
33
import type { AppRouteRequestHandlerOptions } from '.';
4-
import { log } from '../../api/utils';
4+
import { logInternalError } from '../common';
55

66
type Context = { params: Promise<{ path: string[] }> };
77

@@ -59,7 +59,7 @@ export default function factory<Schema extends SchemaDef>(
5959
});
6060
return NextResponse.json(r.body, { status: r.status });
6161
} catch (err) {
62-
log(options.apiHandler.log, 'error', `An unhandled error occurred while processing the request: ${err}${err instanceof Error ? '\n' + err.stack : ''}`);
62+
logInternalError(options.apiHandler.log, err);
6363
return NextResponse.json({ message: 'An internal server error occurred' }, { status: 500 });
6464
}
6565
};

packages/server/src/adapter/next/pages-route-handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { SchemaDef } from '@zenstackhq/orm/schema';
22
import type { NextApiRequest, NextApiResponse } from 'next';
33
import type { PageRouteRequestHandlerOptions } from '.';
4-
import { log } from '../../api/utils';
4+
import { logInternalError } from '../common';
55

66
/**
77
* Creates a Next.js API endpoint "pages" router request handler that handles ZenStack CRUD requests.
@@ -35,7 +35,7 @@ export default function factory<Schema extends SchemaDef>(
3535
});
3636
res.status(r.status).send(r.body);
3737
} catch (err) {
38-
log(options.apiHandler.log, 'error', `An unhandled error occurred while processing the request: ${err}${err instanceof Error ? '\n' + err.stack : ''}`);
38+
logInternalError(options.apiHandler.log, err);
3939
res.status(500).send({ message: 'An internal server error occurred' });
4040
}
4141
};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { ClientContract } from '@zenstackhq/orm';
2+
import type { SchemaDef } from '@zenstackhq/orm/schema';
3+
import {
4+
H3Event,
5+
defineEventHandler,
6+
getQuery,
7+
getRouterParams,
8+
readBody,
9+
type EventHandlerRequest
10+
} from 'h3';
11+
import { setResponseStatus } from 'nuxt/app';
12+
import { logInternalError, type CommonAdapterOptions } from '../common';
13+
14+
/**
15+
* Nuxt request handler options
16+
*/
17+
export interface HandlerOptions<Schema extends SchemaDef> extends CommonAdapterOptions<Schema> {
18+
/**
19+
* Callback for getting a ZenStackClient for the given request
20+
*/
21+
getClient: (event: H3Event<EventHandlerRequest>) => ClientContract<Schema> | Promise<ClientContract<Schema>>;
22+
}
23+
24+
export function createEventHandler<Schema extends SchemaDef>(options: HandlerOptions<Schema>) {
25+
return defineEventHandler(async (event) => {
26+
const client = await options.getClient(event);
27+
if (!client) {
28+
setResponseStatus(event, 500);
29+
return { message: 'unable to get ZenStackClient from request context' };
30+
}
31+
32+
const routerParam = getRouterParams(event);
33+
const query = await getQuery(event);
34+
35+
let reqBody: unknown;
36+
if (event.method === 'POST' || event.method === 'PUT' || event.method === 'PATCH') {
37+
reqBody = await readBody(event);
38+
}
39+
40+
try {
41+
const { status, body } = await options.apiHandler.handleRequest({
42+
method: event.method,
43+
path: routerParam['_']!,
44+
query: query as Record<string, string | string[]>,
45+
requestBody: reqBody,
46+
client,
47+
});
48+
49+
setResponseStatus(event, status);
50+
return body;
51+
} catch (err) {
52+
setResponseStatus(event, 500);
53+
logInternalError(options.apiHandler.log, err);
54+
return { message: 'An internal server error occurred' };
55+
}
56+
});
57+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './handler';

packages/server/tsup.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ export default defineConfig({
55
api: 'src/api/index.ts',
66
express: 'src/adapter/express/index.ts',
77
next: 'src/adapter/next/index.ts',
8+
fastify: 'src/adapter/fastify/index.ts',
9+
elysia: 'src/adapter/elysia/index.ts',
10+
nuxt: 'src/adapter/nuxt/index.ts',
11+
hono: 'src/adapter/hono/index.ts',
812
},
913
outDir: 'dist',
1014
splitting: false,

0 commit comments

Comments
 (0)