Skip to content

Commit 37f6f2e

Browse files
committed
wip
1 parent 0a0608b commit 37f6f2e

File tree

11 files changed

+344
-1
lines changed

11 files changed

+344
-1
lines changed

apps/worxpace/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"dependencies": {
1717
"@acme/i18n": "workspace:*",
1818
"@acme/prisma": "workspace:*",
19+
"@acme/trpc": "workspace:*",
1920
"@acme/ui": "workspace:*",
2021
"@acme/validators": "workspace:*",
2122
"@blocknote/core": "^0.15.0",
@@ -30,6 +31,7 @@
3031
"@liveblocks/react": "^1.12.0",
3132
"@liveblocks/yjs": "^1.12.0",
3233
"@t3-oss/env-nextjs": "^0.10.1",
34+
"@trpc/server": "next",
3335
"lucide-react": "^0.408.0",
3436
"next": "^14.2.3",
3537
"react": "18.3.1",
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { appRouter, createTRPCContext } from "@acme/trpc";
2+
import * as trpcNext from '@trpc/server/adapters/next'
3+
4+
export const runtime = "edge";
5+
6+
/**
7+
* Configure basic CORS headers
8+
* You should extend this to match your needs
9+
*/
10+
const setCorsHeaders = (res: Response) => {
11+
res.headers.set("Access-Control-Allow-Origin", "*");
12+
res.headers.set("Access-Control-Request-Method", "*");
13+
res.headers.set("Access-Control-Allow-Methods", "OPTIONS, GET, POST");
14+
res.headers.set("Access-Control-Allow-Headers", "*");
15+
};
16+
17+
export const OPTIONS = () => {
18+
const response = new Response(null, {
19+
status: 204,
20+
});
21+
setCorsHeaders(response);
22+
return response;
23+
};
24+
25+
const handler = trpcNext.createNextApiHandler( {
26+
router: appRouter,
27+
createContext: createTRPCContext,
28+
onError({ error, path }) {
29+
console.error(`>>> tRPC Error on '${path}'`, error);
30+
},
31+
})
32+
33+
// const handler = auth(async (req) => {
34+
// const response = await fetchRequestHandler({
35+
// endpoint: "/api/trpc",
36+
// router: appRouter,
37+
// req,
38+
// createContext: () =>
39+
// createTRPCContext({
40+
// session: req.auth,
41+
// headers: req.headers,
42+
// }),
43+
// onError({ error, path }) {
44+
// console.error(`>>> tRPC Error on '${path}'`, error);
45+
// },
46+
// });
47+
48+
// setCorsHeaders(response);
49+
// return response;
50+
// });
51+
52+
export { handler as GET, handler as POST };

apps/worxpace/src/hooks/use-documents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
const fetcher = async ({ workspaceId }: DocumentsKey) => {
2525
try {
2626
return await fetchUrl<DetailedDocument[]>(
27-
`/api/documents?workspaceId=${workspaceId}`,
27+
`/api/trpc/documents?workspaceId=${workspaceId}`,
2828
);
2929
} catch (error) {
3030
console.log(error);

packages/trpc/package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "@acme/trpc",
3+
"version": "1.0.3",
4+
"private": true,
5+
"type": "module",
6+
"exports": {
7+
".": "./src/index.ts"
8+
},
9+
"license": "MIT",
10+
"scripts": {
11+
"clean": "rm -rf .turbo node_modules",
12+
"format": "prettier --check . --ignore-path ../../.gitignore",
13+
"lint": "eslint .",
14+
"typecheck": "tsc --noEmit"
15+
},
16+
"dependencies": {
17+
"@acme/prisma": "workspace:*",
18+
"@acme/validators": "workspace:*",
19+
"@clerk/nextjs": "^5.2.12",
20+
"@t3-oss/env-nextjs": "^0.10.1",
21+
"@trpc/client": "next",
22+
"@trpc/server": "next",
23+
"superjson": "2.2.1",
24+
"zod": "^3.23.7"
25+
},
26+
"devDependencies": {
27+
"@acme/eslint-config": "workspace:*",
28+
"@acme/prettier-config": "workspace:*",
29+
"@acme/tsconfig": "workspace:*",
30+
"eslint": "^8.56.0",
31+
"prettier": "^3.2.5",
32+
"typescript": "^5.5.4"
33+
},
34+
"eslintConfig": {
35+
"root": true,
36+
"extends": [
37+
"@acme/eslint-config/base"
38+
]
39+
},
40+
"prettier": "@acme/prettier-config"
41+
}

packages/trpc/src/index.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
2+
3+
import type { AppRouter } from "./root";
4+
import { appRouter } from "./root";
5+
import { createCallerFactory, createTRPCContext } from "./trpc";
6+
7+
/**
8+
* Create a server-side caller for the tRPC API
9+
* @example
10+
* const trpc = createCaller(createContext);
11+
* const res = await trpc.post.all();
12+
* ^? Post[]
13+
*/
14+
const createCaller = createCallerFactory(appRouter);
15+
16+
/**
17+
* Inference helpers for input types
18+
* @example
19+
* type PostByIdInput = RouterInputs['post']['byId']
20+
* ^? { id: number }
21+
**/
22+
type RouterInputs = inferRouterInputs<AppRouter>;
23+
24+
/**
25+
* Inference helpers for output types
26+
* @example
27+
* type AllPostsOutput = RouterOutputs['post']['all']
28+
* ^? Post[]
29+
**/
30+
type RouterOutputs = inferRouterOutputs<AppRouter>;
31+
32+
export { createTRPCContext, appRouter, createCaller };
33+
export type { AppRouter, RouterInputs, RouterOutputs };

packages/trpc/src/root.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { authRouter } from "./router/auth";
2+
import { documentRouter } from "./router/document";
3+
import { createTRPCRouter } from "./trpc";
4+
5+
export const appRouter = createTRPCRouter({
6+
auth: authRouter,
7+
document: documentRouter,
8+
});
9+
10+
// export type definition of API
11+
export type AppRouter = typeof appRouter;

packages/trpc/src/router/auth.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {currentUser} from "@clerk/nextjs/server"
2+
3+
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
4+
5+
export const authRouter = createTRPCRouter({
6+
getAuth: publicProcedure.query(({ ctx }) => {
7+
return ctx.auth;
8+
}),
9+
getUser: publicProcedure.query(() => {
10+
return currentUser()
11+
}),
12+
getSecretMessage: protectedProcedure.query(() => {
13+
// testing type validation of overridden next-auth Session in @acme/auth package
14+
return "you can see this secret message!";
15+
}),
16+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { z } from "zod";
2+
3+
4+
import { createTRPCRouter, protectedProcedure } from "../trpc";
5+
6+
const include = { workspace: true, createdBy: true, updatedBy: true }
7+
8+
export const documentRouter = createTRPCRouter({
9+
all: protectedProcedure
10+
.input(z.object({ workspaceId: z.string(), isArchived: z.boolean().optional() }))
11+
.query(({ ctx, input }) => {
12+
return ctx.db.document.findMany({
13+
where: input,
14+
orderBy: { createdAt: "desc" },
15+
include,
16+
})
17+
}),
18+
19+
byId: protectedProcedure
20+
.input(z.string())
21+
.query(({ ctx, input }) => {
22+
return ctx.db.document.findUnique({ where: { id: input }, include });
23+
}),
24+
});

packages/trpc/src/trpc.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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.
7+
* The pieces you will need to use are documented accordingly near the end
8+
*/
9+
import { initTRPC, TRPCError } from "@trpc/server";
10+
import superjson from "superjson";
11+
import { ZodError } from "zod";
12+
13+
import { getAuth } from "@clerk/nextjs/server";
14+
import {worxpace} from "@acme/prisma"
15+
// import { NextApiRequest } from "@trpc/server/adapters/next";
16+
import * as trpcNext from '@trpc/server/adapters/next'
17+
18+
/**
19+
* 1. CONTEXT
20+
*
21+
* This section defines the "contexts" that are available in the backend API.
22+
*
23+
* These allow you to access things when processing a request, like the database, the session, etc.
24+
*
25+
* This helper generates the "internals" for a tRPC context. The API handler and RSC clients each
26+
* wrap this and provides the required context.
27+
*
28+
* @see https://trpc.io/docs/server/context
29+
*/
30+
export const createTRPCContext = async (opts: trpcNext.CreateNextContextOptions) => {
31+
const auth = getAuth(opts.req)
32+
// const source = opts.headers.get("x-trpc-source") ?? "unknown";
33+
// console.log(">>> tRPC Request from", source, "by", auth?.userId);
34+
35+
return {
36+
auth,
37+
db: worxpace,
38+
};
39+
};
40+
41+
/**
42+
* 2. INITIALIZATION
43+
*
44+
* This is where the trpc api is initialized, connecting the context and
45+
* transformer
46+
*/
47+
const t = initTRPC.context<typeof createTRPCContext>().create({
48+
transformer: superjson,
49+
errorFormatter: ({ shape, error }) => ({
50+
...shape,
51+
data: {
52+
...shape.data,
53+
zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
54+
},
55+
}),
56+
});
57+
58+
/**
59+
* Create a server-side caller
60+
* @see https://trpc.io/docs/server/server-side-calls
61+
*/
62+
export const createCallerFactory = t.createCallerFactory;
63+
64+
/**
65+
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
66+
*
67+
* These are the pieces you use to build your tRPC API. You should import these
68+
* a lot in the /src/server/api/routers folder
69+
*/
70+
71+
/**
72+
* This is how you create new routers and subrouters in your tRPC API
73+
* @see https://trpc.io/docs/router
74+
*/
75+
export const createTRPCRouter = t.router;
76+
77+
/**
78+
* Public (unauthed) procedure
79+
*
80+
* This is the base piece you use to build new queries and mutations on your
81+
* tRPC API. It does not guarantee that a user querying is authorized, but you
82+
* can still access user session data if they are logged in
83+
*/
84+
export const publicProcedure = t.procedure;
85+
86+
/**
87+
* Protected (authenticated) procedure
88+
*
89+
* If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies
90+
* the `AuthObject` is valid and guarantees `ctx.auth.userId` is not null.
91+
*
92+
* @see https://trpc.io/docs/procedures
93+
*/
94+
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
95+
if (!ctx.auth.userId) {
96+
throw new TRPCError({ code: "UNAUTHORIZED" });
97+
}
98+
return next({
99+
ctx: {
100+
// infers the `auth` as non-nullable
101+
auth: ctx.auth,
102+
},
103+
});
104+
});

packages/trpc/tsconfig.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "@acme/tsconfig/base.json",
3+
"compilerOptions": {
4+
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
5+
},
6+
"include": ["src"],
7+
"exclude": ["node_modules"]
8+
}

0 commit comments

Comments
 (0)