Skip to content

Commit 45e5104

Browse files
iceStormGabrola
andauthored
feat(core): add router-level metadata (#581)
Co-authored-by: Youssef Gaber <1728215+Gabrola@users.noreply.github.com>
1 parent 60f90fa commit 45e5104

3 files changed

Lines changed: 75 additions & 0 deletions

File tree

‎.changeset/twelve-ghosts-eat.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ts-rest/core': minor
3+
---
4+
5+
Add router-level metadata

‎libs/ts-rest/core/src/lib/dsl.spec.ts‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ContractNoBodyType,
99
} from './dsl';
1010
import type { Equal, Expect } from './test-helpers';
11+
import { Prettify } from './type-utils';
1112

1213
const c = initContract();
1314

@@ -615,6 +616,65 @@ describe('contract', () => {
615616
>;
616617
});
617618

619+
it('should merge metadata options from router to its routes', () => {
620+
const contract = c.router(
621+
{
622+
getPost: {
623+
method: 'GET',
624+
path: '/posts/:id',
625+
responses: {
626+
200: c.type<{ id: number }>(),
627+
},
628+
},
629+
deletePost: {
630+
method: 'DELETE',
631+
path: '/posts/:id',
632+
body: c.type<undefined>(),
633+
metadata: {
634+
requireAuth: false,
635+
headerName: 'x-authorization',
636+
},
637+
responses: {
638+
200: c.type<{ id: number }>(),
639+
},
640+
},
641+
},
642+
{
643+
metadata: {
644+
requireAuth: true,
645+
},
646+
},
647+
);
648+
649+
expect(contract.getPost.metadata).toStrictEqual({
650+
requireAuth: true,
651+
});
652+
653+
expect(contract.deletePost.metadata).toStrictEqual({
654+
requireAuth: false,
655+
headerName: 'x-authorization',
656+
});
657+
658+
type MetadataShape = Expect<
659+
Equal<
660+
typeof contract.getPost.metadata,
661+
{
662+
requireAuth: boolean;
663+
}
664+
>
665+
>;
666+
667+
type MetadataShape2 = Expect<
668+
Equal<
669+
Prettify<typeof contract.deletePost.metadata>,
670+
{
671+
requireAuth: boolean;
672+
headerName: string;
673+
}
674+
>
675+
>;
676+
});
677+
618678
describe('pathPrefix', () => {
619679
it('Should recursively apply pathPrefix to path', () => {
620680
const postsContractNested = c.router(

‎libs/ts-rest/core/src/lib/dsl.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ type ApplyOptions<
145145
responses: 'commonResponses' extends keyof TOptions
146146
? Prettify<Merge<TOptions['commonResponses'], TRoute['responses']>>
147147
: TRoute['responses'];
148+
metadata: 'metadata' extends keyof TOptions
149+
? Prettify<Merge<TOptions['metadata'], TRoute['metadata']>>
150+
: TRoute['metadata'];
148151
}>;
149152

150153
/**
@@ -168,6 +171,7 @@ export type RouterOptions<TPrefix extends string = string> = {
168171
strictStatusCodes?: boolean;
169172
pathPrefix?: TPrefix;
170173
commonResponses?: Record<number, AppRouteResponse>;
174+
metadata?: unknown;
171175

172176
/**
173177
* @deprecated Use `validateResponse` on the client options
@@ -273,6 +277,12 @@ const recursivelyApplyOptions = <T extends AppRouter>(
273277
...options?.commonResponses,
274278
...value.responses,
275279
},
280+
metadata: options?.metadata
281+
? {
282+
...options?.metadata,
283+
...(value.metadata ?? {}),
284+
}
285+
: value.metadata,
276286
},
277287
];
278288
} else {

0 commit comments

Comments
 (0)