Skip to content

Commit ceaae30

Browse files
committed
Add global headers
1 parent 3aa52a4 commit ceaae30

File tree

2 files changed

+99
-9
lines changed

2 files changed

+99
-9
lines changed

src/__test__/trpcToOpenApi.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ describe("trpcToOpenApi", () => {
6969
},
7070
},
7171
},
72+
components: {},
7273
});
7374
});
7475

@@ -140,6 +141,7 @@ describe("trpcToOpenApi", () => {
140141
},
141142
},
142143
},
144+
components: {},
143145
});
144146
});
145147

@@ -197,4 +199,56 @@ describe("trpcToOpenApi", () => {
197199
]);
198200
});
199201
});
202+
203+
describe("globalHeaders", () => {
204+
it("includes headers in every endpoint", () => {
205+
const t = initTRPC.create();
206+
const router = t.router({
207+
createThing: t.procedure
208+
.input(z.object({ name: z.string() }))
209+
.mutation(() => undefined),
210+
getThing: t.procedure
211+
.input(z.object({ name: z.string() }))
212+
.query(() => undefined),
213+
});
214+
215+
const openApiSpec = trpcToOpenApi({
216+
apiTitle: "My API",
217+
apiVersion: "1.0",
218+
basePath: "",
219+
router,
220+
globalHeaders: {
221+
MyHeader: {
222+
in: "header",
223+
name: "X-My-Header",
224+
schema: { type: "string" },
225+
required: false,
226+
},
227+
},
228+
});
229+
230+
expect(openApiSpec.components).toEqual({
231+
parameters: {
232+
MyHeader: {
233+
in: "header",
234+
name: "X-My-Header",
235+
schema: { type: "string" },
236+
required: false,
237+
},
238+
},
239+
});
240+
241+
const expectedHeaderReferences = [
242+
{ $ref: "#/components/parameters/MyHeader" },
243+
];
244+
245+
expect(openApiSpec.paths?.["/createThing"]?.post?.parameters).toEqual(
246+
expectedHeaderReferences,
247+
);
248+
249+
expect(
250+
openApiSpec.paths?.["/getThing"]?.get?.parameters?.slice(1),
251+
).toEqual(expectedHeaderReferences);
252+
});
253+
});
200254
});

src/trpcToOpenApi.ts

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
type ProcedureType,
55
type RouterRecord,
66
} from "@trpc/server/unstable-core-do-not-import";
7-
import { type OpenAPIV3, type OpenAPIV3_1 } from "openapi-types";
7+
import { OpenAPIV3, type OpenAPIV3_1 } from "openapi-types";
88
import { type ZodSchema } from "zod";
99
import { zodToJsonSchema } from "zod-to-json-schema";
1010
import { OpenApiMeta } from "./meta.js";
@@ -16,19 +16,32 @@ export function trpcToOpenApi({
1616
apiVersion,
1717
basePath,
1818
router,
19+
globalHeaders,
1920
}: {
2021
apiTitle: string;
2122
apiVersion: string;
2223
basePath: string;
2324
router: AnyTRPCRouter;
25+
globalHeaders?: Record<string, OpenAPIV3_1.ParameterObject>;
2426
}): OpenAPIV3_1.Document {
27+
const headerParameters =
28+
globalHeaders != null
29+
? Object.keys(globalHeaders).map(
30+
(headerKey): OpenAPIV3_1.ReferenceObject => ({
31+
$ref: `#/components/parameters/${headerKey}`,
32+
}),
33+
)
34+
: undefined;
35+
2536
return {
2637
openapi: "3.1.0",
2738
info: { title: apiTitle, version: apiVersion },
28-
paths: getPathsForRouterRecord(
39+
paths: getPathsForRouterRecord({
2940
basePath,
30-
router._def.procedures as RouterRecord,
31-
),
41+
routerRecord: router._def.procedures as RouterRecord,
42+
additionalParameters: headerParameters,
43+
}),
44+
components: globalHeaders != null ? { parameters: globalHeaders } : {},
3245
};
3346
}
3447

@@ -41,10 +54,17 @@ const PROCEDURE_TYPE_HTTP_METHOD_MAP: Record<
4154
subscription: undefined,
4255
};
4356

44-
function getPathsForRouterRecord(
45-
basePath: string,
46-
routerRecord: RouterRecord,
47-
): OpenAPIV3_1.PathsObject {
57+
function getPathsForRouterRecord({
58+
basePath,
59+
routerRecord,
60+
additionalParameters,
61+
}: {
62+
basePath: string;
63+
routerRecord: RouterRecord;
64+
additionalParameters:
65+
| (OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.ParameterObject)[]
66+
| undefined;
67+
}): OpenAPIV3_1.PathsObject {
4868
const paths: OpenAPIV3_1.PathsObject = {};
4969

5070
for (const [procedureName, procedureOrRouterRecord] of entries(
@@ -57,8 +77,13 @@ function getPathsForRouterRecord(
5777
basePath,
5878
procedureName: String(procedureName),
5979
procedure: procedureOrRouterRecord,
80+
additionalParameters,
6081
})
61-
: getPathsForRouterRecord(basePath, procedureOrRouterRecord),
82+
: getPathsForRouterRecord({
83+
basePath,
84+
routerRecord: procedureOrRouterRecord,
85+
additionalParameters,
86+
}),
6287
);
6388
}
6489

@@ -69,10 +94,14 @@ function getPathsForProcedure({
6994
basePath,
7095
procedureName,
7196
procedure,
97+
additionalParameters,
7298
}: {
7399
basePath: string;
74100
procedureName: string;
75101
procedure: AnyProcedure;
102+
additionalParameters:
103+
| (OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.ParameterObject)[]
104+
| undefined;
76105
}): OpenAPIV3_1.PathsObject {
77106
const def = procedure._def as unknown as AnyProcedure["_def"] &
78107
ProcedureBuilderDef;
@@ -108,6 +137,13 @@ function getPathsForProcedure({
108137
content,
109138
};
110139
}
140+
141+
if (additionalParameters != null) {
142+
operation.parameters = [
143+
...(operation.parameters ?? []),
144+
...(additionalParameters ?? []),
145+
];
146+
}
111147
}
112148

113149
return {

0 commit comments

Comments
 (0)