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 { db } from "~/server/db";
14import { validateSessionTokenCookie } from "../auth/validate";
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 db,
31 ...opts,
32 };
33};
34
35/**
36 * 2. INITIALIZATION
37 *
38 * This is where the tRPC API is initialized, connecting the context and transformer. We also parse
39 * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
40 * errors on the backend.
41 */
42const t = initTRPC.context<typeof createTRPCContext>().create({
43 transformer: superjson,
44 errorFormatter({ shape, error }) {
45 return {
46 ...shape,
47 data: {
48 ...shape.data,
49 zodError:
50 error.cause instanceof ZodError ? error.cause.flatten() : null,
51 },
52 };
53 },
54});
55
56/**
57 * Create a server-side caller.
58 *
59 * @see https://trpc.io/docs/server/server-side-calls
60 */
61export const createCallerFactory = t.createCallerFactory;
62
63/**
64 * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
65 *
66 * These are the pieces you use to build your tRPC API. You should import these a lot in the
67 * "/src/server/api/routers" directory.
68 */
69
70/**
71 * This is how you create new routers and sub-routers in your tRPC API.
72 *
73 * @see https://trpc.io/docs/router
74 */
75export const createTRPCRouter = t.router;
76
77/**
78 * Middleware for timing procedure execution and adding an artificial delay in development.
79 *
80 * You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating
81 * network latency that would occur in production but not in local development.
82 */
83const timingMiddleware = t.middleware(async ({ next, path }) => {
84 const start = Date.now();
85
86 if (t._config.isDev) {
87 // artificial delay in dev
88 const waitMs = Math.floor(Math.random() * 400) + 100;
89 await new Promise((resolve) => setTimeout(resolve, waitMs));
90 }
91
92 const result = await next();
93
94 const end = Date.now();
95 console.log(`[TRPC] ${path} took ${end - start}ms to execute`);
96
97 return result;
98});
99
100/**
101 * Public (unauthenticated) procedure
102 *
103 * This is the base piece you use to build new queries and mutations on your tRPC API. It does not
104 * guarantee that a user querying is authorized, but you can still access user session data if they
105 * are logged in.
106 */
107export const publicProcedure = t.procedure.use(timingMiddleware);
108
109export const protectedProcedure = t.procedure.use(async function (opts) {
110 const user = await validateSessionTokenCookie();
111
112 if (!user) {
113 throw new TRPCError({ code: "UNAUTHORIZED" });
114 }
115
116 return opts.next({
117 ctx: {
118 user,
119 },
120 });
121});