From 4ad5c934ec8680f58ce38417714417a39ab1712c Mon Sep 17 00:00:00 2001 From: Vikrant Bhat <50735025+bhatvikrant@users.noreply.github.com> Date: Sat, 23 Jul 2022 03:10:30 +0530 Subject: [PATCH] chore: add protected trpc router example (#194) --- src/installers/trpc.ts | 27 ++++++++++++++----- template/addons/trpc/auth-index-router.ts | 4 +-- template/addons/trpc/auth-router.ts | 22 --------------- .../addons/trpc/protected-example-router.ts | 14 ++++++++++ template/addons/trpc/protected-router.ts | 20 ++++++++++++++ 5 files changed, 57 insertions(+), 30 deletions(-) delete mode 100644 template/addons/trpc/auth-router.ts create mode 100644 template/addons/trpc/protected-example-router.ts create mode 100644 template/addons/trpc/protected-router.ts diff --git a/src/installers/trpc.ts b/src/installers/trpc.ts index 7789702640..63652604a4 100644 --- a/src/installers/trpc.ts +++ b/src/installers/trpc.ts @@ -40,12 +40,6 @@ export const trpcInstaller: Installer = async ({ const contextSrc = path.join(trpcAssetDir, contextFile); const contextDest = path.join(projectDir, "src/server/router/context.ts"); - if (usingAuth) { - const authRouterSrc = path.join(trpcAssetDir, "auth-router.ts"); - const authRouterDest = path.join(projectDir, "src/server/router/auth.ts"); - await fs.copy(authRouterSrc, authRouterDest); - } - const indexRouterFile = usingAuth ? "auth-index-router.ts" : "index-router.ts"; @@ -61,11 +55,32 @@ export const trpcInstaller: Installer = async ({ "src/server/router/example.ts", ); + const protectedExampleRouterSrc = path.join( + trpcAssetDir, + "protected-example-router.ts", + ); + const protectedExampleRouterDest = path.join( + projectDir, + "src/server/router/protected-example-router.ts", + ); + + const protectedRouterSrc = path.join(trpcAssetDir, "protected-router.ts"); + const protectedRouterDest = path.join( + projectDir, + "src/server/router/protected-router.ts", + ); + await Promise.all([ fs.copy(apiHandlerSrc, apiHandlerDest), fs.copy(utilsSrc, utilsDest), fs.copy(contextSrc, contextDest), fs.copy(indexRouterSrc, indexRouterDest), fs.copy(exampleRouterSrc, exampleRouterDest), + ...(usingAuth + ? [ + fs.copy(protectedExampleRouterSrc, protectedExampleRouterDest), + fs.copy(protectedRouterSrc, protectedRouterDest), + ] + : []), ]); }; diff --git a/template/addons/trpc/auth-index-router.ts b/template/addons/trpc/auth-index-router.ts index 5c83f8c125..9714320fb1 100644 --- a/template/addons/trpc/auth-index-router.ts +++ b/template/addons/trpc/auth-index-router.ts @@ -3,12 +3,12 @@ import { createRouter } from "./context"; import superjson from "superjson"; import { exampleRouter } from "./example"; -import { authRouter } from "./auth"; +import { protectedExampleRouter } from "./protected-example-router"; export const appRouter = createRouter() .transformer(superjson) .merge("example.", exampleRouter) - .merge("auth.", authRouter); + .merge("question.", protectedExampleRouter); // export type definition of API export type AppRouter = typeof appRouter; diff --git a/template/addons/trpc/auth-router.ts b/template/addons/trpc/auth-router.ts deleted file mode 100644 index 73f60a9192..0000000000 --- a/template/addons/trpc/auth-router.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { TRPCError } from "@trpc/server"; -import { createRouter } from "./context"; - -export const authRouter = createRouter() - .query("getSession", { - resolve({ ctx }) { - return ctx.session; - }, - }) - .middleware(async ({ ctx, next }) => { - // Any queries or mutations after this middleware will - // raise an error unless there is a current session - if (!ctx.session) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - return next(); - }) - .query("getSecretMessage", { - async resolve({ ctx }) { - return "You are logged in and can see this secret message!"; - }, - }); diff --git a/template/addons/trpc/protected-example-router.ts b/template/addons/trpc/protected-example-router.ts new file mode 100644 index 0000000000..64e007b8ab --- /dev/null +++ b/template/addons/trpc/protected-example-router.ts @@ -0,0 +1,14 @@ +import { createProtectedRouter } from "./protected-router"; + +// Example router with queries that can only be hit if the user requesting is signed in +export const protectedExampleRouter = createProtectedRouter() + .query("getSession", { + resolve({ ctx }) { + return ctx.session; + }, + }) + .query("getSecretMessage", { + resolve({ ctx }) { + return "He who asks a question is a fool for five minutes; he who does not ask a question remains a fool forever."; + }, + }); diff --git a/template/addons/trpc/protected-router.ts b/template/addons/trpc/protected-router.ts new file mode 100644 index 0000000000..19dbcdebd9 --- /dev/null +++ b/template/addons/trpc/protected-router.ts @@ -0,0 +1,20 @@ +import * as trpc from "@trpc/server"; +import { createRouter } from "./context"; + +/** + * Creates a tRPC router that asserts all queries and mutations are from an authorized user. Will throw an unauthorized error if a user is not signed in. + */ +export function createProtectedRouter() { + return createRouter().middleware(({ ctx, next }) => { + if (!ctx.session || !ctx.session.user) { + throw new trpc.TRPCError({ code: "UNAUTHORIZED" }); + } + return next({ + ctx: { + ...ctx, + // infers that `session` is non-nullable to downstream resolvers + session: { ...ctx.session, user: ctx.session.user }, + }, + }); + }); +}