From aff946424551bbf3d5060919255196c57ec100c2 Mon Sep 17 00:00:00 2001 From: kenneth Date: Sun, 25 Jan 2026 22:19:05 +0000 Subject: [PATCH] feat(backend): add tRPC with Hono adapter - Add @trpc/server, @hono/trpc-server, zod dependencies - Create tRPC context with BetterAuth session - Create router with publicProcedure and protectedProcedure - Mount tRPC at /trpc/* via Hono adapter Co-authored-by: Ona --- apps/aris-backend/package.json | 5 ++++- apps/aris-backend/src/server.ts | 11 +++++++++++ apps/aris-backend/src/trpc/context.ts | 14 ++++++++++++++ apps/aris-backend/src/trpc/router.ts | 5 +++++ apps/aris-backend/src/trpc/trpc.ts | 22 ++++++++++++++++++++++ bun.lock | 15 ++++++++++++++- 6 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 apps/aris-backend/src/trpc/context.ts create mode 100644 apps/aris-backend/src/trpc/router.ts create mode 100644 apps/aris-backend/src/trpc/trpc.ts diff --git a/apps/aris-backend/package.json b/apps/aris-backend/package.json index 87e03c6..e0702d8 100644 --- a/apps/aris-backend/package.json +++ b/apps/aris-backend/package.json @@ -12,9 +12,12 @@ "@aris/core": "workspace:*", "@aris/source-location": "workspace:*", "@aris/source-weatherkit": "workspace:*", + "@hono/trpc-server": "^0.3", + "@trpc/server": "^11", "better-auth": "^1", "hono": "^4", - "pg": "^8" + "pg": "^8", + "zod": "^3" }, "devDependencies": { "@types/pg": "^8" diff --git a/apps/aris-backend/src/server.ts b/apps/aris-backend/src/server.ts index dd0ed3c..287c8ca 100644 --- a/apps/aris-backend/src/server.ts +++ b/apps/aris-backend/src/server.ts @@ -1,6 +1,9 @@ +import { trpcServer } from "@hono/trpc-server" import { Hono } from "hono" import { registerAuthHandlers } from "./auth/http.ts" +import { createContext } from "./trpc/context.ts" +import { appRouter } from "./trpc/router.ts" const app = new Hono() @@ -8,6 +11,14 @@ app.get("/health", (c) => c.json({ status: "ok" })) registerAuthHandlers(app) +app.use( + "/trpc/*", + trpcServer({ + router: appRouter, + createContext, + }), +) + export default { port: 3000, fetch: app.fetch, diff --git a/apps/aris-backend/src/trpc/context.ts b/apps/aris-backend/src/trpc/context.ts new file mode 100644 index 0000000..d62265c --- /dev/null +++ b/apps/aris-backend/src/trpc/context.ts @@ -0,0 +1,14 @@ +import type { FetchCreateContextFnOptions } from "@trpc/server/adapters/fetch" + +import { auth } from "../auth/index.ts" + +export async function createContext(opts: FetchCreateContextFnOptions) { + const session = await auth.api.getSession({ headers: opts.req.headers }) + + return { + user: session?.user ?? null, + session: session?.session ?? null, + } +} + +export type Context = Awaited> diff --git a/apps/aris-backend/src/trpc/router.ts b/apps/aris-backend/src/trpc/router.ts new file mode 100644 index 0000000..2b99062 --- /dev/null +++ b/apps/aris-backend/src/trpc/router.ts @@ -0,0 +1,5 @@ +import { router } from "./trpc.ts" + +export const appRouter = router({}) + +export type AppRouter = typeof appRouter diff --git a/apps/aris-backend/src/trpc/trpc.ts b/apps/aris-backend/src/trpc/trpc.ts new file mode 100644 index 0000000..368c742 --- /dev/null +++ b/apps/aris-backend/src/trpc/trpc.ts @@ -0,0 +1,22 @@ +import { initTRPC, TRPCError } from "@trpc/server" + +import type { Context } from "./context.ts" + +const t = initTRPC.context().create() + +export const router = t.router +export const publicProcedure = t.procedure + +const isAuthed = t.middleware(({ ctx, next }) => { + if (!ctx.user || !ctx.session) { + throw new TRPCError({ code: "UNAUTHORIZED" }) + } + return next({ + ctx: { + user: ctx.user, + session: ctx.session, + }, + }) +}) + +export const protectedProcedure = t.procedure.use(isAuthed) diff --git a/bun.lock b/bun.lock index 16d2d02..78740e9 100644 --- a/bun.lock +++ b/bun.lock @@ -20,9 +20,12 @@ "@aris/core": "workspace:*", "@aris/source-location": "workspace:*", "@aris/source-weatherkit": "workspace:*", + "@hono/trpc-server": "^0.3", + "@trpc/server": "^11", "better-auth": "^1", "hono": "^4", "pg": "^8", + "zod": "^3", }, "devDependencies": { "@types/pg": "^8", @@ -91,6 +94,8 @@ "@better-fetch/fetch": ["@better-fetch/fetch@1.1.21", "", {}, "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="], + "@hono/trpc-server": ["@hono/trpc-server@0.3.4", "", { "peerDependencies": { "@trpc/server": "^10.10.0 || >11.0.0-rc", "hono": ">=4.*" } }, "sha512-xFOPjUPnII70FgicDzOJy1ufIoBTu8eF578zGiDOrYOrYN8CJe140s9buzuPkX+SwJRYK8LjEBHywqZtxdm8aA=="], + "@noble/ciphers": ["@noble/ciphers@2.1.1", "", {}, "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw=="], "@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="], @@ -129,6 +134,8 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@trpc/server": ["@trpc/server@11.8.1", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-P4rzZRpEL7zDFgjxK65IdyH0e41FMFfTkQkuq0BA5tKcr7E6v9/v38DEklCpoDN6sPiB1Sigy/PUEzHENhswDA=="], + "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="], "@types/node": ["@types/node@25.0.9", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw=="], @@ -197,6 +204,12 @@ "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@better-auth/core/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "better-auth/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "better-call/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], } }