Skip to content

Commit eb2c647

Browse files
authored
feat: create fluent router builder for serverless and optimize exported return types (#639)
1 parent 0eff920 commit eb2c647

28 files changed

Lines changed: 1275 additions & 161 deletions

File tree

.changeset/green-readers-change.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+
BREAKING CHANGE: The order of generics on the `tsr.*` methods have been swapped. Now you don't have to pass `typeof contract` first. You can now pass your request extension type only.

.changeset/spotty-ads-glow.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@ts-rest/react-query': minor
3+
'@ts-rest/solid-query': minor
4+
'@ts-rest/serverless': minor
5+
'@ts-rest/vue-query': minor
6+
'@ts-rest/express': minor
7+
'@ts-rest/fastify': minor
8+
'@ts-rest/next': minor
9+
---
10+
11+
Make sure initialized client and router types are exported so they can be re-exported with types emitted

.changeset/violet-dogs-explode.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+
Fluent router builder for easier modification of request context type in middleware. Try it out through `tsr.routerBuilder(contract)`!

apps/docs/docs/serverless/routers.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import { tsr } from '@ts-rest/serverless/aws';
3535
import { contract } from './contract';
3636

3737
export const postsRouter = tsr.router<
38-
typeof contract, // <-- You have to add the contract type here
3938
{ userId: string } // <-- Add the extended part here. This will be visible in request.userId
4039
>(contract, { ... });
4140
```

libs/ts-rest/express/src/lib/ts-rest-express.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import type {
2222
import {
2323
AppRouteImplementationOrOptions,
2424
TsRestExpressOptions,
25-
RecursiveRouterObj,
25+
RouterImplementation,
2626
TsRestRequestHandler,
2727
isAppRouteImplementation,
2828
} from './types';
@@ -31,12 +31,21 @@ import { Stream } from 'stream';
3131

3232
export const initServer = () => {
3333
return {
34-
router: <T extends AppRouter>(router: T, args: RecursiveRouterObj<T>) =>
35-
args,
36-
route: <T extends AppRoute>(
37-
route: T,
38-
args: AppRouteImplementationOrOptions<T>,
39-
) => args,
34+
router: <
35+
T extends AppRouter = AppRouter,
36+
TRouter extends RouterImplementation<T> = RouterImplementation<T>,
37+
>(
38+
contract: T,
39+
router: TRouter,
40+
) => router,
41+
route: <
42+
T extends AppRoute,
43+
TRoute extends
44+
AppRouteImplementationOrOptions<T> = AppRouteImplementationOrOptions<T>,
45+
>(
46+
contract: T,
47+
route: TRoute,
48+
) => route,
4049
};
4150
};
4251

@@ -46,7 +55,7 @@ const recursivelyApplyExpressRouter = ({
4655
processRoute,
4756
}: {
4857
schema: AppRouter | AppRoute;
49-
router: RecursiveRouterObj<any> | AppRouteImplementationOrOptions<any>;
58+
router: RouterImplementation<any> | AppRouteImplementationOrOptions<any>;
5059
processRoute: (
5160
implementation: AppRouteImplementationOrOptions<AppRoute>,
5261
schema: AppRoute,
@@ -60,7 +69,7 @@ const recursivelyApplyExpressRouter = ({
6069

6170
recursivelyApplyExpressRouter({
6271
schema: schema[key],
63-
router: (router as RecursiveRouterObj<any>)[key],
72+
router: (router as RouterImplementation<any>)[key],
6473
processRoute,
6574
});
6675
}
@@ -286,7 +295,7 @@ const requestValidationErrorHandler = (
286295

287296
export const createExpressEndpoints = <TRouter extends AppRouter>(
288297
schema: TRouter,
289-
router: RecursiveRouterObj<TRouter>,
298+
router: RouterImplementation<TRouter>,
290299
app: IRouter,
291300
options: TsRestExpressOptions<TRouter> = {
292301
logInitialization: true,

libs/ts-rest/express/src/lib/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ export const isAppRouteImplementation = <TRoute extends AppRoute>(
8080
return typeof obj === 'function';
8181
};
8282

83-
export type RecursiveRouterObj<T extends AppRouter> = {
83+
export type RouterImplementation<T extends AppRouter> = {
8484
[TKey in keyof T]: T[TKey] extends AppRouter
85-
? RecursiveRouterObj<T[TKey]>
85+
? RouterImplementation<T[TKey]>
8686
: T[TKey] extends AppRoute
8787
? AppRouteImplementationOrOptions<T[TKey]>
8888
: never;

libs/ts-rest/fastify/src/lib/ts-rest-fastify.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ type AppRouteImplementation<T extends AppRoute> = (
6161
},
6262
) => Promise<ServerInferResponses<T>>;
6363

64-
type RecursiveRouterObj<T extends AppRouter> = {
64+
export type RouterImplementation<T extends AppRouter> = {
6565
[TKey in keyof T]: T[TKey] extends AppRouter
66-
? RecursiveRouterObj<T[TKey]>
66+
? RouterImplementation<T[TKey]>
6767
: T[TKey] extends AppRoute
6868
? AppRouteImplementationOrOptions<T[TKey]>
6969
: never;
@@ -193,8 +193,8 @@ const RouterEmbeddedContract = Symbol('RouterEmbeddedContract');
193193
export const initServer = () => ({
194194
router: <TContract extends AppRouter>(
195195
contract: TContract,
196-
routes: RecursiveRouterObj<TContract>,
197-
): RecursiveRouterObj<TContract> => ({
196+
routes: RouterImplementation<TContract>,
197+
): RouterImplementation<TContract> => ({
198198
...routes,
199199
[RouterEmbeddedContract]: contract,
200200
}),
@@ -203,7 +203,7 @@ export const initServer = () => ({
203203
implementation: AppRouteImplementation<TAppRoute>,
204204
) => implementation,
205205
registerRouter: <
206-
T extends RecursiveRouterObj<TContract>,
206+
T extends RouterImplementation<TContract>,
207207
TContract extends AppRouter,
208208
>(
209209
contract: TContract,
@@ -239,7 +239,7 @@ export const initServer = () => ({
239239
},
240240
plugin:
241241
<T extends AppRouter>(
242-
router: RecursiveRouterObj<T>,
242+
router: RouterImplementation<T>,
243243
): fastify.FastifyPluginCallback<RegisterRouterOptions<T>> =>
244244
(
245245
app,
@@ -252,7 +252,7 @@ export const initServer = () => ({
252252
done,
253253
) => {
254254
const embeddedContract = (
255-
router as RecursiveRouterObj<T> & { [RouterEmbeddedContract]: T }
255+
router as RouterImplementation<T> & { [RouterEmbeddedContract]: T }
256256
)[RouterEmbeddedContract];
257257

258258
const { hooks = {}, ...restOfOptions } = opts;
@@ -423,7 +423,7 @@ const registerRoute = <TAppRoute extends AppRoute>(
423423
* @param options
424424
*/
425425
const recursivelyRegisterRouter = <T extends AppRouter>(
426-
routerImpl: RecursiveRouterObj<T>,
426+
routerImpl: RouterImplementation<T>,
427427
appRouter: T,
428428
path: string[],
429429
fastify: fastify.FastifyInstance,
@@ -435,7 +435,7 @@ const recursivelyRegisterRouter = <T extends AppRouter>(
435435
) {
436436
for (const key in routerImpl) {
437437
recursivelyRegisterRouter(
438-
routerImpl[key] as unknown as RecursiveRouterObj<T>,
438+
routerImpl[key] as unknown as RouterImplementation<T>,
439439
appRouter[key] as unknown as T,
440440
[...path, key],
441441
fastify,

libs/ts-rest/next/src/lib/ts-rest-next.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ type AppRouteImplementation<T extends AppRoute> = (
3838
},
3939
) => Promise<ServerInferResponses<T>>;
4040

41-
type RecursiveRouterObj<T extends AppRouter> = {
41+
export type RouterImplementation<T extends AppRouter> = {
4242
[TKey in keyof T]: T[TKey] extends AppRouter
43-
? RecursiveRouterObj<T[TKey]>
43+
? RouterImplementation<T[TKey]>
4444
: T[TKey] extends AppRoute
4545
? AppRouteImplementation<T[TKey]>
4646
: never;
@@ -74,7 +74,7 @@ type CreateNextRouterOptions = {
7474
*/
7575
const mergeRouterAndImplementation = <T extends AppRouter>(
7676
router: T,
77-
implementation: RecursiveRouterObj<T>,
77+
implementation: RouterImplementation<T>,
7878
): AppRouterWithImplementation => {
7979
const keys = Object.keys(router);
8080

@@ -176,7 +176,7 @@ const getRouteImplementation = (
176176
export const createNextRoute = <T extends AppRouter | AppRoute>(
177177
appRouter: T,
178178
implementation: T extends AppRouter
179-
? RecursiveRouterObj<T>
179+
? RouterImplementation<T>
180180
: T extends AppRoute
181181
? AppRouteImplementation<T>
182182
: never,
@@ -200,7 +200,7 @@ export const createNextRoute = <T extends AppRouter | AppRoute>(
200200
*/
201201
export const createNextRouter = <T extends AppRouter>(
202202
routes: T,
203-
obj: RecursiveRouterObj<T>,
203+
obj: RouterImplementation<T>,
204204
options?: CreateNextRouterOptions,
205205
) => {
206206
return handlerFactory((req) => {

libs/ts-rest/react-query/src/lib/react-query.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,20 @@ const getRouteUseMutation = <
149149
};
150150
};
151151

152+
/** @deprecated Use `TsRestReactQueryClient` instead */
152153
export type InitClientReturn<
153154
T extends AppRouter,
154155
TClientArgs extends ClientArgs,
156+
> = UseTsRestQueryClient<T, TClientArgs>;
157+
158+
export type TsRestReactQueryClient<
159+
T extends AppRouter,
160+
TClientArgs extends ClientArgs,
155161
> = {
156162
[TKey in keyof T]: T[TKey] extends AppRoute
157163
? Without<AppRouteFunctions<T[TKey], TClientArgs>, never>
158164
: T[TKey] extends AppRouter
159-
? InitClientReturn<T[TKey], TClientArgs>
165+
? TsRestReactQueryClient<T[TKey], TClientArgs>
160166
: never;
161167
};
162168

@@ -168,10 +174,10 @@ export const initQueryClient = <
168174
>(
169175
router: T,
170176
clientArgs: TClientArgs,
171-
): InitClientReturn<T, TClientArgs> => {
177+
): TsRestReactQueryClient<T, TClientArgs> => {
172178
const recursiveInit = <TInner extends AppRouter>(
173179
innerRouter: TInner,
174-
): InitClientReturn<TInner, TClientArgs> => {
180+
): TsRestReactQueryClient<TInner, TClientArgs> => {
175181
return Object.fromEntries(
176182
Object.entries(innerRouter).map(([key, subRouter]) => {
177183
if (isAppRoute(subRouter)) {
@@ -311,23 +317,23 @@ export const initQueryClient = <
311317
};
312318
};
313319

314-
type InitUseTsRestQueryClientReturn<
320+
export type UseTsRestQueryClient<
315321
T extends AppRouter,
316322
TClientArgs extends ClientArgs,
317323
> = {
318324
[TKey in keyof T]: T[TKey] extends AppRoute
319325
? Without<AppRouteFunctionsWithQueryClient<T[TKey], TClientArgs>, never>
320326
: T[TKey] extends AppRouter
321-
? InitUseTsRestQueryClientReturn<T[TKey], TClientArgs>
327+
? UseTsRestQueryClient<T[TKey], TClientArgs>
322328
: never;
323329
};
324330

325331
export const useTsRestQueryClient = <
326332
T extends AppRouter,
327333
TClientArgs extends ClientArgs,
328334
>(
329-
client: InitClientReturn<T, TClientArgs>,
330-
): InitUseTsRestQueryClientReturn<T, TClientArgs> => {
335+
client: TsRestReactQueryClient<T, TClientArgs>,
336+
): UseTsRestQueryClient<T, TClientArgs> => {
331337
// @ts-expect-error - hidden symbol, so we can refetch the original client router and clientArgs
332338
const { router } = client[ClientParameters] as unknown as {
333339
router: T;
@@ -338,8 +344,8 @@ export const useTsRestQueryClient = <
338344

339345
const recursiveInit = <TInner extends AppRouter>(
340346
innerRouter: TInner,
341-
innerClient: InitClientReturn<TInner, TClientArgs>,
342-
): InitUseTsRestQueryClientReturn<TInner, TClientArgs> => {
347+
innerClient: TsRestReactQueryClient<TInner, TClientArgs>,
348+
): UseTsRestQueryClient<TInner, TClientArgs> => {
343349
return Object.fromEntries(
344350
Object.entries(innerRouter).map(([key, subRouter]) => {
345351
if (isAppRoute(subRouter)) {

libs/ts-rest/react-query/src/lib/types.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
PartialClientInferRequest,
1616
SuccessfulHttpStatusCode,
1717
} from '@ts-rest/core';
18-
import { InitClientReturn } from './react-query';
18+
import { TsRestReactQueryClient } from './react-query';
1919

2020
// Data response if it's a 2XX
2121
export type DataResponse<TAppRoute extends AppRoute> = ClientInferResponses<
@@ -59,34 +59,34 @@ export type UseInfiniteQueryResult<
5959
TData = DataResponse<TAppRoute>,
6060
> = TanStackUseInfiniteQueryResult<TData, ErrorResponse<TAppRoute>>;
6161

62-
type InferClientArgs<TClient extends InitClientReturn<any, any>> =
63-
TClient extends InitClientReturn<any, infer TClientArgs>
62+
type InferClientArgs<TClient extends TsRestReactQueryClient<any, any>> =
63+
TClient extends TsRestReactQueryClient<any, infer TClientArgs>
6464
? TClientArgs
6565
: never;
6666

6767
export type UseMutationOptions<
6868
TAppRoute extends AppRoute,
69-
TClientArgsOrClient extends ClientArgs | InitClientReturn<any, any>,
69+
TClientArgsOrClient extends ClientArgs | TsRestReactQueryClient<any, any>,
7070
> = TanStackUseMutationOptions<
7171
DataResponse<TAppRoute>,
7272
ErrorResponse<TAppRoute>,
7373
TClientArgsOrClient extends ClientArgs
7474
? PartialClientInferRequest<TAppRoute, TClientArgsOrClient>
75-
: TClientArgsOrClient extends InitClientReturn<any, any>
75+
: TClientArgsOrClient extends TsRestReactQueryClient<any, any>
7676
? PartialClientInferRequest<TAppRoute, InferClientArgs<TClientArgsOrClient>>
7777
: never,
7878
unknown
7979
>;
8080

8181
export type UseMutationResult<
8282
TAppRoute extends AppRoute,
83-
TClientArgsOrClient extends ClientArgs | InitClientReturn<any, any>,
83+
TClientArgsOrClient extends ClientArgs | TsRestReactQueryClient<any, any>,
8484
> = TanStackUseMutationResult<
8585
DataResponse<TAppRoute>,
8686
ErrorResponse<TAppRoute>,
8787
TClientArgsOrClient extends ClientArgs
8888
? PartialClientInferRequest<TAppRoute, TClientArgsOrClient>
89-
: TClientArgsOrClient extends InitClientReturn<any, any>
89+
: TClientArgsOrClient extends TsRestReactQueryClient<any, any>
9090
? PartialClientInferRequest<TAppRoute, InferClientArgs<TClientArgsOrClient>>
9191
: never,
9292
unknown

0 commit comments

Comments
 (0)