Skip to content

Commit bc15ece

Browse files
feat: update trpc to v10 (@next) (#203)
* feat: modify installer and bp * feat: update frontend * fix: update import * fix: add protectedprocedure
1 parent cef7027 commit bc15ece

21 files changed

+234
-285
lines changed

src/installers/trpc.ts

+21-12
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ export const trpcInstaller: Installer = async ({
1616
packages: [
1717
"react-query",
1818
"superjson",
19-
"@trpc/server",
20-
"@trpc/client",
21-
"@trpc/next",
22-
"@trpc/react",
19+
"@trpc/server@experimental",
20+
"@trpc/client@experimental",
21+
"@trpc/next@experimental",
22+
"@trpc/react@experimental",
2323
],
2424
devMode: false,
2525
noInstallMode: noInstall,
@@ -35,6 +35,10 @@ export const trpcInstaller: Installer = async ({
3535
const utilsSrc = path.join(trpcAssetDir, "utils.ts");
3636
const utilsDest = path.join(projectDir, "src/utils/trpc.ts");
3737

38+
const serverUtilFile = usingAuth ? "auth-server-utils.ts" : "server-utils.ts";
39+
const serverUtilSrc = path.join(trpcAssetDir, serverUtilFile);
40+
const serverUtilDest = path.join(projectDir, "src/server/trpc/utils.ts");
41+
3842
const contextFile =
3943
usingAuth && usingPrisma
4044
? "auth-prisma-context.ts"
@@ -44,34 +48,39 @@ export const trpcInstaller: Installer = async ({
4448
? "prisma-context.ts"
4549
: "base-context.ts";
4650
const contextSrc = path.join(trpcAssetDir, contextFile);
47-
const contextDest = path.join(projectDir, "src/server/router/context.ts");
51+
const contextDest = path.join(projectDir, "src/server/trpc/context.ts");
4852

49-
if (usingAuth) {
50-
const authRouterSrc = path.join(trpcAssetDir, "auth-router.ts");
51-
const authRouterDest = path.join(projectDir, "src/server/router/auth.ts");
52-
await fs.copy(authRouterSrc, authRouterDest);
53-
}
53+
const authRouterSrc = path.join(trpcAssetDir, "auth-router.ts");
54+
const authRouterDest = path.join(
55+
projectDir,
56+
"src/server/trpc/router/auth.ts",
57+
);
5458

5559
const indexRouterFile = usingAuth
5660
? "auth-index-router.ts"
5761
: "index-router.ts";
5862
const indexRouterSrc = path.join(trpcAssetDir, indexRouterFile);
59-
const indexRouterDest = path.join(projectDir, "src/server/router/index.ts");
63+
const indexRouterDest = path.join(
64+
projectDir,
65+
"src/server/trpc/router/index.ts",
66+
);
6067

6168
const exampleRouterFile = usingPrisma
6269
? "example-prisma-router.ts"
6370
: "example-router.ts";
6471
const exampleRouterSrc = path.join(trpcAssetDir, exampleRouterFile);
6572
const exampleRouterDest = path.join(
6673
projectDir,
67-
"src/server/router/example.ts",
74+
"src/server/trpc/router/example.ts",
6875
);
6976

7077
await Promise.all([
7178
fs.copy(apiHandlerSrc, apiHandlerDest),
7279
fs.copy(utilsSrc, utilsDest),
80+
fs.copy(serverUtilSrc, serverUtilDest),
7381
fs.copy(contextSrc, contextDest),
7482
fs.copy(indexRouterSrc, indexRouterDest),
7583
fs.copy(exampleRouterSrc, exampleRouterDest),
84+
...(usingAuth ? [fs.copy(authRouterSrc, authRouterDest)] : []),
7685
]);
7786
};

template/addons/trpc/api-handler.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// src/pages/api/trpc/[trpc].ts
22
import { createNextApiHandler } from "@trpc/server/adapters/next";
3-
import { appRouter } from "../../../server/router";
4-
import { createContext } from "../../../server/router/context";
3+
import { appRouter } from "../../../server/trpc/router";
4+
import { createContext } from "../../../server/trpc/context";
55

66
// export API handler
77
export default createNextApiHandler({

template/addons/trpc/auth-context.ts

+4-12
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,18 @@
1-
// src/server/router/context.ts
1+
// src/server/trpc/context.ts
22
import * as trpc from "@trpc/server";
33
import * as trpcNext from "@trpc/server/adapters/next";
44
import { unstable_getServerSession as getServerSession } from "next-auth";
55

66
import { authOptions as nextAuthOptions } from "../../pages/api/auth/[...nextauth]";
77

88
export const createContext = async (
9-
opts?: trpcNext.CreateNextContextOptions,
9+
opts: trpcNext.CreateNextContextOptions,
1010
) => {
11-
const req = opts?.req;
12-
const res = opts?.res;
13-
14-
const session =
15-
req && res && (await getServerSession(req, res, nextAuthOptions));
11+
const session = await getServerSession(opts.req, opts.res, nextAuthOptions);
1612

1713
return {
18-
req,
19-
res,
2014
session,
2115
};
2216
};
2317

24-
type Context = trpc.inferAsyncReturnType<typeof createContext>;
25-
26-
export const createRouter = () => trpc.router<Context>();
18+
export type Context = trpc.inferAsyncReturnType<typeof createContext>;
+6-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
// src/server/router/index.ts
2-
import { createRouter } from "./context";
3-
import superjson from "superjson";
4-
1+
// src/server/trpc/router/index.ts
2+
import { t } from "../utils";
53
import { exampleRouter } from "./example";
64
import { authRouter } from "./auth";
75

8-
export const appRouter = createRouter()
9-
.transformer(superjson)
10-
.merge("example.", exampleRouter)
11-
.merge("auth.", authRouter);
6+
export const appRouter = t.router({
7+
example: exampleRouter,
8+
auth: authRouter,
9+
});
1210

1311
// export type definition of API
1412
export type AppRouter = typeof appRouter;
+4-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// src/server/router/context.ts
1+
// src/server/trpc/context.ts
22
import * as trpc from "@trpc/server";
33
import * as trpcNext from "@trpc/server/adapters/next";
44
import { unstable_getServerSession as getServerSession } from "next-auth";
@@ -7,22 +7,14 @@ import { authOptions as nextAuthOptions } from "../../pages/api/auth/[...nextaut
77
import { prisma } from "../db/client";
88

99
export const createContext = async (
10-
opts?: trpcNext.CreateNextContextOptions,
10+
opts: trpcNext.CreateNextContextOptions,
1111
) => {
12-
const req = opts?.req;
13-
const res = opts?.res;
14-
15-
const session =
16-
req && res && (await getServerSession(req, res, nextAuthOptions));
12+
const session = await getServerSession(opts.req, opts.res, nextAuthOptions);
1713

1814
return {
19-
req,
20-
res,
2115
session,
2216
prisma,
2317
};
2418
};
2519

26-
type Context = trpc.inferAsyncReturnType<typeof createContext>;
27-
28-
export const createRouter = () => trpc.router<Context>();
20+
export type Context = trpc.inferAsyncReturnType<typeof createContext>;

template/addons/trpc/auth-router.ts

+9-21
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,10 @@
1-
import { TRPCError } from "@trpc/server";
2-
import { createRouter } from "./context";
1+
import { t, authedProcedure } from "../utils";
32

4-
export const authRouter = createRouter()
5-
.query("getSession", {
6-
resolve({ ctx }) {
7-
return ctx.session;
8-
},
9-
})
10-
.middleware(async ({ ctx, next }) => {
11-
// Any queries or mutations after this middleware will
12-
// raise an error unless there is a current session
13-
if (!ctx.session) {
14-
throw new TRPCError({ code: "UNAUTHORIZED" });
15-
}
16-
return next();
17-
})
18-
.query("getSecretMessage", {
19-
async resolve({ ctx }) {
20-
return "You are logged in and can see this secret message!";
21-
},
22-
});
3+
export const authRouter = t.router({
4+
getSession: t.procedure.query(({ ctx }) => {
5+
return ctx.session;
6+
}),
7+
getSecretMessage: authedProcedure.query(() => {
8+
return "You are logged in and can see this secret message!";
9+
}),
10+
});
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { initTRPC, TRPCError } from "@trpc/server";
2+
import type { Context } from "./context";
3+
import superjson from "superjson";
4+
5+
export const t = initTRPC<{ ctx: Context }>()({
6+
transformer: superjson,
7+
errorFormatter({ shape }) {
8+
return shape;
9+
},
10+
});
11+
12+
export const authedProcedure = t.procedure.use(({ ctx, next }) => {
13+
if (!ctx.session || !ctx.session.user) {
14+
throw new TRPCError({ code: "UNAUTHORIZED" });
15+
}
16+
return next({
17+
ctx: {
18+
...ctx,
19+
// infers that `session` is non-nullable to downstream resolvers
20+
session: { ...ctx.session, user: ctx.session.user },
21+
},
22+
});
23+
});

template/addons/trpc/base-context.ts

+3-11
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,8 @@
22
import * as trpc from "@trpc/server";
33
import * as trpcNext from "@trpc/server/adapters/next";
44

5-
export const createContext = (opts?: trpcNext.CreateNextContextOptions) => {
6-
const req = opts?.req;
7-
const res = opts?.res;
8-
9-
return {
10-
req,
11-
res,
12-
};
5+
export const createContext = (opts: trpcNext.CreateNextContextOptions) => {
6+
return {};
137
};
148

15-
type Context = trpc.inferAsyncReturnType<typeof createContext>;
16-
17-
export const createRouter = () => trpc.router<Context>();
9+
export type Context = trpc.inferAsyncReturnType<typeof createContext>;
+10-16
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
1-
import { createRouter } from "./context";
1+
import { t } from "../utils";
22
import { z } from "zod";
33

4-
export const exampleRouter = createRouter()
5-
.query("hello", {
6-
input: z
7-
.object({
8-
text: z.string().nullish(),
9-
})
10-
.nullish(),
11-
resolve({ input }) {
4+
export const exampleRouter = t.router({
5+
hello: t.procedure
6+
.input(z.object({ text: z.string().nullish() }).nullish())
7+
.query(({ input }) => {
128
return {
139
greeting: `Hello ${input?.text ?? "world"}`,
1410
};
15-
},
16-
})
17-
.query("getAll", {
18-
async resolve({ ctx }) {
19-
return await ctx.prisma.example.findMany();
20-
},
21-
});
11+
}),
12+
getAll: t.procedure.query(({ ctx }) => {
13+
return ctx.prisma.example.findMany();
14+
}),
15+
});
+9-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
import { createRouter } from "./context";
1+
import { t } from "../utils";
22
import { z } from "zod";
33

4-
export const exampleRouter = createRouter().query("hello", {
5-
input: z
6-
.object({
7-
text: z.string().nullish(),
8-
})
9-
.nullish(),
10-
resolve({ input }) {
11-
return {
12-
greeting: `Hello ${input?.text ?? "world"}`,
13-
};
14-
},
4+
export const exampleRouter = t.router({
5+
hello: t.procedure
6+
.input(z.object({ text: z.string().nullish() }).nullish())
7+
.query(({ input }) => {
8+
return {
9+
greeting: `Hello ${input?.text ?? "world"}`,
10+
};
11+
}),
1512
});

template/addons/trpc/index-router.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
// src/server/router/index.ts
2-
import { createRouter } from "./context";
3-
import superjson from "superjson";
2+
import { t } from "../utils";
43

54
import { exampleRouter } from "./example";
65

7-
export const appRouter = createRouter()
8-
.transformer(superjson)
9-
.merge("example.", exampleRouter);
6+
export const appRouter = t.router({
7+
example: exampleRouter,
8+
});
109

1110
// export type definition of API
1211
export type AppRouter = typeof appRouter;

template/addons/trpc/prisma-context.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,10 @@ import * as trpc from "@trpc/server";
33
import * as trpcNext from "@trpc/server/adapters/next";
44
import { prisma } from "../db/client";
55

6-
export const createContext = (opts?: trpcNext.CreateNextContextOptions) => {
7-
const req = opts?.req;
8-
const res = opts?.res;
9-
6+
export const createContext = (opts: trpcNext.CreateNextContextOptions) => {
107
return {
11-
req,
12-
res,
138
prisma,
149
};
1510
};
1611

17-
type Context = trpc.inferAsyncReturnType<typeof createContext>;
18-
19-
export const createRouter = () => trpc.router<Context>();
12+
export type Context = trpc.inferAsyncReturnType<typeof createContext>;

template/addons/trpc/server-utils.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { initTRPC } from "@trpc/server";
2+
import type { Context } from "./context";
3+
import superjson from "superjson";
4+
5+
export const t = initTRPC<{ ctx: Context }>()({
6+
transformer: superjson,
7+
errorFormatter({ shape }) {
8+
return shape;
9+
},
10+
});

template/addons/trpc/utils.ts

+37-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,42 @@
11
// src/utils/trpc.ts
2-
import type { AppRouter } from "../server/router";
3-
import { createReactQueryHooks } from "@trpc/react";
2+
import { setupTRPC } from "@trpc/next";
3+
import type { inferProcedureInput, inferProcedureOutput } from "@trpc/server";
4+
import type { AppRouter } from "../server/trpc/router";
5+
import superjson from "superjson";
46

5-
export const trpc = createReactQueryHooks<AppRouter>();
7+
const getBaseUrl = () => {
8+
if (typeof window !== "undefined") return ""; // browser should use relative url
9+
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
10+
11+
return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
12+
};
13+
14+
export const trpc = setupTRPC<AppRouter>({
15+
config() {
16+
return {
17+
url: `${getBaseUrl()}/api/trpc`,
18+
transformer: superjson,
19+
};
20+
},
21+
ssr: false,
22+
});
623

724
/**
8-
* Check out tRPC docs for Inference Helpers
9-
* https://trpc.io/docs/infer-types#inference-helpers
25+
* This is a helper method to infer the output of a query resolver
26+
* @example type HelloOutput = inferQueryOutput<'hello'>
1027
*/
28+
export type inferQueryOutput<
29+
TRouteKey extends keyof AppRouter["_def"]["queries"],
30+
> = inferProcedureOutput<AppRouter["_def"]["queries"][TRouteKey]>;
31+
32+
export type inferQueryInput<
33+
TRouteKey extends keyof AppRouter["_def"]["queries"],
34+
> = inferProcedureInput<AppRouter["_def"]["queries"][TRouteKey]>;
35+
36+
export type inferMutationOutput<
37+
TRouteKey extends keyof AppRouter["_def"]["mutations"],
38+
> = inferProcedureOutput<AppRouter["_def"]["mutations"][TRouteKey]>;
39+
40+
export type inferMutationInput<
41+
TRouteKey extends keyof AppRouter["_def"]["mutations"],
42+
> = inferProcedureInput<AppRouter["_def"]["mutations"][TRouteKey]>;

0 commit comments

Comments
 (0)