├── .env ├── .eslintrc ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── main.yml ├── .gitignore ├── .stackblitzrc ├── .vscode ├── extensions.json └── settings.json ├── README.md ├── next-env.d.ts ├── next.config.js ├── package.json ├── playwright.config.ts ├── playwright └── smoke.test.ts ├── prisma ├── migrations │ ├── 20220706070154_init │ │ └── migration.sql │ └── migration_lock.toml ├── schema.prisma └── seed.ts ├── public ├── favicon.ico └── vercel.svg ├── render.yaml ├── sandbox.config.json ├── src ├── components │ └── DefaultLayout.tsx ├── pages │ ├── _app.tsx │ ├── api │ │ └── trpc │ │ │ └── [trpc].ts │ ├── index.tsx │ └── post │ │ └── [id].tsx ├── server │ ├── context.ts │ ├── env.js │ ├── prisma.ts │ ├── routers │ │ ├── _app.ts │ │ ├── health.ts │ │ ├── post.test.ts │ │ └── post.ts │ └── trpc.ts └── utils │ ├── publicRuntimeConfig.ts │ └── trpc.ts ├── tsconfig.json └── vitest.config.ts /.env: -------------------------------------------------------------------------------- 1 | # Make sure to override these in deployment 2 | # DATABASE_URL=postgresql://postgres:@localhost:5432/next-prisma-starter-new 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", // Specifies the ESLint parser 3 | "extends": [ 4 | "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin 5 | "plugin:react/recommended", 6 | "plugin:react-hooks/recommended", 7 | "plugin:prettier/recommended" 8 | ], 9 | "parserOptions": { 10 | "project": "tsconfig.json", 11 | "ecmaVersion": 2018, // Allows for the parsing of modern ECMAScript features 12 | "sourceType": "module" // Allows for the use of imports 13 | }, 14 | "rules": { 15 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs 16 | "@typescript-eslint/explicit-function-return-type": "off", 17 | "@typescript-eslint/explicit-module-boundary-types": "off", 18 | "react/react-in-jsx-scope": "off", 19 | "react/prop-types": "off", 20 | "@typescript-eslint/no-explicit-any": "off" 21 | }, 22 | // "overrides": [ 23 | // { 24 | // "files": [], 25 | // "rules": { 26 | // "@typescript-eslint/no-unused-vars": "off" 27 | // } 28 | // } 29 | // ], 30 | "settings": { 31 | "react": { 32 | "version": "detect" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: KATT 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: '/' 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 2 8 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: E2E-testing 2 | on: [push] 3 | jobs: 4 | e2e: 5 | env: 6 | NODE_ENV: test 7 | NEXTAUTH_SECRET: supersecret 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | node: ['16.x'] 12 | os: [ubuntu-latest] 13 | steps: 14 | - name: Checkout repo 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - uses: pnpm/action-setup@v2.2.2 20 | with: 21 | version: 7.12.0 22 | 23 | - name: Use Node ${{ matrix.node }} 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node }} 27 | # cache: 'pnpm' # You can active this cache when your repo has a lockfile 28 | 29 | - name: Install deps (with cache) 30 | run: pnpm install 31 | 32 | - name: Next.js cache 33 | uses: actions/cache@v3 34 | with: 35 | path: ${{ github.workspace }}/.next/cache 36 | key: ${{ runner.os }}-${{ runner.node }}-${{ hashFiles('**/pnpm-lock.yaml') }}-nextjs 37 | 38 | - name: Build and test 39 | run: pnpm build && pnpm test-start && pnpm test-dev 40 | 41 | - name: Upload test results 42 | if: ${{ always() }} 43 | uses: actions/upload-artifact@v2 44 | with: 45 | name: test results 46 | path: | 47 | playwright/test-results 48 | -------------------------------------------------------------------------------- /.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 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | *.db 37 | *.db-journal 38 | 39 | 40 | # testing 41 | playwright/test-results 42 | -------------------------------------------------------------------------------- /.stackblitzrc: -------------------------------------------------------------------------------- 1 | { 2 | "installDependencies": true, 3 | "startCommand": "yarn dx", 4 | "env": { 5 | "NODE_ENV": "development" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "dbaeumer.vscode-eslint", 5 | "prisma.prisma" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Prisma + tRPC 2 | 3 | 4 | > V10 Preview of tRPC! 5 | 6 | - 📚 See docs here: https://alpha.trpc.io/docs 7 | - 🙏 Feel free to open issues in this repo to give feedback! 8 | - ❓ Search the project for `QUESTION` for open API discussions, but don't feel limited to *only* give feedback on those! 9 | - ⚡ ~~Open in CodeSandbox: [https://codesandbox.io/s/github/trpc/examples-v10-next-prisma-starter-sqlite](https://codesandbox.io/s/github/trpc/examples-v10-next-prisma-starter-sqlite?file=/src/pages/post/%5Bid%5D.tsx)~~ Inference on CodeSandbox currently doesn't work, so you'll have to open the project locally 10 | 11 | 44 | 45 | 46 | ### Requirements 47 | 48 | - Node >= 14 49 | 50 | 51 | ## Development 52 | 53 | ### Clone & start project 54 | 55 | ```bash 56 | yarn create next-app --example https://github.com/trpc/examples-v10-next-prisma-starter-sqlite trpc-prisma-starter 57 | cd trpc-prisma-starter 58 | yarn 59 | yarn dev 60 | ``` 61 | 62 | ### Commands 63 | 64 | ```bash 65 | yarn build # runs `prisma generate` + `prisma migrate` + `next build` 66 | yarn db-reset # resets local db 67 | yarn dev # does db changes + starts next.js 68 | yarn test-dev # runs e2e tests on dev 69 | yarn test-start # runs e2e tests on `next start` - build required before 70 | yarn test:unit # runs normal jest unit tests 71 | yarn test:e2e # runs e2e tests 72 | ``` 73 | 74 | ## Deployment 75 | 76 | ### Using [Render](https://render.com/) 77 | 78 | The project contains a [`render.yaml`](./render.yaml) [*"Blueprint"*](https://render.com/docs/blueprint-spec) which makes the project easily deployable on [Render](https://render.com/). 79 | 80 | Go to [dashboard.render.com/blueprints](https://dashboard.render.com/blueprints) and connect to this Blueprint and see how the app and database automatically gets deployed. 81 | 82 | ## Files of note 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
PathDescription
./prisma/schema.prismaPrisma schema
./src/pages/api/trpc/[trpc].tstRPC response handler
./src/server/routersYour app's different tRPC-routers
106 | 107 | --- 108 | 109 | Created by [@alexdotjs](https://twitter.com/alexdotjs). 110 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | /* eslint-disable @typescript-eslint/no-var-requires */ 3 | const { env } = require('./src/server/env'); 4 | 5 | /** 6 | * Don't be scared of the generics here. 7 | * All they do is to give us autocompletion when using this. 8 | * 9 | * @template {import('next').NextConfig} T 10 | * @param {T} config - A generic parameter that flows through to the return type 11 | * @constraint {{import('next').NextConfig}} 12 | */ 13 | function getConfig(config) { 14 | return config; 15 | } 16 | 17 | /** 18 | * @link https://nextjs.org/docs/api-reference/next.config.js/introduction 19 | */ 20 | module.exports = getConfig({ 21 | /** 22 | * Dynamic configuration available for the browser and server. 23 | * Note: requires `ssr: true` or a `getInitialProps` in `_app.tsx` 24 | * @link https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration 25 | */ 26 | publicRuntimeConfig: { 27 | NODE_ENV: env.NODE_ENV, 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@examples/next-starter-sqlite", 3 | "version": "10.0.0-proxy-beta.17", 4 | "private": true, 5 | "scripts": { 6 | "build:1-migrate": "prisma migrate deploy", 7 | "build:2-build": "next build", 8 | "build": "run-s build:*", 9 | "db-seed": "prisma db seed", 10 | "db-migrate-dev": "prisma migrate dev", 11 | "db-reset": "prisma migrate reset", 12 | "dev:next": "next dev", 13 | "dev": "run-s db-migrate-dev db-seed dev:next", 14 | "start": "next start", 15 | "lint": "eslint src", 16 | "lint-fix": "pnpm lint --fix", 17 | "test-dev": "start-server-and-test dev 3000 test", 18 | "test-start": "start-server-and-test start 3000 test", 19 | "test": "run-s test:*", 20 | "test:unit": "vitest run", 21 | "test:e2e": "playwright test", 22 | "postinstall": "prisma generate" 23 | }, 24 | "prisma": { 25 | "seed": "tsx prisma/seed.ts" 26 | }, 27 | "prettier": { 28 | "printWidth": 80, 29 | "trailingComma": "all", 30 | "singleQuote": true 31 | }, 32 | "dependencies": { 33 | "@prisma/client": "^4.3.1", 34 | "@tanstack/react-query": "^4.3.8", 35 | "@trpc/client": "^10.0.0-proxy-beta.17", 36 | "@trpc/next": "^10.0.0-proxy-beta.17", 37 | "@trpc/react": "^10.0.0-proxy-beta.17", 38 | "@trpc/server": "^10.0.0-proxy-beta.17", 39 | "clsx": "^1.1.1", 40 | "next": "^12.3.1", 41 | "react": "^18.2.0", 42 | "react-dom": "^18.2.0", 43 | "superjson": "^1.7.4", 44 | "zod": "^3.0.0" 45 | }, 46 | "devDependencies": { 47 | "@playwright/test": "^1.26.1", 48 | "@tanstack/react-query-devtools": "^4.3.8", 49 | "@types/node": "^18.7.20", 50 | "@types/react": "^18.0.9", 51 | "@typescript-eslint/eslint-plugin": "^5.37.0", 52 | "@typescript-eslint/parser": "^5.37.0", 53 | "eslint": "^7.32.0", 54 | "eslint-config-next": "^12.3.1", 55 | "eslint-config-prettier": "^8.5.0", 56 | "eslint-plugin-prettier": "^4.0.0", 57 | "eslint-plugin-react": "^7.25.1", 58 | "eslint-plugin-react-hooks": "^4.2.0", 59 | "npm-run-all": "^4.1.5", 60 | "prettier": "^2.7.1", 61 | "prisma": "^4.3.1", 62 | "start-server-and-test": "^1.12.0", 63 | "tsx": "^3.9.0", 64 | "typescript": "^4.8.3", 65 | "vite": "^3.1.3", 66 | "vitest": "^0.23.4" 67 | }, 68 | "publishConfig": { 69 | "access": "restricted" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { PlaywrightTestConfig, devices } from '@playwright/test'; 2 | 3 | const baseUrl = process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://localhost:3000'; 4 | console.log(`ℹ️ Using base URL "${baseUrl}"`); 5 | 6 | const opts = { 7 | // launch headless on CI, in browser locally 8 | headless: !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS, 9 | // collectCoverage: !!process.env.PLAYWRIGHT_HEADLESS 10 | }; 11 | const config: PlaywrightTestConfig = { 12 | testDir: './playwright', 13 | outputDir: './playwright/test-results', 14 | // 'github' for GitHub Actions CI to generate annotations, plus a concise 'dot' 15 | // default 'list' when running locally 16 | reporter: process.env.CI ? 'github' : 'list', 17 | use: { 18 | ...devices['Desktop Chrome'], 19 | baseURL: baseUrl, 20 | headless: opts.headless, 21 | video: 'on', 22 | }, 23 | }; 24 | 25 | export default config; 26 | -------------------------------------------------------------------------------- /playwright/smoke.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.setTimeout(35e3); 4 | 5 | test('go to /', async ({ page }) => { 6 | await page.goto('/'); 7 | 8 | await page.waitForSelector(`text=Starter`); 9 | }); 10 | 11 | test('test 404', async ({ page }) => { 12 | const res = await page.goto('/post/not-found'); 13 | expect(res?.status()).toBe(404); 14 | }); 15 | 16 | test('add a post', async ({ page, browser }) => { 17 | const nonce = `${Math.random()}`; 18 | 19 | await page.goto('/'); 20 | await page.fill(`[name=title]`, nonce); 21 | await page.fill(`[name=text]`, nonce); 22 | await page.click(`form [type=submit]`); 23 | await page.waitForLoadState('networkidle'); 24 | await page.reload(); 25 | 26 | expect(await page.content()).toContain(nonce); 27 | 28 | const ssrContext = await browser.newContext({ 29 | javaScriptEnabled: false, 30 | }); 31 | const ssrPage = await ssrContext.newPage(); 32 | await ssrPage.goto('/'); 33 | 34 | expect(await ssrPage.content()).toContain(nonce); 35 | }); 36 | 37 | test('server-side rendering test', async ({ page, browser }) => { 38 | // add a post 39 | const nonce = `${Math.random()}`; 40 | 41 | await page.goto('/'); 42 | await page.fill(`[name=title]`, nonce); 43 | await page.fill(`[name=text]`, nonce); 44 | await page.click(`form [type=submit]`); 45 | await page.waitForLoadState('networkidle'); 46 | 47 | // load the page without js 48 | const ssrContext = await browser.newContext({ 49 | javaScriptEnabled: false, 50 | }); 51 | const ssrPage = await ssrContext.newPage(); 52 | await ssrPage.goto('/'); 53 | expect(await ssrPage.content()).toContain(nonce); 54 | }); 55 | -------------------------------------------------------------------------------- /prisma/migrations/20220706070154_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Post" ( 3 | "id" TEXT NOT NULL PRIMARY KEY, 4 | "title" TEXT NOT NULL, 5 | "text" TEXT NOT NULL, 6 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | "updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP 8 | ); 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 = "sqlite" -------------------------------------------------------------------------------- /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 | datasource db { 5 | provider = "sqlite" 6 | url = "file:./dev.db" 7 | } 8 | 9 | generator client { 10 | provider = "prisma-client-js" 11 | } 12 | 13 | model Post { 14 | id String @id @default(uuid()) 15 | title String 16 | text String 17 | 18 | // To return `Date`s intact through the API we need to add data transformers 19 | // https://trpc.io/docs/data-transformers 20 | createdAt DateTime @default(now()) 21 | updatedAt DateTime @default(now()) @updatedAt 22 | } 23 | -------------------------------------------------------------------------------- /prisma/seed.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds seed data to your db 3 | * 4 | * @link https://www.prisma.io/docs/guides/database/seed-database 5 | */ 6 | import { PrismaClient } from '@prisma/client'; 7 | 8 | const prisma = new PrismaClient(); 9 | 10 | async function main() { 11 | const firstPostId = '5c03994c-fc16-47e0-bd02-d218a370a078'; 12 | await prisma.post.upsert({ 13 | where: { 14 | id: firstPostId, 15 | }, 16 | create: { 17 | id: firstPostId, 18 | title: 'First Post', 19 | text: 'This is an example post generated from `prisma/seed.ts`', 20 | }, 21 | update: {}, 22 | }); 23 | } 24 | 25 | main() 26 | .catch((e) => { 27 | console.error(e); 28 | process.exit(1); 29 | }) 30 | .finally(async () => { 31 | await prisma.$disconnect(); 32 | }); 33 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trpc/examples-v10-next-prisma-starter-sqlite/62b1f1f98f634f77aa8483d093bea00dd298bc27/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /render.yaml: -------------------------------------------------------------------------------- 1 | #### Render Blueprint specification: https://dashboard.render.com/blueprints #### 2 | ## 👇 Preview environments: https://render.com/docs/preview-environments ### 3 | # previewsEnabled: true 4 | ## 👇 Automatically nuke the environment after X days of inactivity to reduce billing: 5 | # previewsExpireAfterDays: 2 6 | services: 7 | - type: web 8 | name: trpc-starter-app 9 | env: node 10 | plan: free 11 | ## 👇 Specify the plan for the PR deployment: 12 | # previewPlan: starter 13 | ## 👇 Preview Environment Initialization script: 14 | # initialDeployHook: yarn db-seed 15 | buildCommand: pnpm install && pnpm build 16 | startCommand: pnpm start 17 | healthCheckPath: /api/trpc/healthz 18 | envVars: 19 | - key: DATABASE_URL 20 | fromDatabase: 21 | name: trpc-starter-db 22 | property: connectionString 23 | 24 | databases: 25 | - name: trpc-starter-db 26 | plan: free 27 | -------------------------------------------------------------------------------- /sandbox.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": "next", 3 | "container": { 4 | "node": "16", 5 | "startScript": "dev" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/components/DefaultLayout.tsx: -------------------------------------------------------------------------------- 1 | import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; 2 | import Head from 'next/head'; 3 | import { ReactNode } from 'react'; 4 | 5 | type DefaultLayoutProps = { children: ReactNode }; 6 | 7 | export const DefaultLayout = ({ children }: DefaultLayoutProps) => { 8 | return ( 9 | <> 10 | 11 | Prisma Starter 12 | 13 | 14 | 15 |
{children}
16 | 17 | {process.env.NODE_ENV !== 'production' && ( 18 | 19 | )} 20 | 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { NextPage } from 'next'; 2 | import type { AppType, AppProps } from 'next/app'; 3 | import type { ReactElement, ReactNode } from 'react'; 4 | import { DefaultLayout } from '~/components/DefaultLayout'; 5 | import { trpc } from '~/utils/trpc'; 6 | 7 | export type NextPageWithLayout< 8 | TProps = Record, 9 | TInitialProps = TProps, 10 | > = NextPage & { 11 | getLayout?: (page: ReactElement) => ReactNode; 12 | }; 13 | 14 | type AppPropsWithLayout = AppProps & { 15 | Component: NextPageWithLayout; 16 | }; 17 | 18 | const MyApp = (({ Component, pageProps }: AppPropsWithLayout) => { 19 | const getLayout = 20 | Component.getLayout ?? ((page) => {page}); 21 | 22 | return getLayout(); 23 | }) as AppType; 24 | 25 | export default trpc.withTRPC(MyApp); 26 | -------------------------------------------------------------------------------- /src/pages/api/trpc/[trpc].ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file contains tRPC's HTTP response handler 3 | */ 4 | import * as trpcNext from '@trpc/server/adapters/next'; 5 | import { createContext } from '~/server/context'; 6 | import { appRouter } from '~/server/routers/_app'; 7 | 8 | export default trpcNext.createNextApiHandler({ 9 | router: appRouter, 10 | /** 11 | * @link https://trpc.io/docs/context 12 | */ 13 | createContext, 14 | /** 15 | * @link https://trpc.io/docs/error-handling 16 | */ 17 | onError({ error }) { 18 | if (error.code === 'INTERNAL_SERVER_ERROR') { 19 | // send to bug reporting 20 | console.error('Something went wrong', error); 21 | } 22 | }, 23 | /** 24 | * Enable query batching 25 | */ 26 | batching: { 27 | enabled: true, 28 | }, 29 | /** 30 | * @link https://trpc.io/docs/caching#api-response-caching 31 | */ 32 | // responseMeta() { 33 | // // ... 34 | // }, 35 | }); 36 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { trpc } from '../utils/trpc'; 2 | import { NextPageWithLayout } from './_app'; 3 | import Link from 'next/link'; 4 | import React from 'react'; 5 | 6 | const IndexPage: NextPageWithLayout = () => { 7 | const utils = trpc.useContext(); 8 | const postsQuery = trpc.post.list.useQuery(); 9 | 10 | const addPost = trpc.post.add.useMutation({ 11 | async onSuccess() { 12 | // refetches posts after a post is added 13 | await utils.post.list.invalidate(); 14 | }, 15 | }); 16 | 17 | // prefetch all posts for instant navigation 18 | // React.useEffect(() => { 19 | // for (const { id } of postsQuery.data ?? []) { 20 | // utils.post.byId.prefetch({ id }); 21 | // } 22 | // }, [postsQuery.data, utils]); 23 | 24 | return ( 25 | <> 26 |

Welcome to your tRPC starter!

27 |

28 | Check the docs whenever you get 29 | stuck, or ping @alexdotjs on 30 | Twitter. 31 |

32 | 33 |

34 | Posts 35 | {postsQuery.status === 'loading' && '(loading)'} 36 |

37 | {postsQuery.data?.map((item) => ( 38 |
39 |

{item.title}

40 | 41 | View more 42 | 43 |
44 | ))} 45 | 46 |
47 | 48 |
{ 50 | e.preventDefault(); 51 | /** 52 | * In a real app you probably don't want to use this manually 53 | * Checkout React Hook Form - it works great with tRPC 54 | * @link https://react-hook-form.com/ 55 | */ 56 | 57 | const $text: HTMLInputElement = (e as any).target.elements.text; 58 | const $title: HTMLInputElement = (e as any).target.elements.title; 59 | const input = { 60 | title: $title.value, 61 | text: $text.value, 62 | }; 63 | try { 64 | await addPost.mutateAsync(input); 65 | 66 | $title.value = ''; 67 | $text.value = ''; 68 | } catch {} 69 | }} 70 | > 71 | 72 |
73 | 79 | 80 |
81 | 82 |
83 |