├── .env
├── .eslintrc
├── .github
├── FUNDING.yml
└── workflows
│ └── lint.yml
├── .gitignore
├── README.md
├── apps
├── expo
│ ├── .babelrc.js
│ ├── .env.example
│ ├── .expo-shared
│ │ └── assets.json
│ ├── .gitignore
│ ├── App.tsx
│ ├── app.config.js
│ ├── assets
│ │ ├── adaptive-icon.png
│ │ ├── icon.development.png
│ │ ├── icon.png
│ │ ├── icon.staging.png
│ │ └── splash.png
│ ├── eas.json
│ ├── index.js
│ ├── metro.config.js
│ ├── package.json
│ ├── react-native.config.js
│ ├── tsconfig.json
│ └── utils
│ │ └── trpc.tsx
└── nextjs
│ ├── .env
│ ├── .gitignore
│ ├── .vscode
│ ├── extensions.json
│ └── settings.json
│ ├── README.md
│ ├── jest-playwright.config.js
│ ├── jest.config.js
│ ├── next-env.d.ts
│ ├── next.config.js
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ └── vercel.svg
│ ├── src
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── api
│ │ │ └── trpc
│ │ │ │ └── [trpc].ts
│ │ ├── index.tsx
│ │ └── post
│ │ │ └── [id].tsx
│ └── utils
│ │ └── trpc.ts
│ ├── test
│ └── playwright.test.ts
│ └── tsconfig.json
├── docker-compose.yml
├── package.json
├── packages
├── api
│ ├── package.json
│ └── src
│ │ ├── createContext.ts
│ │ ├── createRouter.ts
│ │ └── routers
│ │ ├── _app.ts
│ │ └── post.ts
├── react-native
│ ├── hello-world.tsx
│ ├── package.json
│ ├── theme.ts
│ └── trpc.tsx
└── react
│ ├── package.json
│ └── trpc.tsx
├── prisma
├── migrations
│ ├── 20210910132122_init
│ │ └── migration.sql
│ ├── 20220111230540_prisma_3
│ │ └── migration.sql
│ ├── 20220201105846_drop_unique
│ │ └── migration.sql
│ └── migration_lock.toml
└── schema.prisma
├── tsconfig.base.json
├── tsconfig.json
└── yarn.lock
/.env:
--------------------------------------------------------------------------------
1 | # Environment variables declared in this file are automatically made available to Prisma.
2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#using-environment-variables
3 |
4 | # Prisma supports the native connection string format for PostgreSQL, MySQL and SQLite.
5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings
6 |
7 | DATABASE_URL="postgresql://postgres:@localhost:5466/zart"
8 |
--------------------------------------------------------------------------------
/.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 | "ecmaVersion": 2018, // Allows for the parsing of modern ECMAScript features
11 | "sourceType": "module" // Allows for the use of imports
12 | },
13 | "rules": {
14 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
15 | "@typescript-eslint/explicit-function-return-type": "off",
16 | "@typescript-eslint/explicit-module-boundary-types": "off",
17 | "react/react-in-jsx-scope": "off",
18 | "react/prop-types": "off",
19 | "@typescript-eslint/no-explicit-any": "off"
20 | },
21 | "overrides": [
22 | {
23 | "files": ["examples/**/*"],
24 | "rules": {
25 | "@typescript-eslint/no-unused-vars": "off"
26 | }
27 | }
28 | ],
29 | "settings": {
30 | "react": {
31 | "version": "detect"
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: KATT
2 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 | on: [push]
3 | jobs:
4 | lint:
5 | runs-on: ubuntu-latest
6 |
7 | steps:
8 | - uses: actions/checkout@v2
9 | with:
10 | fetch-depth: 0
11 |
12 | - name: Use Node.js 14.x
13 | uses: actions/setup-node@v1
14 | with:
15 | version: 14.x
16 |
17 | - name: Install deps
18 | uses: bahmutov/npm-install@v1
19 |
20 | - name: Lint
21 | run: yarn lint
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | dist
5 |
6 | node_modules
7 | package-lock.json
8 |
9 | .yalc
10 | yalc.lock
11 |
12 | coverage/
13 | *.db
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ℹ️ℹ️ℹ️ This example project is not actively maintained and is using an old version (v9) of tRPC ℹ️ℹ️ℹ️
2 |
3 | If you're looking for alternatives, have a look at https://github.com/t3-oss/create-t3-turbo and other reference projects on https://trpc.io/docs/awesome-trpc
4 |
5 |
6 | ---
7 |
8 |
9 | # zART-Stack 🤯
10 |
11 | > **Z**ero-**A**PI, **R**eact, & **T**ypeScript
12 |
13 | **⚡️ Probably the fastest way to build a React Native app with your own backend ⚡️**
14 |
15 | ## Introduction
16 |
17 | A monorepo containing:
18 |
19 | - Next.js web app
20 | - React Native app with Expo
21 | - A [tRPC](https://trpc.io)-API which is inferred straight into the above.
22 | - [Prisma](http://prisma.io/) as a typesafe ORM
23 |
24 | > In tRPC you simply write API-functions that are automatically inferred straight into your frontend - no matter if it's React, React Native, or something else _(that is TS/JS-based)_.
25 |
26 | ### Video
27 |
28 | > Very rough video recorded in 2 minutes 😅
29 |
30 | [](http://www.youtube.com/watch?v=dLLm6hgMhMQ "Video Title")
31 |
32 | ## Requirements
33 |
34 | You will need docker compose to run the postgres database.
35 | It comes with the [Docker Desktop App](https://docs.docker.com/get-docker/)
36 |
37 | ## Getting started
38 |
39 | ```bash
40 | git clone git@github.com:KATT/zart.git
41 | cd zart
42 | yarn
43 | yarn dev
44 | ```
45 |
46 | > Press `i` after `yarn dev` in to launch the iOS Simulator.
47 |
48 | Now - head over to one of the [`./apps`](./apps), whilist updating [a router in tRPC](./packages/api/src/routers) or the [Database Schema](./prisma/schema.prisma) and see that the data is directly inferred.
49 |
50 | ## Available commands
51 |
52 | | Command | Description |
53 | | --------------------- | ---------------------------------------------------------------------------------------------- |
54 | | `yarn dev` | Starts Postgres, Expo & Next.js |
55 | | `yarn db-up` | Starts Postgres on port `5466` |
56 | | `yarn db-migrate-dev` | Runs the latest Database migrations after updating the [Prisma schema](./prisma/schema.prisma) |
57 | | `yarn db-nuke` | Stops and deletes the the database |
58 |
59 |
60 | ## Folder structure
61 |
62 |
63 | ```graphql
64 | .
65 | ├── apps
66 | │ ├── expo # Expo/RN application
67 | │ └── nextjs # Server-side rendered Next.js application
68 | ├── packages
69 | │ ├── api # tRPC API
70 | │ ├── react # Shared React-helpers
71 | │ └── react-native # RN components. **Could** be shared between Expo & Next.js if you're in to that sort of thing.
72 | └── prisma # Prisma setup
73 | ```
74 |
75 | ## Further reading
76 |
77 | ### Deployment
78 |
79 | #### Vercel
80 |
81 | - Create a Postgres Database
82 | - Set env `DATABASE_URL` pointing towards that db
83 | - Configure *"Root Directory"* as `apps/nextjs` and tick _Include source files outside of the Root Directory in the Build Step_.
84 |
85 |
86 | ### Questions?
87 |
88 | Shoot me a message [on Twitter](https://twitter.com/alexdotjs)!
89 |
90 |
91 | ## Credits
92 |
93 | - tRPC and this example is made by [@alexdotjs](https://twitter.com/alexdotjs)
94 | - `apps/expo` is basically a copy-paste from [`expo-next-monorepo-example`](https://github.com/axeldelafosse/expo-next-monorepo-example) by [axeldelafosse](https://github.com/axeldelafosse).
95 |
96 |
--------------------------------------------------------------------------------
/apps/expo/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true);
3 |
4 | return {
5 | presets: ['@expo/next-adapter/babel'],
6 | plugins: ['react-native-reanimated/plugin']
7 | };
8 | };
9 |
--------------------------------------------------------------------------------
/apps/expo/.env.example:
--------------------------------------------------------------------------------
1 | STAGE=development
--------------------------------------------------------------------------------
/apps/expo/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 | }
5 |
--------------------------------------------------------------------------------
/apps/expo/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | .next/*
4 | npm-debug.*
5 | yarn-error.*
6 | *.jks
7 | *.p8
8 | *.p12
9 | *.key
10 | *.mobileprovision
11 | *.orig.*
12 | web-build/
13 | __generated__/
14 | # @generated expo-cli sync-2138f1e3e130677ea10ea873f6d498e3890e677b
15 | # The following patterns were generated by expo-cli
16 |
17 | # OSX
18 | #
19 | .DS_Store
20 |
21 | # Xcode
22 | #
23 | build/
24 | *.pbxuser
25 | !default.pbxuser
26 | *.mode1v3
27 | !default.mode1v3
28 | *.mode2v3
29 | !default.mode2v3
30 | *.perspectivev3
31 | !default.perspectivev3
32 | xcuserdata
33 | *.xccheckout
34 | *.moved-aside
35 | DerivedData
36 | *.hmap
37 | *.ipa
38 | *.xcuserstate
39 | project.xcworkspace
40 |
41 | # Android/IntelliJ
42 | #
43 | build/
44 | .idea
45 | .gradle
46 | local.properties
47 | *.iml
48 | *.hprof
49 |
50 | # node.js
51 | #
52 | node_modules/
53 | npm-debug.log
54 | yarn-error.log
55 |
56 | # BUCK
57 | buck-out/
58 | \.buckd/
59 | *.keystore
60 | !debug.keystore
61 |
62 | # Bundle artifacts
63 | *.jsbundle
64 |
65 | # CocoaPods
66 | /ios/Pods/
67 |
68 | # Expo
69 | .expo/
70 | web-build/
71 |
72 | # @end expo-cli
73 |
74 | # Native
75 | ios/
76 | android/
--------------------------------------------------------------------------------
/apps/expo/App.tsx:
--------------------------------------------------------------------------------
1 | import { HelloWorld } from '@zart/react-native/hello-world';
2 | import { theme } from '@zart/react-native/theme';
3 | import { DripsyProvider } from 'dripsy';
4 | import Constants from 'expo-constants';
5 | import { StatusBar } from 'expo-status-bar';
6 | import React, { useState } from 'react';
7 | import { SafeAreaProvider } from 'react-native-safe-area-context';
8 | import { enableScreens } from 'react-native-screens';
9 | import { QueryClient, QueryClientProvider } from 'react-query';
10 | import { transformer, trpc } from './utils/trpc';
11 | enableScreens(true);
12 | const { manifest } = Constants;
13 |
14 | const localhost = `http://${manifest.debuggerHost?.split(':').shift()}:3000`;
15 |
16 | export default function App() {
17 | const [queryClient] = useState(() => new QueryClient());
18 | const [trpcClient] = useState(() =>
19 | trpc.createClient({
20 | url: `${localhost}/api/trpc`,
21 |
22 | async headers() {
23 | return {};
24 | },
25 | transformer,
26 | }),
27 | );
28 | return (
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/apps/expo/app.config.js:
--------------------------------------------------------------------------------
1 | const STAGE = process.env.STAGE;
2 |
3 | const envConfig = {
4 | development: {
5 | scheme: 'com.example.development',
6 | icon: './assets/icon.development.png',
7 | backgroundColor: '#FF0000',
8 | },
9 | staging: {
10 | scheme: 'com.example.staging',
11 | icon: './assets/icon.staging.png',
12 | backgroundColor: '#8000FF',
13 | },
14 | production: {
15 | scheme: 'com.example',
16 | icon: './assets/icon.png',
17 | backgroundColor: '#1610FF',
18 | },
19 | };
20 |
21 | const config = envConfig[STAGE || 'development'];
22 |
23 | export default {
24 | name: 'Example',
25 | description: 'Expo + Next.js Monorepo Example',
26 | slug: 'example',
27 | scheme: 'example',
28 | owner: 'poolpoolpool',
29 | icon: config.icon,
30 | sdkVersion: '42.0.0',
31 | version: '0.0.1',
32 | splash: {
33 | image: './assets/splash.png',
34 | resizeMode: 'contain',
35 | backgroundColor: '#000000',
36 | },
37 | ios: {
38 | bundleIdentifier: config.scheme,
39 | supportsTablet: true,
40 | },
41 | android: {
42 | package: config.scheme,
43 | versionCode: 1,
44 | adaptiveIcon: {
45 | foregroundImage: './assets/adaptive-icon.png',
46 | backgroundColor: config.backgroundColor,
47 | },
48 | jsEngine: 'hermes',
49 | },
50 | androidNavigationBar: {
51 | barStyle: 'dark-content',
52 | backgroundColor: '#FFFFFF',
53 | },
54 | assetBundlePatterns: ['**/*'],
55 | orientation: 'portrait',
56 | updates: {
57 | fallbackToCacheTimeout: 0,
58 | },
59 | hooks: {
60 | postPublish: [
61 | {
62 | file: 'sentry-expo/upload-sourcemaps',
63 | config: {},
64 | },
65 | ],
66 | },
67 | extra: {
68 | STAGE: process.env.STAGE,
69 | },
70 | plugins: ['sentry-expo'],
71 | };
72 |
--------------------------------------------------------------------------------
/apps/expo/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trpc/zart/4aaed67ce41ffc3d7e4eb1e7582fa98834f074e6/apps/expo/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/apps/expo/assets/icon.development.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trpc/zart/4aaed67ce41ffc3d7e4eb1e7582fa98834f074e6/apps/expo/assets/icon.development.png
--------------------------------------------------------------------------------
/apps/expo/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trpc/zart/4aaed67ce41ffc3d7e4eb1e7582fa98834f074e6/apps/expo/assets/icon.png
--------------------------------------------------------------------------------
/apps/expo/assets/icon.staging.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trpc/zart/4aaed67ce41ffc3d7e4eb1e7582fa98834f074e6/apps/expo/assets/icon.staging.png
--------------------------------------------------------------------------------
/apps/expo/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trpc/zart/4aaed67ce41ffc3d7e4eb1e7582fa98834f074e6/apps/expo/assets/splash.png
--------------------------------------------------------------------------------
/apps/expo/eas.json:
--------------------------------------------------------------------------------
1 | {
2 | "build": {
3 | "release": {
4 | "releaseChannel": "production",
5 | "env": {
6 | "STAGE": "production"
7 | }
8 | },
9 | "preview": {
10 | "releaseChannel": "staging",
11 | "distribution": "internal",
12 | "android": {
13 | "buildType": "apk"
14 | },
15 | "env": {
16 | "STAGE": "staging"
17 | }
18 | },
19 | "development": {
20 | "developmentClient": true,
21 | "distribution": "internal",
22 | "env": {
23 | "STAGE": "development"
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/apps/expo/index.js:
--------------------------------------------------------------------------------
1 | import 'expo-dev-client';
2 | import 'expo-dev-launcher';
3 | import 'expo/build/Expo.fx';
4 | import { activateKeepAwake } from 'expo-keep-awake';
5 | import { registerRootComponent } from 'expo';
6 |
7 | import App from './App';
8 |
9 | if (__DEV__) {
10 | activateKeepAwake();
11 | }
12 |
13 | registerRootComponent(App);
14 |
--------------------------------------------------------------------------------
/apps/expo/metro.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | const { createMetroConfiguration } = require('expo-yarn-workspaces');
3 |
4 | module.exports = createMetroConfiguration(__dirname);
5 |
--------------------------------------------------------------------------------
/apps/expo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "index.js",
3 | "name": "@expo-next-monorepo-example/expo",
4 | "scripts": {
5 | "start": "react-native start",
6 | "start:expo": "expo start",
7 | "start:clear": "yarn start:expo -c",
8 | "start:dev-client": "yarn expo:prebuild && yarn start:expo --dev-client",
9 | "start:dev-client:clear": "yarn start:dev-client -c",
10 | "start:android": "expo --android",
11 | "start:ios": "expo --ios",
12 | "android": "react-native run-android",
13 | "ios": "react-native run-ios",
14 | "dev": "yarn start:expo",
15 | "eject": "expo eject",
16 | "expo:prebuild": "expo prebuild --skip-dependency-update react",
17 | "run:ios": "yarn expo:prebuild && expo run:ios",
18 | "run:android": "yarn expo:prebuild && expo run:android",
19 | "postinstall": "expo-yarn-workspaces postinstall && cd ../..",
20 | "publish:development": "STAGE=development expo publish",
21 | "publish:staging": "STAGE=staging expo publish --release-channel=staging",
22 | "publish:production": "STAGE=production expo publish --release-channel=production",
23 | "build:development": "STAGE=development eas build --profile development --platform all",
24 | "build:development:ios": "STAGE=development eas build --profile development --platform ios",
25 | "build:development:android": "STAGE=development eas build --profile development --platform android",
26 | "build:preview": "STAGE=staging eas build --profile preview --platform all",
27 | "build:preview:ios": "STAGE=staging eas build --profile preview --platform ios",
28 | "build:preview:android": "STAGE=staging eas build --profile preview --platform android",
29 | "build:production": "STAGE=production eas build --profile production --platform all",
30 | "build:production:ios": "STAGE=production eas build --profile production --platform ios",
31 | "build:production:android": "STAGE=production eas build --profile production --platform android"
32 | },
33 | "dependencies": {
34 | "@gorhom/bottom-sheet": "^4.0.0-alpha.30",
35 | "@sentry/react-native": "^2.6.2",
36 | "@trpc/client": "^9.6.0",
37 | "@trpc/react": "^9.6.0",
38 | "app": "*",
39 | "dripsy": "^2.3.6",
40 | "expo": "^42.0.0-beta.1",
41 | "expo-app-loading": "^1.0.3",
42 | "expo-application": "~3.2.0",
43 | "expo-constants": "~11.0.1",
44 | "expo-dev-client": "^0.4.7",
45 | "expo-device": "^3.3.0",
46 | "expo-font": "~9.2.1",
47 | "expo-haptics": "~10.1.0",
48 | "expo-linking": "~2.3.1",
49 | "expo-modules-core": "^0.2.0",
50 | "expo-notifications": "^0.12.3",
51 | "expo-splash-screen": "~0.11.1",
52 | "expo-status-bar": "~1.0.4",
53 | "expo-updates": "~0.8.4",
54 | "hermes-engine": "0.5.2-rc1",
55 | "react": "^17.0.2",
56 | "react-dom": "^17.0.2",
57 | "react-native": "~0.63.4",
58 | "react-native-gesture-handler": "~1.10.2",
59 | "react-native-reanimated": "2.2.0",
60 | "react-native-safe-area-context": "3.2.0",
61 | "react-native-screens": "~3.6.0",
62 | "react-native-unimodules": "~0.14.5",
63 | "react-native-web": "^0.17.1",
64 | "react-query": "^3.23.2",
65 | "sentry-expo": "^4.0.0",
66 | "superjson": "^1.7.5",
67 | "unimodules-app-loader": "~2.2.0",
68 | "unimodules-constants-interface": "^6.1.0",
69 | "unimodules-file-system-interface": "^6.1.0",
70 | "unimodules-font-interface": "^6.1.0",
71 | "unimodules-permissions-interface": "^6.1.0"
72 | },
73 | "devDependencies": {
74 | "@babel/core": "^7.14.8",
75 | "@expo/next-adapter": "^3.0.5",
76 | "@types/react": "^17.0.20",
77 | "@types/react-native": "~0.63.2",
78 | "expo-cli": "^4.11.0",
79 | "expo-yarn-workspaces": "^1.7.0",
80 | "next-compose-plugins": "^2.2.1",
81 | "typescript": "4.4.2"
82 | },
83 | "private": true,
84 | "version": "1.0.0",
85 | "expo-yarn-workspaces": {
86 | "symlinks": [
87 | "react-native",
88 | "react-native-unimodules",
89 | "expo-modules-core",
90 | "react-native-reanimated",
91 | "react-native-safe-area-context",
92 | "sentry-expo",
93 | "@sentry/cli",
94 | "@sentry/react-native",
95 | "@expo/next-adapter",
96 | "expo-app-loading",
97 | "expo-application",
98 | "expo-constants",
99 | "expo-device",
100 | "expo-error-recovery",
101 | "expo-file-system",
102 | "expo-font",
103 | "expo-haptics",
104 | "expo-keep-awake",
105 | "expo-linking",
106 | "expo-permissions",
107 | "expo-splash-screen",
108 | "expo-status-bar",
109 | "expo-structured-headers",
110 | "expo-updates",
111 | "expo-dev-client",
112 | "expo-dev-launcher",
113 | "expo-dev-menu-interface",
114 | "expo-dev-menu",
115 | "expo-updates-interface",
116 | "expo-notifications",
117 | "unimodules-app-loader",
118 | "unimodules-barcode-scanner-interface",
119 | "unimodules-camera-interface",
120 | "unimodules-constants-interface",
121 | "@unimodules/core",
122 | "unimodules-face-detector-interface",
123 | "unimodules-file-system-interface",
124 | "unimodules-font-interface",
125 | "unimodules-image-loader-interface",
126 | "unimodules-permissions-interface",
127 | "@unimodules/react-native-adapter",
128 | "unimodules-sensors-interface",
129 | "unimodules-task-manager-interface"
130 | ]
131 | },
132 | "react-native-unimodules": {
133 | "android": {
134 | "modulesPaths": [
135 | "../../../../",
136 | "../../../../packages"
137 | ],
138 | "configuration": "api",
139 | "target": "react-native"
140 | },
141 | "ios": {
142 | "modules_paths": [
143 | "../../../",
144 | "../../../packages"
145 | ],
146 | "flags": {
147 | "inhibit_warnings": false
148 | }
149 | }
150 | },
151 | "license": "MIT"
152 | }
153 |
--------------------------------------------------------------------------------
/apps/expo/react-native.config.js:
--------------------------------------------------------------------------------
1 | // File created by expo-dev-client/app.plugin.js
2 |
3 | module.exports = {
4 | dependencies: {
5 | ...require('expo-dev-client/dependencies'),
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/apps/expo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "jsx": "preserve",
5 | "lib": ["dom", "esnext"],
6 | "moduleResolution": "node",
7 | "noEmit": true,
8 | "skipLibCheck": true,
9 | "resolveJsonModule": true,
10 | "strict": false,
11 | "target": "esnext",
12 | "allowJs": true,
13 | "forceConsistentCasingInFileNames": true,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "isolatedModules": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"],
20 | "extends": "expo/tsconfig.base"
21 | }
22 |
--------------------------------------------------------------------------------
/apps/expo/utils/trpc.tsx:
--------------------------------------------------------------------------------
1 | export * from '@zart/react/trpc';
2 |
--------------------------------------------------------------------------------
/apps/nextjs/.env:
--------------------------------------------------------------------------------
1 | # Environment variables declared in this file are automatically made available to Prisma.
2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#using-environment-variables
3 |
4 | # Prisma supports the native connection string format for PostgreSQL, MySQL and SQLite.
5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings
6 |
7 | DATABASE_URL="postgresql://postgres:@localhost:5466/zart"
8 |
--------------------------------------------------------------------------------
/apps/nextjs/.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 | prisma/_sqlite/migrations
39 |
--------------------------------------------------------------------------------
/apps/nextjs/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "esbenp.prettier-vscode",
4 | "dbaeumer.vscode-eslint",
5 | "prisma.prisma"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/apps/nextjs/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/apps/nextjs/README.md:
--------------------------------------------------------------------------------
1 | # Prisma + tRPC
2 |
3 | Try in CodeSandbox: [https://githubbox.com/trpc/trpc/tree/main/examples/next-prisma-starter](https://codesandbox.io/s/github/trpc/trpc/tree/main/examples/next-prisma-starter?file=/src/pages/index.tsx)
4 |
5 |
6 | ## Features
7 |
8 | - 🧙♂️ E2E typesafety with [tRPC](https://trpc.io)
9 | - ⚡ Full-stack React with Next.js
10 | - ⚡ Database with Prisma
11 | - ⚙️ VSCode extensions
12 | - 🎨 ESLint + Prettier
13 | - 💚 CI setup using GitHub Actions:
14 | - ✅ E2E testing with [Playwright](https://playwright.dev/)
15 | - ✅ Linting
16 |
17 |
18 | ## Setup
19 |
20 | ```bash
21 | npx create-next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter
22 | cd trpc-prisma-starter
23 | yarn
24 | yarn dev
25 | ```
26 |
27 | ## Files of note
28 |
29 |
51 |
52 | ## Commands
53 |
54 | ```bash
55 | yarn dx # runs prisma studio + next
56 | yarn build # runs `prisma generate` + `prisma migrate` + `next build`
57 | yarn test-dev # runs e2e tests on dev
58 | yarn test-start # runs e2e tests on `next start` - build required before
59 | yarn dev-nuke # resets local db
60 | ```
61 |
62 | ## ℹ️ How to switch from SQLite to Postgres
63 |
64 | How to switch to postgres
65 |
66 | - Remove migrations: `rm -rf ./prisma/migrations`
67 | - Update: `./prisma/schema.prisma` (see commented code)
68 |
69 | ---
70 |
71 | Created by [@alexdotjs](https://twitter.com/alexdotjs).
72 |
--------------------------------------------------------------------------------
/apps/nextjs/jest-playwright.config.js:
--------------------------------------------------------------------------------
1 | // https://github.com/playwright-community/jest-playwright/#configuration
2 | module.exports = {
3 | browsers: ['chromium', 'firefox', 'webkit'],
4 | exitOnPageError: false, // GitHub currently throws errors
5 | launchOptions: {
6 | headless: true,
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/apps/nextjs/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | verbose: true,
3 | preset: 'jest-playwright-preset',
4 | transform: {
5 | '^.+\\.ts$': 'ts-jest',
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/apps/nextjs/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | // NOTE: This file should not be edited
6 | // see https://nextjs.org/docs/basic-features/typescript for more information.
7 |
--------------------------------------------------------------------------------
/apps/nextjs/next.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @link https://nextjs.org/docs/api-reference/next.config.js/introduction
3 | */
4 | module.exports = {
5 | experimental: {
6 | externalDir: true,
7 | eslint: {
8 | // This allows production builds to successfully complete even if the project has ESLint errors.
9 | ignoreDuringBuilds: true,
10 | },
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/apps/nextjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@zart/nextjs",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "predev": "prisma generate",
7 | "dev": "next dev",
8 | "migrate-dev": "prisma migrate dev",
9 | "migrate": "prisma migrate deploy",
10 | "start": "next start",
11 | "build:1-generate": "prisma generate",
12 | "build:2-migrate": "prisma migrate deploy",
13 | "build:3-build": "next build",
14 | "build": "run-s build:* --print-label",
15 | "studio": "prisma studio",
16 | "test-dev": "start-server-and-test dev 3000 test",
17 | "test-start": "start-server-and-test start 3000 test",
18 | "test": "jest"
19 | },
20 | "dependencies": {
21 | "@prisma/client": "^3.8.1",
22 | "@trpc/client": "^9.6.0",
23 | "@trpc/next": "^9.2.0",
24 | "@trpc/react": "^9.6.0",
25 | "@trpc/server": "^9.6.0",
26 | "clsx": "^1.1.1",
27 | "next": "^12.0.1",
28 | "prisma": "^3.8.1",
29 | "react": "^17.0.2",
30 | "react-dom": "^17.0.2",
31 | "react-query": "^3.23.2",
32 | "start-server-and-test": "^1.12.0",
33 | "superjson": "^1.7.5",
34 | "zod": "^3.0.0"
35 | },
36 | "devDependencies": {
37 | "@types/jest": "^27.0.1",
38 | "@types/node": "^16.0.0",
39 | "@types/react": "^17.0.20",
40 | "jest": "^27.1.0",
41 | "jest-playwright": "^0.0.1",
42 | "jest-playwright-preset": "^1.4.5",
43 | "npm-run-all": "^4.1.5",
44 | "playwright": "^1.14.1",
45 | "prettier": "^2.4.0",
46 | "ts-jest": "^27.0.5",
47 | "typescript": "4.4.2"
48 | },
49 | "publishConfig": {
50 | "access": "restricted"
51 | },
52 | "resolutions": {
53 | "**/react": "17.0.2",
54 | "**/react-dom": "17.0.2"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/apps/nextjs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trpc/zart/4aaed67ce41ffc3d7e4eb1e7582fa98834f074e6/apps/nextjs/public/favicon.ico
--------------------------------------------------------------------------------
/apps/nextjs/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/nextjs/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { httpBatchLink } from '@trpc/client/links/httpBatchLink';
2 | import { loggerLink } from '@trpc/client/links/loggerLink';
3 | import { withTRPC } from '@trpc/next';
4 | import { AppType } from 'next/dist/shared/lib/utils';
5 | import type { AppRouter } from '@zart/api/src/routers/_app';
6 | import { transformer } from 'utils/trpc';
7 |
8 | const MyApp: AppType = ({ Component, pageProps }) => {
9 | return (
10 | <>
11 |
12 | >
13 | );
14 | };
15 |
16 | function getBaseUrl() {
17 | if (process.browser) {
18 | return '';
19 | }
20 | // // reference for vercel.com
21 | if (process.env.VERCEL_URL) {
22 | return `https://${process.env.VERCEL_URL}`;
23 | }
24 |
25 | // // reference for render.com
26 | // if (process.env.RENDER_INTERNAL_HOSTNAME) {
27 | // return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;
28 | // }
29 |
30 | // assume localhost
31 | return `http://localhost:${process.env.PORT ?? 3000}`;
32 | }
33 |
34 | export default withTRPC({
35 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
36 | config() {
37 | const url = `${getBaseUrl()}/api/trpc`;
38 | /**
39 | * If you want to use SSR, you need to use the server's full URL
40 | * @link https://trpc.io/docs/ssr
41 | */
42 | return {
43 | /**
44 | * @link https://trpc.io/docs/links
45 | */
46 | links: [
47 | // adds pretty logs to your console in development and logs errors in production
48 | loggerLink({
49 | enabled: (opts) =>
50 | process.env.NODE_ENV === 'development' ||
51 | (opts.direction === 'down' && opts.result instanceof Error),
52 | }),
53 | httpBatchLink({
54 | url,
55 | }),
56 | ],
57 | /**
58 | * @link https://trpc.io/docs/data-transformers
59 | */
60 | transformer,
61 | /**
62 | * @link https://react-query.tanstack.com/reference/QueryClient
63 | */
64 | // queryClientConfig: { defaultOptions: { queries: { staleTime: 6000 } } },
65 | };
66 | },
67 | /**
68 | * @link https://trpc.io/docs/ssr
69 | */
70 | ssr: true,
71 | /**
72 | * Set headers or status code when doing SSR
73 | */
74 | responseMeta({ clientErrors }) {
75 | if (clientErrors.length) {
76 | // propagate http first error from API calls
77 | return {
78 | status: clientErrors[0].data?.httpStatus ?? 500,
79 | };
80 | }
81 |
82 | // for app caching with SSR see https://trpc.io/docs/caching
83 |
84 | return {};
85 | },
86 | })(MyApp);
87 |
--------------------------------------------------------------------------------
/apps/nextjs/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 { appRouter } from '@zart/api/src/routers/_app';
6 | import { createContext } from '@zart/api/src/createContext';
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 |
--------------------------------------------------------------------------------
/apps/nextjs/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import Link from 'next/link';
3 | import { ReactQueryDevtools } from 'react-query/devtools';
4 | import { trpc } from '../utils/trpc';
5 |
6 | export default function IndexPage() {
7 | const utils = trpc.useContext();
8 | const postsQuery = trpc.useQuery(['post.all']);
9 | const addPost = trpc.useMutation('post.add', {
10 | onSettled() {
11 | return utils.invalidateQuery(['post.all']);
12 | },
13 | });
14 |
15 | // prefetch all posts for instant navigation
16 | // useEffect(() => {
17 | // postsQuery.data?.forEach((post) => {
18 | // utils.prefetchQuery(['post.byId', post.id]);
19 | // });
20 | // }, [postsQuery.data, utils]);
21 |
22 | return (
23 | <>
24 |
25 | Prisma Starter
26 |
27 |
28 | Welcome to your tRPC starter!
29 |
30 | Check the docs whenever you get
31 | stuck, or ping @alexdotjs on
32 | Twitter.
33 |
34 |
35 | Posts
36 | {postsQuery.status === 'loading' && '(loading)'}
37 |
38 | {postsQuery.data?.map((item) => (
39 |
40 | {item.title}
41 |
42 | View more
43 |
44 |
45 | ))}
46 |
47 |
89 |
90 | {process.env.NODE_ENV !== 'production' && (
91 |
92 | )}
93 | >
94 | );
95 | }
96 |
97 | /**
98 | * If you want to statically render this page
99 | * - Export `appRouter` & `createContext` from [trpc].ts
100 | * - Make the `opts` object optional on `createContext()`
101 | *
102 | * @link https://trpc.io/docs/ssg
103 | */
104 | // export const getStaticProps = async (
105 | // context: GetStaticPropsContext<{ filter: string }>,
106 | // ) => {
107 | // const ssg = createSSGHelpers({
108 | // router: appRouter,
109 | // ctx: await createContext(),
110 | // });
111 | //
112 | // await ssg.fetchQuery('post.all');
113 | //
114 | // return {
115 | // props: {
116 | // trpcState: ssg.dehydrate(),
117 | // filter: context.params?.filter ?? 'all',
118 | // },
119 | // revalidate: 1,
120 | // };
121 | // };
122 |
--------------------------------------------------------------------------------
/apps/nextjs/src/pages/post/[id].tsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/dist/client/router';
2 | import { trpc } from 'utils/trpc';
3 | import NextError from 'next/error';
4 |
5 | export default function PostViewPage() {
6 | const id = useRouter().query.id as string;
7 | const postQuery = trpc.useQuery(['post.byId', id]);
8 | if (postQuery.error) {
9 | const statusCode = postQuery.error.data?.httpStatus ?? 500;
10 | return (
11 |
12 | );
13 | }
14 | if (postQuery.status === 'loading') {
15 | return <>Loading...>;
16 | }
17 | return (
18 | <>
19 | {postQuery.data?.title}
20 |
21 | Raw data:
22 | {JSON.stringify(postQuery.data ?? null, null, 4)}
23 | >
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/apps/nextjs/src/utils/trpc.ts:
--------------------------------------------------------------------------------
1 | export * from '@zart/react/trpc';
2 |
--------------------------------------------------------------------------------
/apps/nextjs/test/playwright.test.ts:
--------------------------------------------------------------------------------
1 | jest.setTimeout(35e3);
2 |
3 | test('go to /', async () => {
4 | await page.goto('http://localhost:3000');
5 |
6 | await page.waitForSelector(`text=Starter`);
7 | });
8 |
9 | test('test 404', async () => {
10 | const res = await page.goto('http://localhost:3000/post/not-found');
11 | expect(res?.status()).toBe(404);
12 | });
13 |
14 | export {};
15 |
--------------------------------------------------------------------------------
/apps/nextjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "target": "es5",
6 | "lib": [
7 | "dom",
8 | "dom.iterable",
9 | "esnext"
10 | ],
11 | "allowJs": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "noEmit": true,
14 | "module": "esnext",
15 | "moduleResolution": "node",
16 | "resolveJsonModule": true,
17 | "isolatedModules": true,
18 | "jsx": "preserve",
19 | "incremental": true
20 | },
21 | "include": [
22 | "next-env.d.ts",
23 | "**/*.ts",
24 | "**/*.tsx"
25 | ],
26 | "exclude": [
27 | "node_modules"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | # starts a postgres instance
2 | version: '3.6'
3 | services:
4 | postgres:
5 | image: postgres:13
6 | ports:
7 | - '5466:5432' # expose pg on port 5466 to not collide with pg from elsewhere
8 | restart: always
9 | volumes:
10 | - db_data:/var/lib/postgresql/data
11 | environment:
12 | POSTGRES_PASSWORD: ''
13 | POSTGRES_HOST_AUTH_METHOD: trust
14 | volumes:
15 | db_data:
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zart",
3 | "private": true,
4 | "version": "0.0.1",
5 | "author": "KATT",
6 | "license": "MIT",
7 | "scripts": {
8 | "db-up": "docker compose up -d",
9 | "db-nuke": "docker compose down --volumes --remove-orphans",
10 | "db-migrate-dev": "yarn prisma migrate dev",
11 | "dev:nextjs": "yarn --cwd apps/nextjs dev",
12 | "dev:expo": "yarn --cwd apps/expo dev",
13 | "lint": "eslint --ext \".js,.ts,.tsx\" --ignore-path .gitignore . && manypkg check",
14 | "lint-fix": "yarn lint --fix && manypkg fix",
15 | "dev": "run-s db-up db-migrate-dev && run-p dev:* --print-label",
16 | "clean": "find . -name node_modules -o -name .next -o -name .expo -type d -prune | xargs rm -rf",
17 | "prisma-dev": "prisma migrate dev && prisma generate",
18 | "postinstall": "prisma generate"
19 | },
20 | "workspaces": {
21 | "packages": [
22 | "packages/*",
23 | "apps/*"
24 | ]
25 | },
26 | "prettier": {
27 | "printWidth": 80,
28 | "trailingComma": "all",
29 | "singleQuote": true
30 | },
31 | "dependencies": {
32 | "@manypkg/cli": "^0.18.0",
33 | "@prisma/client": "^3.8.1",
34 | "@typescript-eslint/eslint-plugin": "^4.30.0",
35 | "@typescript-eslint/parser": "^4.31.0",
36 | "eslint": "^7.32.0",
37 | "eslint-config-prettier": "^8.3.0",
38 | "eslint-plugin-prettier": "^4.0.0",
39 | "eslint-plugin-react": "^7.25.1",
40 | "eslint-plugin-react-hooks": "^4.2.0",
41 | "expo-yarn-workspaces": "^1.7.0",
42 | "lerna": "^4.0.0",
43 | "npm-run-all": "^4.1.5",
44 | "prettier": "^2.4.0",
45 | "prisma": "^3.8.1"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@zart/api",
3 | "version": "0.0.1",
4 | "author": "KATT",
5 | "license": "MIT",
6 | "private": true,
7 | "files": [
8 | "src"
9 | ],
10 | "scripts": {},
11 | "dependencies": {},
12 | "devDependencies": {}
13 | }
14 |
--------------------------------------------------------------------------------
/packages/api/src/createContext.ts:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from '@prisma/client';
2 | import * as trpc from '@trpc/server';
3 | import * as trpcNext from '@trpc/server/adapters/next';
4 |
5 | const prisma = new PrismaClient({
6 | log:
7 | process.env.NODE_ENV === 'development'
8 | ? ['query', 'error', 'warn']
9 | : ['error'],
10 | });
11 |
12 | /**
13 | * Creates context for an incoming request
14 | * @link https://trpc.io/docs/context
15 | */
16 | export const createContext = async ({
17 | req,
18 | res,
19 | }: trpcNext.CreateNextContextOptions) => {
20 | // for API-response caching see https://trpc.io/docs/caching
21 | return {
22 | req,
23 | res,
24 | prisma,
25 | };
26 | };
27 |
28 | export type Context = trpc.inferAsyncReturnType;
29 |
--------------------------------------------------------------------------------
/packages/api/src/createRouter.ts:
--------------------------------------------------------------------------------
1 | import * as trpc from '@trpc/server';
2 | import { Context } from './createContext';
3 |
4 | /**
5 | * Helper function to create a router with context
6 | */
7 | export function createRouter() {
8 | return trpc.router();
9 | }
10 |
--------------------------------------------------------------------------------
/packages/api/src/routers/_app.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains the root router of your tRPC-backend
3 | */
4 | import superjson from 'superjson';
5 | import { createRouter } from '../createRouter';
6 | import { postRouter } from './post';
7 |
8 | /**
9 | * Create your application's root router
10 | * If you want to use SSG, you need export this
11 | * @link https://trpc.io/docs/ssg
12 | * @link https://trpc.io/docs/router
13 | */
14 | export const appRouter = createRouter()
15 | /**
16 | * Add data transformers
17 | * @link https://trpc.io/docs/data-transformers
18 | */
19 | .transformer(superjson)
20 | /**
21 | * Optionally do custom error (type safe!) formatting
22 | * @link https://trpc.io/docs/error-formatting
23 | */
24 | // .formatError(({ shape, error }) => { })
25 | .merge('post.', postRouter);
26 |
27 | export type AppRouter = typeof appRouter;
28 |
--------------------------------------------------------------------------------
/packages/api/src/routers/post.ts:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This is an example router, you can delete this file and then update `../pages/api/trpc/[trpc].tsx`
4 | */
5 |
6 | import { createRouter } from '../createRouter';
7 | import { z } from 'zod';
8 | import { TRPCError } from '@trpc/server';
9 |
10 | export const postRouter = createRouter()
11 | // create
12 | .mutation('add', {
13 | input: z.object({
14 | id: z.string().uuid().optional(),
15 | title: z.string().min(1).max(32),
16 | text: z.string().min(1),
17 | }),
18 | async resolve({ ctx, input }) {
19 | const todo = await ctx.prisma.post.create({
20 | data: input,
21 | });
22 | return todo;
23 | },
24 | })
25 | // read
26 | .query('all', {
27 | async resolve({ ctx }) {
28 | /**
29 | * For pagination you can have a look at this docs site
30 | * @link https://trpc.io/docs/useInfiniteQuery
31 | */
32 |
33 | return ctx.prisma.post.findMany({
34 | select: {
35 | id: true,
36 | title: true,
37 | },
38 | });
39 | },
40 | })
41 | .query('byId', {
42 | input: z.string(),
43 | async resolve({ ctx, input }) {
44 | const post = await ctx.prisma.post.findUnique({
45 | where: { id: input },
46 | });
47 | if (!post) {
48 | throw new TRPCError({
49 | code: 'NOT_FOUND',
50 | message: `No post with id '${input}'`,
51 | });
52 | }
53 | return post;
54 | },
55 | })
56 | // update
57 | .mutation('edit', {
58 | input: z.object({
59 | id: z.string().uuid(),
60 | data: z.object({
61 | title: z.string().min(1).max(32).optional(),
62 | text: z.string().min(1).optional(),
63 | }),
64 | }),
65 | async resolve({ ctx, input }) {
66 | const { id, data } = input;
67 | const todo = await ctx.prisma.post.update({
68 | where: { id },
69 | data,
70 | });
71 | return todo;
72 | },
73 | })
74 | // delete
75 | .mutation('delete', {
76 | input: z.string().uuid(),
77 | async resolve({ input: id, ctx }) {
78 | await ctx.prisma.post.delete({ where: { id } });
79 | return id;
80 | },
81 | });
82 |
--------------------------------------------------------------------------------
/packages/react-native/hello-world.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | SafeAreaView,
4 | View,
5 | Text,
6 | StyleSheet,
7 | TouchableOpacity,
8 | } from 'react-native';
9 | import { trpc } from '@zart/react/trpc';
10 | import { FlatList } from 'react-native';
11 |
12 | export function HelloWorld() {
13 | const posts = trpc.useQuery(['post.all'], {
14 | refetchInterval: 3000,
15 | });
16 | return (
17 |
18 | {posts.data ? (
19 | (
21 | {
23 | alert(`You clicked ID ${item.id} `);
24 | }}
25 | >
26 |
27 | {item.title}
28 |
29 |
30 | )}
31 | data={posts.data}
32 | keyExtractor={(item) => item.id}
33 | />
34 | ) : (
35 | {posts.status}
36 | )}
37 |
38 | );
39 | }
40 |
41 | const styles = StyleSheet.create({
42 | container: {
43 | flex: 1,
44 | },
45 | item: {
46 | flex: 1,
47 | backgroundColor: '#f9c2ff',
48 | padding: 20,
49 | marginVertical: 8,
50 | },
51 | });
52 |
--------------------------------------------------------------------------------
/packages/react-native/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@zart/react-native",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {},
6 | "license": "MIT",
7 | "dependencies": {
8 | "@trpc/react": "^9.6.0",
9 | "@trpc/server": "^9.6.0"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/react-native/theme.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/nandorojo/dripsy#custom-theme
2 |
3 | const theme = {};
4 |
5 | export { theme };
6 |
--------------------------------------------------------------------------------
/packages/react-native/trpc.tsx:
--------------------------------------------------------------------------------
1 | import { createReactQueryHooks } from '@trpc/react';
2 | // ℹ️ Type-only import:
3 | // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
4 | import type { AppRouter } from '@zart/api/src/routers/_app';
5 | import type { inferProcedureOutput, inferProcedureInput } from '@trpc/server';
6 | import superjson from 'superjson';
7 | /**
8 | * A set of strongly-typed React hooks from your `AppRouter` type signature with `createReactQueryHooks`.
9 | * @link https://trpc.io/docs/react#3-create-trpc-hooks
10 | */
11 | export const trpc = createReactQueryHooks();
12 |
13 | export const transformer = superjson;
14 |
15 | // export const transformer = superjson;
16 | /**
17 | * This is a helper method to infer the output of a query resolver
18 | * @example type HelloOutput = inferQueryOutput<'hello'>
19 | */
20 | export type inferQueryOutput<
21 | TRouteKey extends keyof AppRouter['_def']['queries'],
22 | > = inferProcedureOutput;
23 |
24 | export type inferQueryInput<
25 | TRouteKey extends keyof AppRouter['_def']['queries'],
26 | > = inferProcedureInput;
27 |
28 | export type inferMutationOutput<
29 | TRouteKey extends keyof AppRouter['_def']['mutations'],
30 | > = inferProcedureOutput;
31 |
32 | export type inferMutationInput<
33 | TRouteKey extends keyof AppRouter['_def']['mutations'],
34 | > = inferProcedureInput;
35 |
36 | export type { AppRouter };
37 |
--------------------------------------------------------------------------------
/packages/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@zart/react",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {},
6 | "license": "MIT",
7 | "dependencies": {}
8 | }
9 |
--------------------------------------------------------------------------------
/packages/react/trpc.tsx:
--------------------------------------------------------------------------------
1 | import { createReactQueryHooks } from '@trpc/react';
2 | // ℹ️ Type-only import:
3 | // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
4 | import type { AppRouter } from '@zart/api/src/routers/_app';
5 | import type { inferProcedureOutput, inferProcedureInput } from '@trpc/server';
6 | import superjson from 'superjson';
7 | /**
8 | * A set of strongly-typed React hooks from your `AppRouter` type signature with `createReactQueryHooks`.
9 | * @link https://trpc.io/docs/react#3-create-trpc-hooks
10 | */
11 | export const trpc = createReactQueryHooks();
12 |
13 | export const transformer = superjson;
14 |
15 | /**
16 | * This is a helper method to infer the output of a query resolver
17 | * @example type HelloOutput = inferQueryOutput<'hello'>
18 | */
19 | export type inferQueryOutput<
20 | TRouteKey extends keyof AppRouter['_def']['queries'],
21 | > = inferProcedureOutput;
22 |
23 | export type inferQueryInput<
24 | TRouteKey extends keyof AppRouter['_def']['queries'],
25 | > = inferProcedureInput;
26 |
27 | export type inferMutationOutput<
28 | TRouteKey extends keyof AppRouter['_def']['mutations'],
29 | > = inferProcedureOutput;
30 |
31 | export type inferMutationInput<
32 | TRouteKey extends keyof AppRouter['_def']['mutations'],
33 | > = inferProcedureInput;
34 |
35 | export type { AppRouter };
36 |
--------------------------------------------------------------------------------
/prisma/migrations/20210910132122_init/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "Post" (
3 | "id" TEXT NOT NULL,
4 | "title" TEXT NOT NULL,
5 | "text" TEXT NOT NULL,
6 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
7 | "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
8 |
9 | PRIMARY KEY ("id")
10 | );
11 |
12 | -- CreateIndex
13 | CREATE UNIQUE INDEX "Post.createdAt_unique" ON "Post"("createdAt");
14 |
15 | -- CreateIndex
16 | CREATE UNIQUE INDEX "Post.updatedAt_unique" ON "Post"("updatedAt");
17 |
--------------------------------------------------------------------------------
/prisma/migrations/20220111230540_prisma_3/migration.sql:
--------------------------------------------------------------------------------
1 | -- RenameIndex
2 | ALTER INDEX "Post.createdAt_unique" RENAME TO "Post_createdAt_key";
3 |
4 | -- RenameIndex
5 | ALTER INDEX "Post.updatedAt_unique" RENAME TO "Post_updatedAt_key";
6 |
--------------------------------------------------------------------------------
/prisma/migrations/20220201105846_drop_unique/migration.sql:
--------------------------------------------------------------------------------
1 | -- DropIndex
2 | DROP INDEX "Post_createdAt_key";
3 |
4 | -- DropIndex
5 | DROP INDEX "Post_updatedAt_key";
6 |
--------------------------------------------------------------------------------
/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"
--------------------------------------------------------------------------------
/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 = "postgres"
6 | url = env("DATABASE_URL")
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())
22 | }
23 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "strictNullChecks": true,
5 | "esModuleInterop": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "noUnusedLocals": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "jsx": "react"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base"
3 | }
4 |
--------------------------------------------------------------------------------