mirror of
https://github.com/SrIzan10/starters.git
synced 2026-05-01 11:05:16 +00:00
add next-prisma-trpc example (#198)
This commit is contained in:
34
examples/next-prisma-trpc/.eslintrc
Normal file
34
examples/next-prisma-trpc/.eslintrc
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser", // Specifies the ESLint parser
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018, // Allows for the parsing of modern ECMAScript features
|
||||
"sourceType": "module" // Allows for the use of imports
|
||||
},
|
||||
"rules": {
|
||||
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/prop-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
},
|
||||
// "overrides": [
|
||||
// {
|
||||
// "files": [],
|
||||
// "rules": {
|
||||
// "@typescript-eslint/no-unused-vars": "off"
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
examples/next-prisma-trpc/.github/FUNDING.yml
vendored
Normal file
3
examples/next-prisma-trpc/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: KATT
|
||||
7
examples/next-prisma-trpc/.github/dependabot.yml
vendored
Normal file
7
examples/next-prisma-trpc/.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 2
|
||||
71
examples/next-prisma-trpc/.github/workflows/codeql-analysis.yml
vendored
Normal file
71
examples/next-prisma-trpc/.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, 0.x ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '27 0 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'typescript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
38
examples/next-prisma-trpc/.github/workflows/main.yml
vendored
Normal file
38
examples/next-prisma-trpc/.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: E2E-testing
|
||||
on: [push]
|
||||
jobs:
|
||||
e2e:
|
||||
env:
|
||||
DATABASE_URL: postgresql://postgres:@localhost:5432/trpcdb
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['14.x']
|
||||
os: [ubuntu-latest]
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:12.1
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: trpcdb
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# - name: Install deps and build (with cache)
|
||||
# uses: bahmutov/npm-install@v1
|
||||
- run: yarn install
|
||||
|
||||
- name: Next.js cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ github.workspace }}/.next/cache
|
||||
key: ${{ runner.os }}-${{ runner.node }}--nextjs
|
||||
|
||||
- run: yarn playwright install-deps
|
||||
- run: yarn lint
|
||||
- run: yarn build
|
||||
- run: yarn test-start
|
||||
- run: yarn test-dev
|
||||
38
examples/next-prisma-trpc/.gitignore
vendored
Normal file
38
examples/next-prisma-trpc/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
*.db
|
||||
*.db-journal
|
||||
prisma/_sqlite/migrations
|
||||
7
examples/next-prisma-trpc/.vscode/extensions.json
vendored
Normal file
7
examples/next-prisma-trpc/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"esbenp.prettier-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"prisma.prisma"
|
||||
]
|
||||
}
|
||||
3
examples/next-prisma-trpc/.vscode/settings.json
vendored
Normal file
3
examples/next-prisma-trpc/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
31
examples/next-prisma-trpc/README.md
Normal file
31
examples/next-prisma-trpc/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: NextJS Prisma tRPC
|
||||
description: A NextJS app using Prisma with tRPC
|
||||
tags:
|
||||
- next
|
||||
- react
|
||||
- prisma
|
||||
- trpc
|
||||
- postgresql
|
||||
- typescript
|
||||
---
|
||||
|
||||
# NextJS Prisma tRPC
|
||||
|
||||
This is an example [NextJS](https://nextjs.org/) app that uses [Prisma](https://www.prisma.io/) with [tRPC](https://trpc.io/).
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- NextJS
|
||||
- Prisma
|
||||
- PostgreSQL
|
||||
- E2E typesafety with [tRPC](https://trpc.io)
|
||||
|
||||
## 💁♀️ How to use
|
||||
|
||||
- Click the `Deploy on Railway` button
|
||||
|
||||
## 📝 Notes
|
||||
|
||||
- [tRPC docs with NextJS](https://trpc.io/docs/nextjs)
|
||||
- [Comparison with BlitzJS](https://trpc.io/docs/further-reading#differences-to-blitzjs)
|
||||
8
examples/next-prisma-trpc/jest-playwright.config.js
Normal file
8
examples/next-prisma-trpc/jest-playwright.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// https://github.com/playwright-community/jest-playwright/#configuration
|
||||
module.exports = {
|
||||
browsers: ['chromium', 'firefox', 'webkit'],
|
||||
exitOnPageError: false, // GitHub currently throws errors
|
||||
launchOptions: {
|
||||
headless: true,
|
||||
},
|
||||
};
|
||||
7
examples/next-prisma-trpc/jest.config.js
Normal file
7
examples/next-prisma-trpc/jest.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
preset: 'jest-playwright-preset',
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest',
|
||||
},
|
||||
};
|
||||
6
examples/next-prisma-trpc/next-env.d.ts
vendored
Normal file
6
examples/next-prisma-trpc/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
4
examples/next-prisma-trpc/next.config.js
Normal file
4
examples/next-prisma-trpc/next.config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* @link https://nextjs.org/docs/api-reference/next.config.js/introduction
|
||||
*/
|
||||
module.exports = {};
|
||||
18204
examples/next-prisma-trpc/package-lock.json
generated
Normal file
18204
examples/next-prisma-trpc/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
71
examples/next-prisma-trpc/package.json
Normal file
71
examples/next-prisma-trpc/package.json
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"name": "@examples/next-starter",
|
||||
"version": "9.8.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build:1-generate": "prisma generate",
|
||||
"build:2-migrate": "prisma migrate deploy",
|
||||
"build:3-build": "next build",
|
||||
"build": "run-s build:*",
|
||||
"dev": "yarn dx:next",
|
||||
"dx:next": "yarn migrate-dev && yarn generate && next dev",
|
||||
"dx:studio": "yarn studio",
|
||||
"dx": "run-p dx:* --print-label",
|
||||
"dev-nuke": "rm -rf prisma/*.db**",
|
||||
"generate": "prisma generate",
|
||||
"migrate-dev": "prisma migrate dev",
|
||||
"migrate": "prisma migrate deploy",
|
||||
"start": "next start",
|
||||
"studio": "prisma studio",
|
||||
"lint": "eslint src",
|
||||
"lint-fix": "yarn lint --fix",
|
||||
"test-dev": "start-server-and-test dev 3000 test",
|
||||
"test-start": "start-server-and-test start 3000 test",
|
||||
"test": "jest"
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 80,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^3.0.1",
|
||||
"@trpc/client": "^9.8.0",
|
||||
"@trpc/next": "^9.8.0",
|
||||
"@trpc/react": "^9.8.0",
|
||||
"@trpc/server": "^9.8.0",
|
||||
"clsx": "^1.1.1",
|
||||
"next": "^11.1.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-query": "^3.6.0",
|
||||
"start-server-and-test": "^1.12.0",
|
||||
"superjson": "^1.7.4",
|
||||
"zod": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/react": "^17.0.20",
|
||||
"@typescript-eslint/eslint-plugin": "^4.30.0",
|
||||
"@typescript-eslint/parser": "^4.26.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-next": "^11.0.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.25.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^27.1.0",
|
||||
"jest-playwright": "^0.0.1",
|
||||
"jest-playwright-preset": "^1.4.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"playwright": "^1.14.1",
|
||||
"prettier": "^2.3.2",
|
||||
"prisma": "^3.0.1",
|
||||
"ts-jest": "^27.0.5",
|
||||
"typescript": "4.4.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "restricted"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Post" (
|
||||
"id" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"text" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
22
examples/next-prisma-trpc/prisma/schema.prisma
Normal file
22
examples/next-prisma-trpc/prisma/schema.prisma
Normal file
@@ -0,0 +1,22 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
datasource db {
|
||||
provider = "postgres"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
model Post {
|
||||
id String @id @default(uuid())
|
||||
title String
|
||||
text String
|
||||
|
||||
// To return `Date`s intact through the API we need to add data transformers
|
||||
// https://trpc.io/docs/data-transformers
|
||||
// createdAt DateTime @unique @default(now())
|
||||
// updatedAt DateTime @unique @default(now())
|
||||
}
|
||||
BIN
examples/next-prisma-trpc/public/favicon.ico
Normal file
BIN
examples/next-prisma-trpc/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
4
examples/next-prisma-trpc/public/vercel.svg
Normal file
4
examples/next-prisma-trpc/public/vercel.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
3
examples/next-prisma-trpc/sandbox.config.json
Normal file
3
examples/next-prisma-trpc/sandbox.config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"template": "next"
|
||||
}
|
||||
85
examples/next-prisma-trpc/src/pages/_app.tsx
Normal file
85
examples/next-prisma-trpc/src/pages/_app.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { httpBatchLink } from '@trpc/client/links/httpBatchLink';
|
||||
import { loggerLink } from '@trpc/client/links/loggerLink';
|
||||
import { withTRPC } from '@trpc/next';
|
||||
import { AppType } from 'next/dist/shared/lib/utils';
|
||||
import { AppRouter } from 'server/routers/_app';
|
||||
import superjson from 'superjson';
|
||||
|
||||
const MyApp: AppType = ({ Component, pageProps }) => {
|
||||
return (
|
||||
<>
|
||||
<Component {...pageProps} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function getBaseUrl() {
|
||||
if (process.browser) {
|
||||
return '';
|
||||
}
|
||||
// // reference for vercel.com
|
||||
// if (process.env.VERCEL_URL) {
|
||||
// return `https://${process.env.VERCEL_URL}`;
|
||||
// }
|
||||
|
||||
// // reference for render.com
|
||||
// if (process.env.RENDER_INTERNAL_HOSTNAME) {
|
||||
// return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;
|
||||
// }
|
||||
|
||||
// assume localhost
|
||||
return `http://localhost:${process.env.PORT ?? 3000}`;
|
||||
}
|
||||
|
||||
export default withTRPC<AppRouter>({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
config() {
|
||||
/**
|
||||
* If you want to use SSR, you need to use the server's full URL
|
||||
* @link https://trpc.io/docs/ssr
|
||||
*/
|
||||
return {
|
||||
/**
|
||||
* @link https://trpc.io/docs/links
|
||||
*/
|
||||
links: [
|
||||
// adds pretty logs to your console in development and logs errors in production
|
||||
loggerLink({
|
||||
enabled: (opts) =>
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
(opts.direction === 'down' && opts.result instanceof Error),
|
||||
}),
|
||||
httpBatchLink({
|
||||
url: `${getBaseUrl()}/api/trpc`,
|
||||
}),
|
||||
],
|
||||
/**
|
||||
* @link https://trpc.io/docs/data-transformers
|
||||
*/
|
||||
transformer: superjson,
|
||||
/**
|
||||
* @link https://react-query.tanstack.com/reference/QueryClient
|
||||
*/
|
||||
// queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
|
||||
};
|
||||
},
|
||||
/**
|
||||
* @link https://trpc.io/docs/ssr
|
||||
*/
|
||||
ssr: true,
|
||||
/**
|
||||
* Set headers or status code when doing SSR
|
||||
*/
|
||||
responseMeta({ clientErrors }) {
|
||||
if (clientErrors.length) {
|
||||
// propagate http first error from API calls
|
||||
return {
|
||||
status: clientErrors[0].data?.httpStatus ?? 500,
|
||||
};
|
||||
}
|
||||
|
||||
// for app caching with SSR see https://trpc.io/docs/caching
|
||||
|
||||
return {};
|
||||
},
|
||||
})(MyApp);
|
||||
35
examples/next-prisma-trpc/src/pages/api/trpc/[trpc].ts
Normal file
35
examples/next-prisma-trpc/src/pages/api/trpc/[trpc].ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* This file contains tRPC's HTTP response handler
|
||||
*/
|
||||
import * as trpcNext from '@trpc/server/adapters/next';
|
||||
import { appRouter } from 'server/routers/_app';
|
||||
import { createContext } from 'server/context';
|
||||
|
||||
export default trpcNext.createNextApiHandler({
|
||||
router: appRouter,
|
||||
/**
|
||||
* @link https://trpc.io/docs/context
|
||||
*/
|
||||
createContext,
|
||||
/**
|
||||
* @link https://trpc.io/docs/error-handling
|
||||
*/
|
||||
onError({ error }) {
|
||||
if (error.code === 'INTERNAL_SERVER_ERROR') {
|
||||
// send to bug reporting
|
||||
console.error('Something went wrong', error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Enable query batching
|
||||
*/
|
||||
batching: {
|
||||
enabled: true,
|
||||
},
|
||||
/**
|
||||
* @link https://trpc.io/docs/caching#api-response-caching
|
||||
*/
|
||||
// responseMeta() {
|
||||
// // ...
|
||||
// },
|
||||
});
|
||||
120
examples/next-prisma-trpc/src/pages/index.tsx
Normal file
120
examples/next-prisma-trpc/src/pages/index.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import { ReactQueryDevtools } from 'react-query/devtools';
|
||||
import { trpc } from '../utils/trpc';
|
||||
|
||||
export default function IndexPage() {
|
||||
const postsQuery = trpc.useQuery(['post.all']);
|
||||
const addPost = trpc.useMutation('post.add');
|
||||
const utils = trpc.useContext();
|
||||
|
||||
// prefetch all posts for instant navigation
|
||||
// useEffect(() => {
|
||||
// postsQuery.data?.forEach((post) => {
|
||||
// utils.prefetchQuery(['post.byId', post.id]);
|
||||
// });
|
||||
// }, [postsQuery.data, utils]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Prisma Starter</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<h1>Welcome to your tRPC starter!</h1>
|
||||
<p>
|
||||
Check <a href="https://trpc.io/docs">the docs</a> whenever you get
|
||||
stuck, or ping <a href="https://twitter.com/alexdotjs">@alexdotjs</a> on
|
||||
Twitter.
|
||||
</p>
|
||||
<h2>
|
||||
Posts
|
||||
{postsQuery.status === 'loading' && '(loading)'}
|
||||
</h2>
|
||||
{postsQuery.data?.map((item) => (
|
||||
<article key={item.id}>
|
||||
<h3>{item.title}</h3>
|
||||
<Link href={`/post/${item.id}`}>
|
||||
<a>View more</a>
|
||||
</Link>
|
||||
</article>
|
||||
))}
|
||||
|
||||
<hr />
|
||||
|
||||
<form
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
/**
|
||||
* In a real app you probably don't want to use this manually
|
||||
* Checkout React Hook Form - it works great with tRPC
|
||||
* @link https://react-hook-form.com/
|
||||
*/
|
||||
|
||||
const $text: HTMLInputElement = (e as any).target.elements.text;
|
||||
const $title: HTMLInputElement = (e as any).target.elements.title;
|
||||
const input = {
|
||||
title: $title.value,
|
||||
text: $text.value,
|
||||
};
|
||||
try {
|
||||
await addPost.mutateAsync(input);
|
||||
utils.invalidateQuery(['post.all']);
|
||||
|
||||
$title.value = '';
|
||||
$text.value = '';
|
||||
} catch {}
|
||||
}}
|
||||
>
|
||||
<label htmlFor="title">Title:</label>
|
||||
<br />
|
||||
<input
|
||||
id="title"
|
||||
name="title"
|
||||
type="text"
|
||||
disabled={addPost.isLoading}
|
||||
/>
|
||||
|
||||
<br />
|
||||
<label htmlFor="text">Text:</label>
|
||||
<br />
|
||||
<textarea id="text" name="text" disabled={addPost.isLoading} />
|
||||
<br />
|
||||
<input type="submit" disabled={addPost.isLoading} />
|
||||
{addPost.error && (
|
||||
<p style={{ color: 'red' }}>{addPost.error.message}</p>
|
||||
)}
|
||||
</form>
|
||||
|
||||
{process.env.NODE_ENV !== 'production' && (
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If you want to statically render this page
|
||||
* - Export `appRouter` & `createContext` from [trpc].ts
|
||||
* - Make the `opts` object optional on `createContext()`
|
||||
*
|
||||
* @link https://trpc.io/docs/ssg
|
||||
*/
|
||||
// export const getStaticProps = async (
|
||||
// context: GetStaticPropsContext<{ filter: string }>,
|
||||
// ) => {
|
||||
// const ssg = createSSGHelpers({
|
||||
// router: appRouter,
|
||||
// ctx: await createContext(),
|
||||
// });
|
||||
//
|
||||
// await ssg.fetchQuery('post.all');
|
||||
//
|
||||
// return {
|
||||
// props: {
|
||||
// trpcState: ssg.dehydrate(),
|
||||
// filter: context.params?.filter ?? 'all',
|
||||
// },
|
||||
// revalidate: 1,
|
||||
// };
|
||||
// };
|
||||
29
examples/next-prisma-trpc/src/pages/post/[id].tsx
Normal file
29
examples/next-prisma-trpc/src/pages/post/[id].tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useRouter } from 'next/dist/client/router';
|
||||
import { trpc } from 'utils/trpc';
|
||||
import NextError from 'next/error';
|
||||
|
||||
export default function PostViewPage() {
|
||||
const id = useRouter().query.id as string;
|
||||
const postQuery = trpc.useQuery(['post.byId', id]);
|
||||
if (postQuery.error) {
|
||||
return (
|
||||
<NextError
|
||||
title={postQuery.error.message}
|
||||
statusCode={postQuery.error.data?.httpStatus ?? 500}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (postQuery.status !== 'success') {
|
||||
return <>Loading...</>;
|
||||
}
|
||||
const { data } = postQuery;
|
||||
return (
|
||||
<>
|
||||
<h1>{data.title}</h1>
|
||||
<p>{data.text}</p>
|
||||
|
||||
<h2>Raw data:</h2>
|
||||
<pre>{JSON.stringify(data, null, 4)}</pre>
|
||||
</>
|
||||
);
|
||||
}
|
||||
27
examples/next-prisma-trpc/src/server/context.ts
Normal file
27
examples/next-prisma-trpc/src/server/context.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import * as trpc from '@trpc/server';
|
||||
import * as trpcNext from '@trpc/server/adapters/next';
|
||||
|
||||
const prisma = new PrismaClient({
|
||||
log:
|
||||
process.env.NODE_ENV === 'development'
|
||||
? ['query', 'error', 'warn']
|
||||
: ['error'],
|
||||
});
|
||||
/**
|
||||
* Creates context for an incoming request
|
||||
* @link https://trpc.io/docs/context
|
||||
*/
|
||||
export const createContext = async ({
|
||||
req,
|
||||
res,
|
||||
}: trpcNext.CreateNextContextOptions) => {
|
||||
// for API-response caching see https://trpc.io/docs/caching
|
||||
return {
|
||||
req,
|
||||
res,
|
||||
prisma,
|
||||
};
|
||||
};
|
||||
|
||||
export type Context = trpc.inferAsyncReturnType<typeof createContext>;
|
||||
9
examples/next-prisma-trpc/src/server/createRouter.ts
Normal file
9
examples/next-prisma-trpc/src/server/createRouter.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import * as trpc from '@trpc/server';
|
||||
import { Context } from './context';
|
||||
|
||||
/**
|
||||
* Helper function to create a router with context
|
||||
*/
|
||||
export function createRouter() {
|
||||
return trpc.router<Context>();
|
||||
}
|
||||
27
examples/next-prisma-trpc/src/server/routers/_app.ts
Normal file
27
examples/next-prisma-trpc/src/server/routers/_app.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* This file contains the root router of your tRPC-backend
|
||||
*/
|
||||
import superjson from 'superjson';
|
||||
import { createRouter } from '../createRouter';
|
||||
import { postRouter } from './post';
|
||||
|
||||
/**
|
||||
* Create your application's root router
|
||||
* If you want to use SSG, you need export this
|
||||
* @link https://trpc.io/docs/ssg
|
||||
* @link https://trpc.io/docs/router
|
||||
*/
|
||||
export const appRouter = createRouter()
|
||||
/**
|
||||
* Add data transformers
|
||||
* @link https://trpc.io/docs/data-transformers
|
||||
*/
|
||||
.transformer(superjson)
|
||||
/**
|
||||
* Optionally do custom error (type safe!) formatting
|
||||
* @link https://trpc.io/docs/error-formatting
|
||||
*/
|
||||
// .formatError(({ shape, error }) => { })
|
||||
.merge('post.', postRouter);
|
||||
|
||||
export type AppRouter = typeof appRouter;
|
||||
86
examples/next-prisma-trpc/src/server/routers/post.ts
Normal file
86
examples/next-prisma-trpc/src/server/routers/post.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
*
|
||||
* This is an example router, you can delete this file and then update `../pages/api/trpc/[trpc].tsx`
|
||||
*/
|
||||
|
||||
import { createRouter } from 'server/createRouter';
|
||||
import { z } from 'zod';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
export const postRouter = createRouter()
|
||||
// create
|
||||
.mutation('add', {
|
||||
input: z.object({
|
||||
id: z.string().uuid().optional(),
|
||||
title: z.string().min(1).max(32),
|
||||
text: z.string().min(1),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const post = await ctx.prisma.post.create({
|
||||
data: input,
|
||||
});
|
||||
return post;
|
||||
},
|
||||
})
|
||||
// read
|
||||
.query('all', {
|
||||
async resolve({ ctx }) {
|
||||
/**
|
||||
* For pagination you can have a look at this docs site
|
||||
* @link https://trpc.io/docs/useInfiniteQuery
|
||||
*/
|
||||
|
||||
return ctx.prisma.post.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
})
|
||||
.query('byId', {
|
||||
input: z.string(),
|
||||
async resolve({ ctx, input }) {
|
||||
const post = await ctx.prisma.post.findUnique({
|
||||
where: { id: input },
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
text: true,
|
||||
},
|
||||
});
|
||||
if (!post) {
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: `No post with id '${input}'`,
|
||||
});
|
||||
}
|
||||
return post;
|
||||
},
|
||||
})
|
||||
// update
|
||||
.mutation('edit', {
|
||||
input: z.object({
|
||||
id: z.string().uuid(),
|
||||
data: z.object({
|
||||
title: z.string().min(1).max(32).optional(),
|
||||
text: z.string().min(1).optional(),
|
||||
}),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const { id, data } = input;
|
||||
const post = await ctx.prisma.post.update({
|
||||
where: { id },
|
||||
data,
|
||||
});
|
||||
return post;
|
||||
},
|
||||
})
|
||||
// delete
|
||||
.mutation('delete', {
|
||||
input: z.string().uuid(),
|
||||
async resolve({ input: id, ctx }) {
|
||||
await ctx.prisma.post.delete({ where: { id } });
|
||||
return id;
|
||||
},
|
||||
});
|
||||
20
examples/next-prisma-trpc/src/utils/trpc.ts
Normal file
20
examples/next-prisma-trpc/src/utils/trpc.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { createReactQueryHooks } from '@trpc/react';
|
||||
import type { inferProcedureOutput } from '@trpc/server';
|
||||
// ℹ️ Type-only import:
|
||||
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
|
||||
import type { AppRouter } from 'server/routers/_app';
|
||||
|
||||
/**
|
||||
* A set of strongly-typed React hooks from your `AppRouter` type signature with `createReactQueryHooks`.
|
||||
* @link https://trpc.io/docs/react#3-create-trpc-hooks
|
||||
*/
|
||||
export const trpc = createReactQueryHooks<AppRouter>();
|
||||
|
||||
// export const transformer = superjson;
|
||||
/**
|
||||
* This is a helper method to infer the output of a query resolver
|
||||
* @example type HelloOutput = inferQueryOutput<'hello'>
|
||||
*/
|
||||
export type inferQueryOutput<
|
||||
TRouteKey extends keyof AppRouter['_def']['queries'],
|
||||
> = inferProcedureOutput<AppRouter['_def']['queries'][TRouteKey]>;
|
||||
14
examples/next-prisma-trpc/test/playwright.test.ts
Normal file
14
examples/next-prisma-trpc/test/playwright.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
jest.setTimeout(35e3);
|
||||
|
||||
test('go to /', async () => {
|
||||
await page.goto('http://localhost:3000');
|
||||
|
||||
await page.waitForSelector(`text=Starter`);
|
||||
});
|
||||
|
||||
test('test 404', async () => {
|
||||
const res = await page.goto('http://localhost:3000/post/not-found');
|
||||
expect(res?.status()).toBe(404);
|
||||
});
|
||||
|
||||
export {};
|
||||
22
examples/next-prisma-trpc/tsconfig.json
Normal file
22
examples/next-prisma-trpc/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"types": ["@types/jest", "jest-playwright-preset", "expect-playwright"],
|
||||
"baseUrl": "./src"
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user