trpc + AWS Lambda (through cdk)

Hi all, has anyone successfully integrated tRPC with AWS Lambda? My current stack is API Gateway + Lambda, all created through cdk. I am trying to figure out how to hook up the tRPC router and client to the lambda code. This is my first time using tRPC 😅
N
Nick408d ago
Have you looked at the Lambda Adapter and its docs?
F
ferdy408d ago
yeah, I have been trying to figure out how to make that sample code work. But it doesn't show me how to fit that code into the surrounding cdk infrastructure. I see that they export a handler:
export const handler = awsLambdaRequestHandler(...)
export const handler = awsLambdaRequestHandler(...)
but when I try to reference that handler from cdk it's not quite working:
const testLambda = new aws_lambda.Function(this, "testLambda", {
handler:("/path/to/ApiEntryPoint.handler"),
...
const testLambda = new aws_lambda.Function(this, "testLambda", {
handler:("/path/to/ApiEntryPoint.handler"),
...
L
Lois408d ago
remember to add AWS extension in your VSCode the prompt + autocomplete is good
N
Nick408d ago
So exporting a handler is a standard lambda thing, that’s the boundary of your application At that point you’re looking at CDK docs to integrate, though honestly I haven’t used CDK. For my own local dev I just use a standalone trpc adapter, and the lambda adapter is just for AWS deployments
F
ferdy407d ago
ok I got it mostly figured out -- for any other wayward souls with this issue, check out this github repo that really helped me out: https://github.com/jacksonludwig/trpc-repro
GitHub
GitHub - jacksonludwig/trpc-repro
Contribute to jacksonludwig/trpc-repro development by creating an account on GitHub.
F
ferdy407d ago
@Nick Lucas thanks for the help. I have one more question: when creating my client typescript keeps complaining to me that I am not providing a transformer, and I don't really know what I am supposed to pass in for that. The sample code I linked above doesn't seem to have to do that, I guess because they are not using createTRPCProxyClient. Do you know why that is or what I can use as a transformer?
N
Nick407d ago
That's a tRPC thing, check the docs for transformers 🙂 It's easy!
F
ferdy407d ago
I will, though I am still curious why every example I've seen of the proxy client doesnt include having to specify a transformer
N
Nick407d ago
You don't have to, but you also can't go half in, which is what you might have inadvertently done if it's moaning at you
F
ferdy407d ago
const client = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: "https://xxxxxxxx.execute-api.us-west-2.amazonaws.com/prod/"
})
],
});
const client = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: "https://xxxxxxxx.execute-api.us-west-2.amazonaws.com/prod/"
})
],
});
^^^ this doesnt compile
N
Nick407d ago
Can you share your appRouter too?
F
ferdy407d ago
yeah
export const createContext = (_: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>) => ({});
type Context = inferAsyncReturnType<typeof createContext>;

const t = initTRPC.context<Context>().create();

export const middleware = t.middleware;
export const router = t.router;
export const procedure = t.procedure;
export const mergeRouters = t.mergeRouters;

const appRouter = router({
testRoute: procedure.query(async () => {
// mock fast db lookup
await new Promise((res) => setTimeout(res, 100));

return {
value: 5,
};
}),
testRoute2: procedure.query(async () => {
await new Promise((res) => setTimeout(res, 100));

return {
obj: {
value: 10,
},
};
}),
});

export const handler = awsLambdaRequestHandler({
router: appRouter,
createContext,
});

export type AppRouter = typeof appRouter;
export const createContext = (_: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>) => ({});
type Context = inferAsyncReturnType<typeof createContext>;

const t = initTRPC.context<Context>().create();

export const middleware = t.middleware;
export const router = t.router;
export const procedure = t.procedure;
export const mergeRouters = t.mergeRouters;

const appRouter = router({
testRoute: procedure.query(async () => {
// mock fast db lookup
await new Promise((res) => setTimeout(res, 100));

return {
value: 5,
};
}),
testRoute2: procedure.query(async () => {
await new Promise((res) => setTimeout(res, 100));

return {
obj: {
value: 10,
},
};
}),
});

export const handler = awsLambdaRequestHandler({
router: appRouter,
createContext,
});

export type AppRouter = typeof appRouter;
N
Nick407d ago
What's the compile error for your example? This does look fine so it's weird you're having an issue
F
ferdy407d ago
TS2345: Argument of type '{ links: TRPCLink<CreateRouterInner<RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>, { testRoute: BuildProcedure<"query", { _config: RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>; ... 5 more ...; _meta: object; }, { ...; ...' is not assignable to parameter of type 'CreateTRPCClientOptions<CreateRouterInner<RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>, { testRoute: BuildProcedure<"query", { _config: RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>; ... 5 more ...; _meta: object; }, {...'.   Property 'transformer' is missing in type '{ links: TRPCLink<CreateRouterInner<RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>, { testRoute: BuildProcedure<"query", { _config: RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>; ... 5 more ...; _meta: object; }, { ...; ...' but required in type '{ transformer: DataTransformerOptions; }'.
TS2345: Argument of type '{ links: TRPCLink<CreateRouterInner<RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>, { testRoute: BuildProcedure<"query", { _config: RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>; ... 5 more ...; _meta: object; }, { ...; ...' is not assignable to parameter of type 'CreateTRPCClientOptions<CreateRouterInner<RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>, { testRoute: BuildProcedure<"query", { _config: RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>; ... 5 more ...; _meta: object; }, {...'.   Property 'transformer' is missing in type '{ links: TRPCLink<CreateRouterInner<RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>, { testRoute: BuildProcedure<"query", { _config: RootConfig<{ ctx: {}; meta: object; errorShape: never; transformer: DataTransformerOptions; }>; ... 5 more ...; _meta: object; }, { ...; ...' but required in type '{ transformer: DataTransformerOptions; }'.
N
Nick407d ago
Hm, what's your @trpc/* version? Are they all identical?
F
ferdy407d ago
"@trpc/server": "^10.14.0",
"@trpc/client": "^10.14.0",
"@trpc/server": "^10.14.0",
"@trpc/client": "^10.14.0",
N
Nick407d ago
I've definitely seen someone else asking about this, and I think they resolved it, just can't find it now
F
ferdy407d ago
ok ill search around as well
N
Nick407d ago
Reload your editor / TS language server and restart your build if you haven't
F
ferdy407d ago
no luck with that
N
Nick407d ago
bah, this is a weird one This is an error that appears when you have set a transformer on the API but not on the frontend It's intentional and I can reproduce that in my own codebase But transformers are optional I suspect it might be something weird related to your repo setup. If you install superjson and get that set up, do any new errors appear?
F
ferdy407d ago
I was able to pass in superjson and get around the error, but then the response from my lambda was coming back undefined -- I was wondering if the default transformer would've just handled the response correctly
N
Nick407d ago
At runtime?
F
ferdy407d ago
yep. and I tested out calling the api directly in my aws console, confirmed its working. Also confirmed that the lambda is getting invoked when I call it from the client by double checking logs it's just the return value isn't getting processed correctly
N
Nick407d ago
That might be something more related to API Gateway if you're using that honestly AWS is a weird balancing act in my experience There's talk to adding some more in-depth docs but we're not there yet
F
ferdy407d ago
it definitely could be an apig oddity, though I can see it in the console returning the json response correctly. Maybe I need to find somewhere in between that I can inspect to see where that response is getting lost... Like a debug log from the trpc client or something
N
Nick407d ago
Yep I put console logs at several stages, exported my own handler and called the lambda adapter's handler within it so I could log before and after. It's a pain but that's AWS 😄 Did also have some similar teething troubles like you
F
ferdy407d ago
ok I got it working -- truly dont understand this yet but adding Context to the type parameter of create did the trick:
export const createContext = ({event, context}: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>) => ({});
type Context = inferAsyncReturnType<typeof createContext>;

const t = initTRPC.context<Context>().create<Context>();
export const createContext = ({event, context}: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>) => ({});
type Context = inferAsyncReturnType<typeof createContext>;

const t = initTRPC.context<Context>().create<Context>();
also fixed having to supply a transformer
N
Nick407d ago
Huh, that’s very weird Typescript issue maybe? Is strict mode enabled?
F
ferdy407d ago
ohh no it wasnt. I didn't do the initial setup of this codebase, didn't even notice strict wasnt on. Enabling strict mode and removing the extra <Context> works, compile-wise -- not sure yet about runtime
N
Nick407d ago
That’s good, strict mode fixes a lot of stuff in these well-typed frameworks
UU
Unknown User407d ago
F
Falo360d ago
Ferdy, i'm wondering how to push the code on aws, did you compile your code with tsc and send the dist folder to aws lambda ? Because i have a tons of problem with tsc and running the js file
F
ferdy352d ago
i do everything through cdk, specifically using NodejsFunction
F
ferdy352d ago
Snyk Advisor
Find the best open-source package for your project with Snyk Open Source Advisor. Explore over 1 million open source packages.
More Posts
Is it possible to narrow an output schema if the query optionally doesn't return all fields?I have a router procedure that has an input schema that has an optional `filter` that changes the shFetching different server url than defined in configIs it possible to access the reactQuery instance and fetch different server url? I would like to useinput using z.or not working properlyi have an input like this let input = z.object({ name: z.string().optional() }).or(z.object({ How can I disable batching with fastify adapter?I cant seem to find a way to disable batching for my server, and this link doesnt help me much httpsIssue with monorepo architecture ant tRPCHi, we had an issue with batched requests that if we batch some requests they produce a TRPCClientErUsing tRPC in CRON jobsHey everyone, this might be a very stupid question, but is it possible to use tRPC inside a CRON joasync createContext for Express AdapterBeen debugging an odd behavior for the past hour, it seems like that an async function does not workIs it possible to split the router definition with the imlementation?I want to define the server router(input\output\meta) in a separate package from the server package Cache not working for `useQuery`I have a query like this: ```js const { data: article, isFetching } = api.public.getArticle.useQueryZod File Upload Validation with Open-Api Support?Hi guys, anyone know how to validate file upload with zod and get also open-api support?Zod validation and open-api support for File on the server?Hi guys, anyone know how to validate a File upload using zod? and also have open-api support?is context cached?If I put an object on the context that represents the User record from my database... ``` export asyJSON inferred router output not matchingHello. I have a procedure query which is returning Json from a postgresql db using Prisma. The type Best way to implement input based validation on a router procedureHi guys, bit of a noob. I have already created a 'protectedProcedure', ensuring the user is logged [Help] Turbo shared typesI have a turborepo with two apps (web and embed). web is a t3 stack and embed is a create-react-app.Cache SSG helper responseI'm using `createProxySSGHelpers` in GSSP with `ssr: false` in the global config. I trying to cacheInput is too big for a single dispatchI decided to try tRPC for my Crypto analytics dashboard. However, I'm having a hard time passing theHow to manage custom errors (e.g. custom error codes) in tRPC?What's the recommended way to add fields to a TRPCError? How do you make that typesafe also on the ctypesafe permissionsHi, So I wanted to infer all the procedures from my router recursively & assign a permission (stringawaiting for procedure & logging the response.Hi, I was wondering if there is a way to handle the return object via the post-middleware's? I know