Skip to content

Commit d4692e3

Browse files
authored
fix(serverless): fix broken response handling for itty-router >= 5.0.17 (#686)
1 parent 7cf4415 commit d4692e3

8 files changed

Lines changed: 323 additions & 268 deletions

File tree

.changeset/fast-cooks-drive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ts-rest/serverless': minor
3+
---
4+
5+
New `createFetchHandler` function added so a fetch handler can be initialized once and re-used

.changeset/wise-spiders-impress.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ts-rest/serverless': patch
3+
---
4+
5+
Fix response handling for itty-router >= 5.0.17

libs/ts-rest/serverless/src/lib/handlers/ts-rest-fetch.spec.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ import { initContract, TsRestResponseError } from '@ts-rest/core';
22
import { parse as parseMultipart, getBoundary } from 'parse-multipart-data';
33
import { z } from 'zod';
44
import { vi } from 'vitest';
5-
import { fetchRequestHandler, tsr } from './ts-rest-fetch';
5+
import {
6+
createFetchHandler,
7+
FetchHandlerOptions,
8+
fetchRequestHandler,
9+
tsr,
10+
} from './ts-rest-fetch';
611
import { RequestValidationErrorSchema } from '../types';
712

813
const c = initContract();
@@ -142,22 +147,26 @@ const router = tsr
142147
throw new Error('Test error');
143148
});
144149

150+
const options = {
151+
jsonQuery: true,
152+
responseValidation: true,
153+
cors: {
154+
origin: ['http://localhost'],
155+
credentials: true,
156+
},
157+
} as FetchHandlerOptions<{}, { foo: string }>;
158+
145159
const testFetchRequestHandler = (request: Request) => {
146160
return fetchRequestHandler({
147161
contract,
148162
router,
149-
options: {
150-
jsonQuery: true,
151-
responseValidation: true,
152-
cors: {
153-
origin: ['http://localhost'],
154-
credentials: true,
155-
},
156-
},
163+
options,
157164
request,
158165
});
159166
};
160167

168+
const newFetchRequestHandler = createFetchHandler(contract, router, options);
169+
161170
describe('fetchRequestHandler', () => {
162171
afterEach(() => {
163172
vi.clearAllMocks();
@@ -194,7 +203,7 @@ describe('fetchRequestHandler', () => {
194203
body: JSON.stringify({ ping: 'foo' }),
195204
});
196205

197-
const response = await testFetchRequestHandler(request);
206+
const response = await newFetchRequestHandler(request);
198207
const expectedResponse = new Response('{"id":123,"pong":"foo"}', {
199208
headers: {
200209
'access-control-allow-credentials': 'true',
@@ -270,7 +279,7 @@ describe('fetchRequestHandler', () => {
270279
body: JSON.stringify({ ping: 'foo' }),
271280
});
272281

273-
const response = await testFetchRequestHandler(request);
282+
const response = await newFetchRequestHandler(request);
274283
const expectedResponse = new Response(null, {
275284
status: 500,
276285
headers: {
@@ -356,7 +365,7 @@ describe('fetchRequestHandler', () => {
356365
headers: { origin: 'http://localhost' },
357366
});
358367

359-
const response = await testFetchRequestHandler(request);
368+
const response = await newFetchRequestHandler(request);
360369
const expectedResponse = new Response(null, {
361370
status: 500,
362371
headers: {

libs/ts-rest/serverless/src/lib/handlers/ts-rest-fetch.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,33 @@ export type FetchHandlerOptions<
1717
TRequestExtension = {},
1818
> = ServerlessHandlerOptions<TPlatformContext, TRequestExtension>;
1919

20+
export const createFetchHandler = <
21+
T extends AppRouter,
22+
TRequestExtension,
23+
TPlatformContext = {},
24+
>(
25+
contract: T,
26+
router: RouterImplementationOrFluentRouter<
27+
T,
28+
TPlatformContext,
29+
TRequestExtension
30+
>,
31+
options: FetchHandlerOptions<TPlatformContext, TRequestExtension> = {},
32+
) => {
33+
const serverlessRouter = createServerlessRouter<
34+
T,
35+
TPlatformContext,
36+
TRequestExtension
37+
>(contract, router, options);
38+
39+
return async (request: Request, platformContext?: TPlatformContext) => {
40+
const tsRestRequest = new TsRestRequest(request);
41+
return serverlessRouter.fetch(tsRestRequest, {
42+
...platformContext,
43+
});
44+
};
45+
};
46+
2047
export const fetchRequestHandler = <
2148
T extends AppRouter,
2249
TRequestExtension,
@@ -43,6 +70,7 @@ export const fetchRequestHandler = <
4370
TPlatformContext,
4471
TRequestExtension
4572
>(contract, router, options);
73+
4674
const tsRestRequest = new TsRestRequest(request);
4775
return serverlessRouter.fetch(tsRestRequest, {
4876
...platformContext,

libs/ts-rest/serverless/src/lib/response.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,8 @@ export class TsRestResponse extends Response {
3434
headers,
3535
});
3636
}
37+
38+
override clone() {
39+
return new TsRestResponse(this.rawBody, this);
40+
}
3741
}

libs/ts-rest/serverless/src/lib/router.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import {
22
AppRoute,
3-
AppRouteMutation,
4-
AppRouteQuery,
53
AppRouter,
64
checkZodSchema,
75
isAppRoute,
@@ -139,21 +137,20 @@ const tsRestMiddleware = <TPlatformArgs, TRequestExtension>(
139137
}
140138
};
141139

142-
const preflight = (
143-
request: TsRestRequest & { preflightCorsHeadersSet: boolean },
144-
): TsRestResponse | void => {
140+
type RequestWithPreflightContext = TsRestRequest & {
141+
preflightCorsHeadersSet: boolean;
142+
};
143+
144+
const preflight = (request: TsRestRequest): TsRestResponse | void => {
145145
const preflightResult = ittyPreflight(request);
146146
if (preflightResult) {
147-
request.preflightCorsHeadersSet = true;
147+
(request as RequestWithPreflightContext).preflightCorsHeadersSet = true;
148148
return new TsRestResponse(null, preflightResult);
149149
}
150150
};
151151

152-
const corsify = (
153-
response: TsRestResponse,
154-
request: TsRestRequest & { preflightCorsHeadersSet: boolean },
155-
) => {
156-
if (!request.preflightCorsHeadersSet) {
152+
const corsify = (response: TsRestResponse, request: TsRestRequest) => {
153+
if (!(request as RequestWithPreflightContext).preflightCorsHeadersSet) {
157154
return ittyCorsify(response, request);
158155
}
159156
return response;

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
"docusaurus-plugin-typedoc": "^0.20.0",
6969
"express": "4.19.2",
7070
"fastify": "4.28.0",
71-
"itty-router": "^5.0.9",
71+
"itty-router": "^5.0.18",
7272
"jsonwebtoken": "^9.0.0",
7373
"lodash": "^4.17.21",
7474
"multer": "1.4.5-lts.1",
@@ -136,7 +136,7 @@
136136
"@types/jest": "29.4.4",
137137
"@types/jsonwebtoken": "^9.0.2",
138138
"@types/lodash": "^4.14.195",
139-
"@types/node": "18.11.9",
139+
"@types/node": "20.16.1",
140140
"@types/passport": "^1.0.12",
141141
"@types/passport-jwt": "^3.0.8",
142142
"@types/react": "18.3.1",

0 commit comments

Comments
 (0)