Skip to content

Commit a2b4298

Browse files
committed
feat(trpc): restructure trpc backend code for separation of procedures
Split procedures out from the router and separate middleware into individual files. Additionally ensure api gateway lambda event is provided to context. re #49
1 parent 3686ee5 commit a2b4298

File tree

18 files changed

+500
-221
lines changed

18 files changed

+500
-221
lines changed

packages/nx-plugin/src/trpc/backend/__snapshots__/generator.spec.ts.snap

Lines changed: 245 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,257 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3-
exports[`trpc backend generator > should generate backend and schema projects > backend-index.ts 1`] = `
4-
"export * from './lambdas/index.js';
3+
exports[`trpc backend generator > should generate backend and schema projects 1`] = `
4+
"export type { AppRouter } from './router.js';
5+
export type { Context } from './init.js';
56
"
67
`;
78

8-
exports[`trpc backend generator > should generate backend and schema projects > schema-index.ts 1`] = `
9+
exports[`trpc backend generator > should generate backend and schema projects 2`] = `
10+
"import { initTRPC } from '@trpc/server';
11+
import {
12+
createErrorPlugin,
13+
createLoggerPlugin,
14+
createMetricsPlugin,
15+
createTracerPlugin,
16+
IMiddlewareContext,
17+
} from './middleware/index.js';
18+
19+
export type Context = IMiddlewareContext;
20+
21+
export const t = initTRPC.context<Context>().create();
22+
23+
export const publicProcedure = t.procedure
24+
.unstable_concat(createLoggerPlugin())
25+
.unstable_concat(createTracerPlugin())
26+
.unstable_concat(createMetricsPlugin())
27+
.unstable_concat(createErrorPlugin());
28+
"
29+
`;
30+
31+
exports[`trpc backend generator > should generate backend and schema projects 3`] = `
32+
"import { initTRPC, TRPCError } from '@trpc/server';
33+
34+
export const createErrorPlugin = () => {
35+
const t = initTRPC.context().create();
36+
return t.procedure.use(async (opts) => {
37+
const response = await opts.next({
38+
ctx: {
39+
...opts.ctx,
40+
},
41+
});
42+
43+
if (!response.ok) {
44+
if (response.error instanceof TRPCError) {
45+
throw response.error;
46+
}
47+
48+
throw new TRPCError({
49+
code: 'INTERNAL_SERVER_ERROR',
50+
message: 'An unexpected error occurred, please try again later.',
51+
cause: response.error,
52+
});
53+
}
54+
55+
return response;
56+
});
57+
};
58+
"
59+
`;
60+
61+
exports[`trpc backend generator > should generate backend and schema projects 4`] = `
62+
"import { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';
63+
import type { APIGatewayProxyEventV2WithIAMAuthorizer } from 'aws-lambda';
64+
import { ILoggerContext } from './logger.js';
65+
import { IMetricsContext } from './metrics.js';
66+
import { ITracerContext } from './tracer.js';
67+
68+
export * from './logger.js';
69+
export * from './metrics.js';
70+
export * from './tracer.js';
71+
export * from './error.js';
72+
73+
export type IMiddlewareContext =
74+
CreateAWSLambdaContextOptions<APIGatewayProxyEventV2WithIAMAuthorizer> &
75+
ILoggerContext &
76+
IMetricsContext &
77+
ITracerContext;
78+
"
79+
`;
80+
81+
exports[`trpc backend generator > should generate backend and schema projects 5`] = `
82+
"import { initTRPC } from '@trpc/server';
83+
import { Logger } from '@aws-lambda-powertools/logger';
84+
85+
export interface ILoggerContext {
86+
logger?: Logger;
87+
}
88+
89+
export const createLoggerPlugin = () => {
90+
const t = initTRPC.context<ILoggerContext>().create();
91+
return t.procedure.use(async (opts) => {
92+
const logger = new Logger();
93+
94+
const response = await opts.next({
95+
ctx: {
96+
...opts.ctx,
97+
logger,
98+
},
99+
});
100+
101+
if (!response.ok) {
102+
logger.error('Unexpected error occured', response.error.cause ?? '');
103+
}
104+
105+
return response;
106+
});
107+
};
108+
"
109+
`;
110+
111+
exports[`trpc backend generator > should generate backend and schema projects 6`] = `
112+
"import { initTRPC } from '@trpc/server';
113+
import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics';
114+
115+
export interface IMetricsContext {
116+
metrics?: Metrics;
117+
}
118+
119+
export const createMetricsPlugin = () => {
120+
const t = initTRPC.context<IMetricsContext>().create();
121+
122+
return t.procedure.use(async (opts) => {
123+
const metrics = new Metrics();
124+
metrics.captureColdStartMetric();
125+
126+
metrics.addMetric('requestCount', MetricUnit.Count, 1);
127+
128+
try {
129+
const response = await opts.next({
130+
ctx: {
131+
...opts.ctx,
132+
metrics,
133+
},
134+
});
135+
136+
if (!response.ok) {
137+
metrics.addMetric('errorCount', MetricUnit.Count, 1);
138+
} else {
139+
metrics.addMetric('successCount', MetricUnit.Count, 1);
140+
}
141+
142+
return response;
143+
} finally {
144+
metrics.publishStoredMetrics();
145+
}
146+
});
147+
};
148+
"
149+
`;
150+
151+
exports[`trpc backend generator > should generate backend and schema projects 7`] = `
152+
"import { initTRPC } from '@trpc/server';
153+
import { Tracer } from '@aws-lambda-powertools/tracer';
154+
import { Subsegment } from 'aws-xray-sdk-core';
155+
156+
export interface ITracerContext {
157+
tracer?: Tracer;
158+
}
159+
160+
export const createTracerPlugin = () => {
161+
const t = initTRPC.context<ITracerContext>().create();
162+
163+
return t.procedure.use(async (opts) => {
164+
const tracer = new Tracer();
165+
const segment = tracer.getSegment();
166+
let handlerSegment: Subsegment | undefined;
167+
168+
if (segment) {
169+
handlerSegment = segment.addNewSubsegment(\`## \${opts.path}\`);
170+
tracer.setSegment(handlerSegment);
171+
}
172+
173+
tracer.annotateColdStart();
174+
tracer.addServiceNameAnnotation();
175+
176+
try {
177+
const response = await opts.next({
178+
ctx: {
179+
...opts.ctx,
180+
tracer,
181+
},
182+
});
183+
184+
if (!response.ok && response.error.cause instanceof Error) {
185+
tracer.addErrorAsMetadata(response.error.cause);
186+
}
187+
return response;
188+
} finally {
189+
if (segment && handlerSegment) {
190+
handlerSegment.close();
191+
tracer.setSegment(segment);
192+
}
193+
}
194+
});
195+
};
196+
"
197+
`;
198+
199+
exports[`trpc backend generator > should generate backend and schema projects 8`] = `
200+
"import { publicProcedure } from '../init.js';
201+
import { EchoInputSchema, EchoOutputSchema } from ':proj/test-api-schema';
202+
203+
export const echo = publicProcedure
204+
.input(EchoInputSchema)
205+
.output(EchoOutputSchema)
206+
.query((opts) => ({ result: opts.input.message }));
207+
"
208+
`;
209+
210+
exports[`trpc backend generator > should generate backend and schema projects 9`] = `
211+
"import {
212+
awsLambdaRequestHandler,
213+
CreateAWSLambdaContextOptions,
214+
} from '@trpc/server/adapters/aws-lambda';
215+
import { echo } from './procedures/echo.js';
216+
import { t } from './init.js';
217+
import { APIGatewayProxyEventV2WithIAMAuthorizer } from 'aws-lambda';
218+
219+
export const router = t.router;
220+
221+
const appRouter = router({
222+
echo,
223+
});
224+
225+
export const handler = awsLambdaRequestHandler({
226+
router: appRouter,
227+
createContext: (
228+
ctx: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2WithIAMAuthorizer>,
229+
) => ctx,
230+
});
231+
232+
export type AppRouter = typeof appRouter;
233+
"
234+
`;
235+
236+
exports[`trpc backend generator > should generate backend and schema projects 10`] = `
237+
"export * from './procedures/echo.js';
238+
"
239+
`;
240+
241+
exports[`trpc backend generator > should generate backend and schema projects 11`] = `
9242
"import { z } from 'zod';
10243
11-
export const EchoSchema = z.object({
244+
export const EchoInputSchema = z.object({
245+
message: z.string(),
246+
});
247+
248+
export type IEchoInput = z.TypeOf<typeof EchoInputSchema>;
249+
250+
export const EchoOutputSchema = z.object({
12251
result: z.string(),
13252
});
14253
15-
export type IEcho = z.TypeOf<typeof EchoSchema>;
254+
export type IEchoOutput = z.TypeOf<typeof EchoOutputSchema>;
16255
"
17256
`;
18257
@@ -33,7 +272,7 @@ export class TestApi extends TrpcApi {
33272
defaultAuthorizer: new HttpIamAuthorizer(),
34273
handlerFilePath: url.fileURLToPath(
35274
new URL(
36-
'../../../../../../apps/test-api/backend/src/lambdas/router.ts',
275+
'../../../../../../apps/test-api/backend/src/router.ts',
37276
import.meta.url,
38277
),
39278
),
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export * from './lambdas/index.js';
1+
export type { AppRouter } from './router.js';
2+
export type { Context } from './init.js';
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { initTRPC } from '@trpc/server';
2+
import {
3+
createErrorPlugin,
4+
createLoggerPlugin,
5+
createMetricsPlugin,
6+
createTracerPlugin,
7+
IMiddlewareContext,
8+
} from './middleware/index.js';
9+
10+
export type Context = IMiddlewareContext;
11+
12+
export const t = initTRPC.context<Context>().create();
13+
14+
export const publicProcedure = t.procedure
15+
.unstable_concat(createLoggerPlugin())
16+
.unstable_concat(createTracerPlugin())
17+
.unstable_concat(createMetricsPlugin())
18+
.unstable_concat(createErrorPlugin());

packages/nx-plugin/src/trpc/backend/files/backend/src/lambdas/index.ts.template

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)