1/**
2 * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS:
3 * 1. You want to modify request context (see Part 1).
4 * 2. You want to create a new middleware or type of procedure (see Part 3).
5 *
6 * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
7 * need to use are documented accordingly near the end.
8 */
9import { initTRPC, TRPCError } from '@trpc/server';
10import superjson from 'superjson';
11import { ZodError } from 'zod';
12
13import { validateSessionToken } from '../auth/validateSession';
14import logger from '../logger';
15
16/**
17 * 1. CONTEXT
18 *
19 * This section defines the "contexts" that are available in the backend API.
20 *
21 * These allow you to access things when processing a request, like the database, the session, etc.
22 *
23 * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each
24 * wrap this and provides the required context.
25 *
26 * @see https://trpc.io/docs/server/context
27 */
28export const createTRPCContext = async (opts: { headers: Headers }) => {
29 return {
30 ...opts,
31 };
32};
33
34/**
35 * 2. INITIALIZATION
36 *
37 * This is where the tRPC API is initialized, connecting the context and transformer. We also parse
38 * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
39 * errors on the backend.
40 */
41const t = initTRPC.context<typeof createTRPCContext>().create({
42 transformer: superjson,
43 errorFormatter({ shape, error }) {
44 return {
45 ...shape,
46 data: {
47 ...shape.data,
48 zodError:
49 error.cause instanceof ZodError ? error.cause.flatten() : null,
50 },
51 };
52 },
53});
54
55/**
56 * Create a server-side caller.
57 *
58 * @see https://trpc.io/docs/server/server-side-calls
59 */
60export const createCallerFactory = t.createCallerFactory;
61
62/**
63 * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
64 *
65 * These are the pieces you use to build your tRPC API. You should import these a lot in the
66 * "/src/server/api/routers" directory.
67 */
68
69/**
70 * This is how you create new routers and sub-routers in your tRPC API.
71 *
72 * @see https://trpc.io/docs/router
73 */
74export const createTRPCRouter = t.router;
75
76/**
77 * Middleware for timing procedure execution and adding an artificial delay in development.
78 *
79 * You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating
80 * network latency that would occur in production but not in local development.
81 */
82const timingMiddleware = t.middleware(async ({ next, path }) => {
83 const start = Date.now();
84
85 if (t._config.isDev) {
86 // artificial delay in dev
87 const waitMs = Math.floor(Math.random() * 400) + 100;
88 await new Promise(resolve => setTimeout(resolve, waitMs));
89 }
90
91 const result = await next();
92
93 const end = Date.now();
94 logger.info(`[TRPC] ${path} took ${end - start}ms to execute`);
95
96 return result;
97});
98
99/**
100 * Public (unauthenticated) procedure
101 *
102 * This is the base piece you use to build new queries and mutations on your tRPC API. It does not
103 * guarantee that a user querying is authorized, but you can still access user session data if they
104 * are logged in.
105 */
106export const publicProcedure = t.procedure.use(timingMiddleware);
107
108export const protectedProcedure = t.procedure.use(async function (opts) {
109 const user = await validateSessionToken();
110
111 if (!user) {
112 throw new TRPCError({ code: 'UNAUTHORIZED' });
113 }
114
115 return opts.next({
116 ctx: {
117 user,
118 },
119 });
120});