I'm following the tRPC docs on how to create a caller for server side calls but how are you suppose to set this value dynamically when used in your NextJS 13 pages?
This is what my context.ts looks like:
import { cookies } from "next/headers";
import * as jose from "jose";
import jwt from "jsonwebtoken";
import { inferAsyncReturnType } from "@trpc/server";
interface ContextUser {
id: number;
username: string;
token: string;
}
export async function createContext() {
async function getUserFromCookie(): Promise<ContextUser | null> {
var token = cookies().get("jwt");
if (!token?.value) return null;
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
try {
await jose.jwtVerify(token.value, secret);
} catch (error) {
return null;
}
const jwtPayload = jwt.decode(token.value) as any;
return {
id: jwtPayload.nameid,
username: jwtPayload.unique_name,
token: token.value,
};
}
const user = await getUserFromCookie();
return {
user,
};
}
export type Context = inferAsyncReturnType<typeof createContext>;
I have middleware to check that a user is set and I export a authorised procedure:
import { TRPCError, initTRPC } from "@trpc/server";
import { Context } from "./context";
const t = initTRPC.context<Context>().create();
export const router = t.router;
export const publicProcedure = t.procedure;
const authorised = t.middleware(async ({ ctx, next }) => {
if (!ctx.user) throw new TRPCError({ code: "UNAUTHORIZED" });
return next();
});
export const authorisedProcedure = t.procedure.use(authorised);
In my server.ts i'm exporting a server client like this:
import { appRouter } from "@/server";
export const trpcServerClient = appRouter.createCaller({
user: { id: 1, username: "foo", token: "bar" },
});
When I call a procedure using the trpcServerClient
with a valid cookie I can see by debugging the createContext
that the token is present and it set the context, but in the procedure the ctx
will be user: { id: 1, username: "foo", token: "bar" }
.
My question then is how are you suppose to dynamically set the context when using a server client, when you have to pass a value for your context in the createCaller
function?
import { createContext } from "@/server/context"
import { appRouter } from "@/server";
export const trpcServerClient = appRouter.createCaller(await createContext());
(note that using await at top level may require changing your tsconfig target if you are still using "es5")