NicolasN
tRPC3y ago
4 replies
Nicolas

Creating inner context for AWS Lambda Context Options

Hi All,

I have been using tRPC for many routes, and I have started to realize I need to call certain routes on the server-side (lambda / serverless in my case), and so I was trying to follow the documentation on how to create an inner context / outer context so I can call my tRPC routes from other routes, but I am stuck and I am confused on how to go about doing it for AWS lambda context as the documentation example is rather simple.

This is what I have so far:
import { inferAsyncReturnType } from "@trpc/server";
import type { CreateAWSLambdaContextOptions } from "@trpc/server/adapters/aws-lambda";
import type { APIGatewayProxyEventV2 } from "aws-lambda";
import { CognitoJwtVerifier } from "aws-jwt-verify";

/**
 * Defines your inner context shape.
 * Add fields here that the inner context brings.
 */
interface CreateInnerContextOptions extends Partial<CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>>{
  context: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>["context"] | undefined;
  event: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>["event"] | undefined;

}

/**
 * Inner context. Will always be available in your procedures, in contrast to the outer context.
 *
 * Also useful for:
 * - testing, so you don't have to mock Next.js' `req`/`res`
 * - tRPC's `createServerSideHelpers` where we don't have `req`/`res`
 *
 * @see https://trpc.io/docs/context#inner-and-outer-context
 */
export async function createContextInner(opts?: CreateInnerContextOptions) {
  return {       
    context: opts?.context,
    event: opts?.event,    
  };
}

/**
 * Outer context. Used in the routers and will e.g. bring `req` & `res` to the context as "not `undefined`".
 *
 * @see https://trpc.io/docs/context#inner-and-outer-context
 */

export async function createContext({ event }: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>) {
  async function getUserFromHeader() {
    // Verifier that expects valid access tokens:
    if (process.env.USER_POOL_ID === undefined || process.env.USER_POOL_CLIENT_ID == undefined) {
      throw new Error("Missing environment variables");
    }
    if (event.headers.authorization) {
      const token = event.headers.authorization.split(" ")[1];
      const verifier = CognitoJwtVerifier.create({
        userPoolId: process.env.USER_POOL_ID,
        tokenUse: "id",
        clientId: process.env.USER_POOL_CLIENT_ID,
      });
      // the Authorization header will contain the JWT token as a Bearer token, we need to remove the Bearer part before we can verify the token
      const payload = await verifier.verify(token);
      return {
        role: payload["cognito:groups"],
        username: payload["cognito:username"],
        orgId: payload["custom:organizationId"] ? (payload["custom:organizationId"] as string) : undefined,
      };
    }
    throw new Error("No authorization header");
  }
  const user = await getUserFromHeader();

  const contextInner = await createContextInner({ context: undefined, event: undefined });

  return {
    ...contextInner,
    event: event,
    apiVersion: (event as { version?: string }).version ?? "1.0",
    user: user,
  };
}



export type Context = inferAsyncReturnType<typeof createContextInner>;
Was this page helpful?