A self hosted solution for privately rating and reviewing different sorts of media
at master 3.4 kB view raw
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});