7 | );
8 |
9 | export default SignUpPage;
10 |
11 | const styles = {
12 | width: "100vw",
13 | height: "100vh",
14 | display: "flex",
15 | justifyContent: "center",
16 | alignItems: "center",
17 | };
18 |
--------------------------------------------------------------------------------
/src/server/api/root.ts:
--------------------------------------------------------------------------------
1 | import { createTRPCRouter } from "./trpc";
2 | import { exampleRouter } from "./routers/example";
3 |
4 | /**
5 | * This is the primary router for your server.
6 | *
7 | * All routers added in /api/routers should be manually added here
8 | */
9 | export const appRouter = createTRPCRouter({
10 | example: exampleRouter,
11 | });
12 |
13 | // export type definition of API
14 | export type AppRouter = typeof appRouter;
15 |
--------------------------------------------------------------------------------
/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { type AppType } from "next/app";
2 | import {
3 | ClerkProvider,
4 | } from "@clerk/nextjs";
5 | import { api } from "../utils/api";
6 |
7 | import "../styles/globals.css";
8 |
9 | const MyApp: AppType = ({ Component, pageProps }) => {
10 |
11 | return (
12 |
13 |
14 |
15 | );
16 | };
17 |
18 | export default api.withTRPC(MyApp);
19 |
--------------------------------------------------------------------------------
/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | // This is your Prisma schema file,
2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema
3 |
4 | generator client {
5 | provider = "prisma-client-js"
6 | }
7 |
8 | datasource db {
9 | provider = "sqlite"
10 | url = env("DATABASE_URL")
11 | }
12 |
13 | model Example {
14 | id String @id @default(cuid())
15 | createdAt DateTime @default(now())
16 | updatedAt DateTime @updatedAt
17 | }
18 |
--------------------------------------------------------------------------------
/src/server/db.ts:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from "@prisma/client";
2 |
3 | import { env } from "../env/server.mjs";
4 |
5 | const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
6 |
7 | export const prisma =
8 | globalForPrisma.prisma ||
9 | new PrismaClient({
10 | log:
11 | env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
12 | });
13 |
14 | if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
15 |
--------------------------------------------------------------------------------
/src/server/api/routers/example.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
4 |
5 | export const exampleRouter = createTRPCRouter({
6 | hello: publicProcedure
7 | .input(z.object({ text: z.string() }))
8 | .query(({ input }) => {
9 | return {
10 | greeting: `Hello ${input.text}`,
11 | };
12 | }),
13 | getAll: protectedProcedure.query(({ ctx }) => {
14 | return ctx.prisma.example.findMany();
15 | }),
16 | });
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | /**
3 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
4 | * This is especially useful for Docker builds.
5 | */
6 | !process.env.SKIP_ENV_VALIDATION && (await import("./src/env/server.mjs"));
7 |
8 | /** @type {import("next").NextConfig} */
9 | const config = {
10 | reactStrictMode: true,
11 | /* If trying out the experimental appDir, comment the i18n config out
12 | * @see https://github.com/vercel/next.js/issues/41980 */
13 | i18n: {
14 | locales: ["en"],
15 | defaultLocale: "en",
16 | },
17 | };
18 | export default config;
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "noUncheckedIndexedAccess": true
18 | },
19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.mjs"],
20 | "exclude": ["node_modules"]
21 | }
22 |
--------------------------------------------------------------------------------
/src/pages/api/trpc/[trpc].ts:
--------------------------------------------------------------------------------
1 | import { createNextApiHandler } from "@trpc/server/adapters/next";
2 |
3 | import { env } from "../../../env/server.mjs";
4 | import { createTRPCContext } from "../../../server/api/trpc";
5 | import { appRouter } from "../../../server/api/root";
6 |
7 | // export API handler
8 | export default createNextApiHandler({
9 | router: appRouter,
10 | createContext: createTRPCContext,
11 | onError:
12 | env.NODE_ENV === "development"
13 | ? ({ path, error }) => {
14 | console.error(
15 | `❌ tRPC failed on ${path ?? ""}: ${error.message}`,
16 | );
17 | }
18 | : undefined,
19 | });
20 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # Since the ".env" file is gitignored, you can use the ".env.example" file to
2 | # build a new ".env" file when you clone the repo. Keep this file up-to-date
3 | # when you add new variables to `.env`.
4 |
5 | # This file will be committed to version control, so make sure not to have any
6 | # secrets in it. If you are cloning this repo, create a copy of this file named
7 | # ".env" and populate it with your secrets.
8 |
9 | # When adding additional environment variables, the schema in "/env/schema.mjs"
10 | # should be updated accordingly.
11 |
12 | # Prisma
13 | # https://www.prisma.io/docs/reference/database-reference/connection-urls#env
14 | DATABASE_URL="file:./db.sqlite"
15 |
16 | # CLERK Keys
17 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=CLERK_PUBLISHABLE_KEY
18 | CLERK_SECRET_KEY=CLERK_SECRET_KEY
--------------------------------------------------------------------------------
/src/pages/protected.tsx:
--------------------------------------------------------------------------------
1 | import { UserButton } from "@clerk/nextjs";
2 | import { type NextPage } from "next";
3 |
4 | const Protected: NextPage = () => {
5 | return (
6 |
7 |
8 |
9 | Welcome to the{" "}
10 | Protected Page
11 |
12 |
Click this User Button!
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default Protected;
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Clerk T3 Minimal
2 |
3 | The following project uses TRPC + Clerk with minimal styling. The project is a good starting point for using TRPC + Clerk.
4 |
5 | ## Project features
6 |
7 | 1. User Context throughout
8 | 2. Protected and public procedures
9 | 3. Sign-in, Sign-Up and User Profile components
10 |
11 | ## How to use
12 |
13 | 1. Create a [Clerk account](https://dashboard.clerk.dev/sign-up)
14 | 2. Copy `.env.example` and rename to `.env` and add your keys found in the dashboard.
15 | 3. Run `npm install`
16 | 4. npx `npx prisma db push`
17 | 4. Run `npm run dev`
18 |
19 | ## Shoutouts
20 |
21 | - Huge shoutout to the team at [TRPC](https://trpc.io) for making an awesome project.
22 |
23 | - Shoutout to [t3 stack](https://create.t3.gg/) for introducing a lot of people to the TRPC and influencing structure of this project.
24 |
--------------------------------------------------------------------------------
/src/env/server.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | /**
3 | * This file is included in `/next.config.mjs` which ensures the app isn't built with invalid env vars.
4 | * It has to be a `.mjs`-file to be imported there.
5 | */
6 | import { serverSchema, serverEnv } from "./schema.mjs";
7 | import { env as clientEnv, formatErrors } from "./client.mjs";
8 |
9 | const _serverEnv = serverSchema.safeParse(serverEnv);
10 |
11 | if (!_serverEnv.success) {
12 | console.error(
13 | "❌ Invalid environment variables:\n",
14 | ...formatErrors(_serverEnv.error.format()),
15 | );
16 | throw new Error("Invalid environment variables");
17 | }
18 |
19 | for (let key of Object.keys(_serverEnv.data)) {
20 | if (key.startsWith("NEXT_PUBLIC_")) {
21 | console.warn("❌ You are exposing a server-side env-variable:", key);
22 |
23 | throw new Error("You are exposing a server-side env-variable");
24 | }
25 | }
26 |
27 | export const env = { ..._serverEnv.data, ...clientEnv };
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/src/env/client.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { clientEnv, clientSchema } from "./schema.mjs";
3 |
4 | const _clientEnv = clientSchema.safeParse(clientEnv);
5 |
6 | export const formatErrors = (
7 | /** @type {import('zod').ZodFormattedError