├── README.md ├── public └── favicon.ico ├── src ├── styles │ └── globals.css ├── pages │ ├── _app.tsx │ ├── api │ │ └── trpc │ │ │ └── [trpc].ts │ └── index.tsx ├── server │ ├── api │ │ ├── root.ts │ │ ├── routers │ │ │ └── example.ts │ │ └── trpc.ts │ └── db.ts ├── env.mjs └── utils │ └── api.ts ├── postcss.config.cjs ├── prisma ├── migrations │ ├── migration_lock.toml │ └── 20230702122908_init │ │ └── migration.sql └── schema.prisma ├── prettier.config.cjs ├── tailwind.config.ts ├── docker-compose.yml ├── next.config.mjs ├── .env.example ├── .gitignore ├── tsconfig.json ├── .eslintrc.cjs └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # Create T3 App 2 | 3 | Check the video: 4 | 5 | https://youtu.be/Gf9RkaHnsR8 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FrancescoXX/tRPC-api/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | 8 | module.exports = config; 9 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("prettier").Config} */ 2 | const config = { 3 | plugins: [require.resolve("prettier-plugin-tailwindcss")], 4 | }; 5 | 6 | module.exports = config; 7 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import { type Config } from "tailwindcss"; 2 | 3 | export default { 4 | content: ["./src/**/*.{js,ts,jsx,tsx}"], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } satisfies Config; 10 | -------------------------------------------------------------------------------- /prisma/migrations/20230702122908_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "User" ( 3 | "id" TEXT NOT NULL, 4 | "name" TEXT NOT NULL, 5 | "email" TEXT NOT NULL, 6 | 7 | CONSTRAINT "User_pkey" PRIMARY KEY ("id") 8 | ); 9 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { type AppType } from "next/app"; 2 | import { api } from "~/utils/api"; 3 | import "~/styles/globals.css"; 4 | 5 | const MyApp: AppType = ({ Component, pageProps }) => { 6 | return ; 7 | }; 8 | 9 | export default api.withTRPC(MyApp); 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | services: 4 | db: 5 | container_name: db 6 | image: postgres:12 7 | ports: 8 | - "5432:5432" 9 | environment: 10 | POSTGRES_USER: postgres 11 | POSTGRES_PASSWORD: postgres 12 | POSTGRES_DB: postgres 13 | volumes: 14 | - pgdata:/var/lib/postgresql/data 15 | 16 | volumes: 17 | pgdata: {} -------------------------------------------------------------------------------- /src/server/api/root.ts: -------------------------------------------------------------------------------- 1 | import { exampleRouter } from "~/server/api/routers/example"; 2 | import { createTRPCRouter } from "~/server/api/trpc"; 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/server/db.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | import { env } from "~/env.mjs"; 3 | 4 | const globalForPrisma = globalThis as unknown as { 5 | prisma: PrismaClient | undefined; 6 | }; 7 | 8 | export const prisma = 9 | globalForPrisma.prisma ?? 10 | new PrismaClient({ 11 | log: 12 | env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"], 13 | }); 14 | 15 | if (env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; 16 | -------------------------------------------------------------------------------- /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 | previewFeatures = ["jsonProtocol"] 7 | } 8 | 9 | datasource db { 10 | provider = "postgresql" 11 | url = env("DATABASE_URL") 12 | } 13 | 14 | //User with an id, name and email as strings 15 | model User { 16 | id String @id @default(uuid()) 17 | name String 18 | email String 19 | } -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful 3 | * for Docker builds. 4 | */ 5 | await import("./src/env.mjs"); 6 | 7 | /** @type {import("next").NextConfig} */ 8 | const config = { 9 | reactStrictMode: true, 10 | 11 | /** 12 | * If you have `experimental: { appDir: true }` set, then you must comment the below `i18n` config 13 | * out. 14 | * 15 | * @see https://github.com/vercel/next.js/issues/41980 16 | */ 17 | i18n: { 18 | locales: ["en"], 19 | defaultLocale: "en", 20 | }, 21 | }; 22 | 23 | export default config; 24 | -------------------------------------------------------------------------------- /src/pages/api/trpc/[trpc].ts: -------------------------------------------------------------------------------- 1 | import { createNextApiHandler } from "@trpc/server/adapters/next"; 2 | import { env } from "~/env.mjs"; 3 | import { appRouter } from "~/server/api/root"; 4 | import { createTRPCContext } from "~/server/api/trpc"; 5 | 6 | // export API handler 7 | export default createNextApiHandler({ 8 | router: appRouter, 9 | createContext: createTRPCContext, 10 | onError: 11 | env.NODE_ENV === "development" 12 | ? ({ path, error }) => { 13 | console.error( 14 | `❌ tRPC failed on ${path ?? ""}: ${error.message}` 15 | ); 16 | } 17 | : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /.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 "/src/env.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 | -------------------------------------------------------------------------------- /.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 | # database 12 | /prisma/db.sqlite 13 | /prisma/db.sqlite-journal 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | next-env.d.ts 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # local env files 34 | # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables 35 | .env 36 | .env*.local 37 | 38 | # vercel 39 | .vercel 40 | 41 | # typescript 42 | *.tsbuildinfo 43 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "checkJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "noUncheckedIndexedAccess": true, 19 | "baseUrl": ".", 20 | "paths": { 21 | "~/*": ["./src/*"] 22 | } 23 | }, 24 | "include": [ 25 | ".eslintrc.cjs", 26 | "next-env.d.ts", 27 | "**/*.ts", 28 | "**/*.tsx", 29 | "**/*.cjs", 30 | "**/*.mjs" 31 | ], 32 | "exclude": ["node_modules"] 33 | } 34 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const path = require("path"); 3 | 4 | /** @type {import("eslint").Linter.Config} */ 5 | const config = { 6 | overrides: [ 7 | { 8 | extends: [ 9 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 10 | ], 11 | files: ["*.ts", "*.tsx"], 12 | parserOptions: { 13 | project: path.join(__dirname, "tsconfig.json"), 14 | }, 15 | }, 16 | ], 17 | parser: "@typescript-eslint/parser", 18 | parserOptions: { 19 | project: path.join(__dirname, "tsconfig.json"), 20 | }, 21 | plugins: ["@typescript-eslint"], 22 | extends: ["next/core-web-vitals", "plugin:@typescript-eslint/recommended"], 23 | rules: { 24 | "@typescript-eslint/consistent-type-imports": [ 25 | "warn", 26 | { 27 | prefer: "type-imports", 28 | fixStyle: "inline-type-imports", 29 | }, 30 | ], 31 | "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], 32 | }, 33 | }; 34 | 35 | module.exports = config; 36 | -------------------------------------------------------------------------------- /src/env.mjs: -------------------------------------------------------------------------------- 1 | import { createEnv } from "@t3-oss/env-nextjs"; 2 | import { z } from "zod"; 3 | 4 | export const env = createEnv({ 5 | /** 6 | * Specify your server-side environment variables schema here. This way you can ensure the app 7 | * isn't built with invalid env vars. 8 | */ 9 | server: { 10 | DATABASE_URL: z.string().url(), 11 | NODE_ENV: z.enum(["development", "test", "production"]), 12 | }, 13 | 14 | /** 15 | * Specify your client-side environment variables schema here. This way you can ensure the app 16 | * isn't built with invalid env vars. To expose them to the client, prefix them with 17 | * `NEXT_PUBLIC_`. 18 | */ 19 | client: { 20 | // NEXT_PUBLIC_CLIENTVAR: z.string().min(1), 21 | }, 22 | 23 | /** 24 | * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. 25 | * middlewares) or client-side so we need to destruct manually. 26 | */ 27 | runtimeEnv: { 28 | DATABASE_URL: process.env.DATABASE_URL, 29 | NODE_ENV: process.env.NODE_ENV, 30 | // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, 31 | }, 32 | /** 33 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. 34 | * This is especially useful for Docker builds. 35 | */ 36 | skipValidation: !!process.env.SKIP_ENV_VALIDATION, 37 | }); 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-t3-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "next build", 7 | "dev": "next dev", 8 | "postinstall": "prisma generate", 9 | "lint": "next lint", 10 | "start": "next start" 11 | }, 12 | "dependencies": { 13 | "@prisma/client": "^4.14.0", 14 | "@t3-oss/env-nextjs": "^0.3.1", 15 | "@tanstack/react-query": "^4.29.7", 16 | "@trpc/client": "^10.26.0", 17 | "@trpc/next": "^10.26.0", 18 | "@trpc/react-query": "^10.26.0", 19 | "@trpc/server": "^10.26.0", 20 | "next": "^13.4.2", 21 | "react": "18.2.0", 22 | "react-dom": "18.2.0", 23 | "superjson": "1.12.2", 24 | "zod": "^3.21.4" 25 | }, 26 | "devDependencies": { 27 | "@types/eslint": "^8.37.0", 28 | "@types/node": "^18.16.0", 29 | "@types/prettier": "^2.7.2", 30 | "@types/react": "^18.2.6", 31 | "@types/react-dom": "^18.2.4", 32 | "@typescript-eslint/eslint-plugin": "^5.59.6", 33 | "@typescript-eslint/parser": "^5.59.6", 34 | "autoprefixer": "^10.4.14", 35 | "eslint": "^8.40.0", 36 | "eslint-config-next": "^13.4.2", 37 | "postcss": "^8.4.21", 38 | "prettier": "^2.8.8", 39 | "prettier-plugin-tailwindcss": "^0.2.8", 40 | "prisma": "^4.14.0", 41 | "tailwindcss": "^3.3.0", 42 | "typescript": "^5.0.4" 43 | }, 44 | "ct3aMetadata": { 45 | "initVersion": "7.15.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/server/api/routers/example.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; 3 | 4 | const idSchema = z.object({ id: z.string() }); 5 | 6 | const userSchema = z.object({ 7 | name: z.string(), 8 | email: z.string(), 9 | }); 10 | 11 | const userUpdateSchema = z.object({ 12 | id: z.string(), 13 | name: z.string(), 14 | email: z.string(), 15 | }); 16 | 17 | export const exampleRouter = createTRPCRouter({ 18 | //get all users 19 | getAll: publicProcedure.query(({ ctx }) => { 20 | return ctx.prisma.user.findMany(); 21 | }), 22 | 23 | //get user by id 24 | getOne: publicProcedure 25 | .input(idSchema) 26 | .query(({ input, ctx }) => { 27 | return ctx.prisma.user.findUnique({ 28 | where: idSchema.parse(input), 29 | }); 30 | }), 31 | 32 | //create user 33 | createUser: publicProcedure 34 | .input(userSchema) 35 | .mutation(({ input, ctx }) => { 36 | return ctx.prisma.user.create({ 37 | data: userSchema.parse(input), 38 | }); 39 | }), 40 | 41 | //update user 42 | updateUser: publicProcedure 43 | .input(userUpdateSchema) 44 | .mutation(({ input, ctx }) => { 45 | return ctx.prisma.user.update({ 46 | where: { 47 | id: input.id.toString(), 48 | }, 49 | data: userUpdateSchema.parse(input), 50 | }); 51 | }), 52 | 53 | //delete user 54 | deleteUser: publicProcedure 55 | .input(idSchema) 56 | .mutation(({ input, ctx }) => { 57 | return ctx.prisma.user.delete({ 58 | where: idSchema.parse(input), 59 | }); 60 | }), 61 | }); 62 | -------------------------------------------------------------------------------- /src/utils/api.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the client-side entrypoint for your tRPC API. It is used to create the `api` object which 3 | * contains the Next.js App-wrapper, as well as your type-safe React Query hooks. 4 | * 5 | * We also create a few inference helpers for input and output types. 6 | */ 7 | import { httpBatchLink, loggerLink } from "@trpc/client"; 8 | import { createTRPCNext } from "@trpc/next"; 9 | import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server"; 10 | import superjson from "superjson"; 11 | import { type AppRouter } from "~/server/api/root"; 12 | 13 | const getBaseUrl = () => { 14 | if (typeof window !== "undefined") return ""; // browser should use relative url 15 | if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url 16 | return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost 17 | }; 18 | 19 | /** A set of type-safe react-query hooks for your tRPC API. */ 20 | export const api = createTRPCNext({ 21 | config() { 22 | return { 23 | /** 24 | * Transformer used for data de-serialization from the server. 25 | * 26 | * @see https://trpc.io/docs/data-transformers 27 | */ 28 | transformer: superjson, 29 | 30 | /** 31 | * Links used to determine request flow from client to server. 32 | * 33 | * @see https://trpc.io/docs/links 34 | */ 35 | links: [ 36 | loggerLink({ 37 | enabled: (opts) => 38 | process.env.NODE_ENV === "development" || 39 | (opts.direction === "down" && opts.result instanceof Error), 40 | }), 41 | httpBatchLink({ 42 | url: `${getBaseUrl()}/api/trpc`, 43 | }), 44 | ], 45 | }; 46 | }, 47 | /** 48 | * Whether tRPC should await queries when server rendering pages. 49 | * 50 | * @see https://trpc.io/docs/nextjs#ssr-boolean-default-false 51 | */ 52 | ssr: false, 53 | }); 54 | 55 | /** 56 | * Inference helper for inputs. 57 | * 58 | * @example type HelloInput = RouterInputs['example']['hello'] 59 | */ 60 | export type RouterInputs = inferRouterInputs; 61 | 62 | /** 63 | * Inference helper for outputs. 64 | * 65 | * @example type HelloOutput = RouterOutputs['example']['hello'] 66 | */ 67 | export type RouterOutputs = inferRouterOutputs; 68 | -------------------------------------------------------------------------------- /src/server/api/trpc.ts: -------------------------------------------------------------------------------- 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. The pieces you will 7 | * need to use are documented accordingly near the end. 8 | */ 9 | import { initTRPC } from "@trpc/server"; 10 | import { type CreateNextContextOptions } from "@trpc/server/adapters/next"; 11 | import superjson from "superjson"; 12 | import { ZodError } from "zod"; 13 | import { prisma } from "~/server/db"; 14 | 15 | /** 16 | * 1. CONTEXT 17 | * 18 | * This section defines the "contexts" that are available in the backend API. 19 | * 20 | * These allow you to access things when processing a request, like the database, the session, etc. 21 | */ 22 | 23 | type CreateContextOptions = Record; 24 | 25 | /** 26 | * This helper generates the "internals" for a tRPC context. If you need to use it, you can export 27 | * it from here. 28 | * 29 | * Examples of things you may need it for: 30 | * - testing, so we don't have to mock Next.js' req/res 31 | * - tRPC's `createSSGHelpers`, where we don't have req/res 32 | * 33 | * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts 34 | */ 35 | const createInnerTRPCContext = (_opts: CreateContextOptions) => { 36 | return { 37 | prisma, 38 | }; 39 | }; 40 | 41 | /** 42 | * This is the actual context you will use in your router. It will be used to process every request 43 | * that goes through your tRPC endpoint. 44 | * 45 | * @see https://trpc.io/docs/context 46 | */ 47 | export const createTRPCContext = (_opts: CreateNextContextOptions) => { 48 | return createInnerTRPCContext({}); 49 | }; 50 | 51 | /** 52 | * 2. INITIALIZATION 53 | * 54 | * This is where the tRPC API is initialized, connecting the context and transformer. We also parse 55 | * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation 56 | * errors on the backend. 57 | */ 58 | 59 | const t = initTRPC.context().create({ 60 | transformer: superjson, 61 | errorFormatter({ shape, error }) { 62 | return { 63 | ...shape, 64 | data: { 65 | ...shape.data, 66 | zodError: 67 | error.cause instanceof ZodError ? error.cause.flatten() : null, 68 | }, 69 | }; 70 | }, 71 | }); 72 | 73 | /** 74 | * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) 75 | * 76 | * These are the pieces you use to build your tRPC API. You should import these a lot in the 77 | * "/src/server/api/routers" directory. 78 | */ 79 | 80 | /** 81 | * This is how you create new routers and sub-routers in your tRPC API. 82 | * 83 | * @see https://trpc.io/docs/router 84 | */ 85 | export const createTRPCRouter = t.router; 86 | 87 | /** 88 | * Public (unauthenticated) procedure 89 | * 90 | * This is the base piece you use to build new queries and mutations on your tRPC API. It does not 91 | * guarantee that a user querying is authorized, but you can still access user session data if they 92 | * are logged in. 93 | */ 94 | export const publicProcedure = t.procedure; 95 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { api } from "~/utils/api"; 3 | 4 | export default function Home() { 5 | //define constants 6 | const [name, setName] = useState(""); 7 | const [email, setEmail] = useState(""); 8 | const [nameToUpdate, setNameToUpdate] = useState(""); 9 | const [emailToUpdate, setEmailToUpdate] = useState(""); 10 | const [userId, setUserId] = useState(""); 11 | const [userIdToUpdate, setUserIdToUpdate] = useState(""); 12 | const [userIdToDelete, setUserIdToDelete] = useState(""); 13 | 14 | //define functions 15 | const fetchAllUsers = api.example.getAll.useQuery(); 16 | const fetchOneUser = api.example.getOne.useQuery({ id: userId }); 17 | 18 | const createUserMutation = api.example.createUser.useMutation(); 19 | const updateUserMutation = api.example.updateUser.useMutation(); 20 | const deleteUserMutation = api.example.deleteUser.useMutation(); 21 | 22 | //define handlers 23 | const handleCreateUser = async () => { 24 | try { 25 | await createUserMutation.mutateAsync({ 26 | name: name, 27 | email: email, 28 | }); 29 | setName(""); 30 | setEmail(""); 31 | fetchAllUsers.refetch(); 32 | } catch (error) { 33 | console.log(error); 34 | } 35 | }; 36 | 37 | const handleUpdateUser = async () => { 38 | try { 39 | await updateUserMutation.mutateAsync({ 40 | id: userIdToUpdate, 41 | name: nameToUpdate, 42 | email: emailToUpdate, 43 | }); 44 | setNameToUpdate(""); 45 | setEmailToUpdate(""); 46 | setUserIdToUpdate(""); 47 | fetchAllUsers.refetch(); 48 | } catch (error) { 49 | console.log(error); 50 | } 51 | }; 52 | 53 | const handleDeleteUser = async () => { 54 | try { 55 | await deleteUserMutation.mutateAsync({ 56 | id: userIdToDelete, 57 | }); 58 | setUserIdToDelete(""); 59 | fetchAllUsers.refetch(); 60 | } catch (error) { 61 | console.log(error); 62 | } 63 | }; 64 | 65 | //return an empty div 66 | return ( 67 |
68 |
69 |

Get All Users

70 |
71 | 77 | 78 |
79 |

Id

80 |

Name

81 |

Email

82 |
83 | 84 | {fetchAllUsers.data && 85 | fetchAllUsers.data.map((user) => ( 86 |
90 |

{user.id}

91 |

{user.name}

92 |

{user.email}

93 |
94 | ))} 95 | 96 | {/* Get one user UI */} 97 | 98 |
99 |

Get One User

100 |
101 | setUserId(String(e.target.value))} 106 | /> 107 | 113 |
114 | {fetchOneUser.data && ( 115 |
116 |

Name: {fetchOneUser.data.name}

117 |

Email: {fetchOneUser.data.email}

118 |
119 | )} 120 |
121 | 122 | {/* Create User */} 123 |
124 |

Create New User

125 |
126 | setName(e.target.value)} 131 | /> 132 | setEmail(e.target.value)} 137 | /> 138 |
139 | 140 | 146 |
147 | 148 | {/* Update User */} 149 |
150 |

Update User

151 |
152 | setNameToUpdate(e.target.value)} 157 | /> 158 | setEmailToUpdate(e.target.value)} 163 | /> 164 |
165 | setUserIdToUpdate(e.target.value)} 170 | /> 171 | 177 |
178 | 179 | {/* Delete User */} 180 | 181 |
182 |

Delete User

183 | setUserIdToDelete(e.target.value)} 188 | /> 189 | 195 |
196 |
197 | ); 198 | } 199 | --------------------------------------------------------------------------------