├── apps ├── web │ ├── .env.example │ ├── .eslintrc │ ├── src │ │ ├── vite-env.d.ts │ │ ├── index.css │ │ ├── utils │ │ │ ├── classNames.ts │ │ │ └── trpc.ts │ │ ├── routes │ │ │ ├── __root.tsx │ │ │ └── index.lazy.tsx │ │ ├── main.tsx │ │ ├── App.tsx │ │ ├── components │ │ │ └── TrpcWrapper.tsx │ │ └── routeTree.gen.ts │ ├── public │ │ └── favicon.ico │ ├── tsconfig.node.json │ ├── tailwind.config.ts │ ├── index.html │ ├── vite.config.ts │ ├── tsconfig.json │ └── package.json └── api │ ├── .eslintrc │ ├── src │ ├── index.ts │ ├── router │ │ ├── index.ts │ │ └── hello.ts │ ├── trpc.ts │ └── server.ts │ ├── tsconfig.json │ ├── .gitignore │ └── package.json ├── pnpm-workspace.yaml ├── prettier.config.js ├── packages ├── typescript-config │ ├── package.json │ ├── base.json │ ├── node.json │ └── react.json ├── tailwind-config │ ├── tsconfig.json │ ├── tailwind.config.ts │ └── package.json └── eslint-config │ ├── package.json │ ├── react.js │ └── eslint.js ├── .prettierrc ├── turbo.json ├── .gitignore ├── package.json ├── LICENSE └── README.md /apps/web/.env.example: -------------------------------------------------------------------------------- 1 | VITE_API_URL=http://localhost:3000 -------------------------------------------------------------------------------- /apps/api/.eslintrc: -------------------------------------------------------------------------------- 1 | { "extends": ["@repo/eslint-config/eslint"] } 2 | -------------------------------------------------------------------------------- /apps/web/.eslintrc: -------------------------------------------------------------------------------- 1 | { "extends": ["@repo/eslint-config/react"] } 2 | -------------------------------------------------------------------------------- /apps/web/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | -------------------------------------------------------------------------------- /apps/web/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require('prettier-plugin-tailwindcss')], 3 | }; 4 | -------------------------------------------------------------------------------- /apps/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noahflk/react-trpc-turbo/HEAD/apps/web/public/favicon.ico -------------------------------------------------------------------------------- /apps/api/src/index.ts: -------------------------------------------------------------------------------- 1 | export type { AppRouter } from './router'; 2 | export type { RouterInput, RouterOutput } from './trpc'; 3 | -------------------------------------------------------------------------------- /apps/web/src/utils/classNames.ts: -------------------------------------------------------------------------------- 1 | import { twMerge } from 'tailwind-merge'; 2 | 3 | export const classNames = (...classes: Array) => twMerge(...classes); 4 | -------------------------------------------------------------------------------- /apps/web/src/routes/__root.tsx: -------------------------------------------------------------------------------- 1 | import { createRootRoute, Outlet } from '@tanstack/react-router'; 2 | 3 | export const Route = createRootRoute({ 4 | component: () => , 5 | }); 6 | -------------------------------------------------------------------------------- /apps/web/src/utils/trpc.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCReact } from '@trpc/react-query'; 2 | 3 | import type { AppRouter } from '@repo/api'; 4 | 5 | export const trpc = createTRPCReact(); 6 | -------------------------------------------------------------------------------- /packages/typescript-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/typescript-config", 3 | "version": "1.0.0", 4 | "private": true, 5 | "publishConfig": { 6 | "access": "public" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"], 3 | "semi": true, 4 | "trailingComma": "es5", 5 | "singleQuote": true, 6 | "printWidth": 140, 7 | "tabWidth": 2, 8 | "useTabs": false 9 | } -------------------------------------------------------------------------------- /apps/api/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { router } from '@api/trpc'; 2 | import { helloRouter } from '@api/router/hello'; 3 | 4 | export const appRouter = router({ 5 | hello: helloRouter, 6 | }); 7 | 8 | export type AppRouter = typeof appRouter; 9 | -------------------------------------------------------------------------------- /apps/web/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/typescript-config/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "target": "ES2020", 6 | "lib": ["es2022", "DOM", "DOM.Iterable"], 7 | 8 | 9 | "paths": { 10 | "@repo/api/*": ["./src/*"] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | 3 | import sharedConfig from '@repo/tailwind-config'; 4 | 5 | const config: Pick = { 6 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 7 | presets: [sharedConfig], 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /packages/tailwind-config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/base.json", 3 | "compilerOptions": { 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "allowSyntheticDefaultImports": true, 7 | }, 8 | "include": ["."], 9 | "exclude": ["dist", "build", "node_modules"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/api/src/router/hello.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | import { publicProcedure, router } from '@api/trpc'; 4 | 5 | export const schema = z.object({ 6 | name: z.string(), 7 | }); 8 | 9 | export const helloRouter = router({ 10 | get: publicProcedure.input(schema).query(async ({ input }) => ({ success: true, message: `Hello ${input.name}!` })), 11 | }); 12 | -------------------------------------------------------------------------------- /packages/tailwind-config/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | import forms from '@tailwindcss/forms'; 3 | 4 | const config: Omit = { 5 | theme: { 6 | extend: { 7 | colors: { 8 | primary: '#1B416F', 9 | }, 10 | }, 11 | }, 12 | plugins: [forms({ strategy: 'class' })], 13 | }; 14 | export default config; 15 | -------------------------------------------------------------------------------- /packages/tailwind-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/tailwind-config", 3 | "version": "1.0.0", 4 | "private": true, 5 | "exports": { 6 | ".": "./tailwind.config.ts" 7 | }, 8 | "devDependencies": { 9 | "@repo/typescript-config": "workspace:*", 10 | "tailwindcss": "^3.4.0" 11 | }, 12 | "dependencies": { 13 | "@tailwindcss/forms": "^0.5.7" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "globalDependencies": ["**/.env.*local"], 4 | "tasks": { 5 | "build": { 6 | "dependsOn": ["^build"], 7 | "outputs": ["dist/**"] 8 | }, 9 | "lint": { 10 | "dependsOn": ["^lint"] 11 | }, 12 | "dev": { 13 | "cache": false, 14 | "persistent": true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/web/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | 4 | import { App } from './App'; 5 | 6 | const rootElement = document.getElementById('root')!; 7 | 8 | if (!rootElement.innerHTML) { 9 | const root = ReactDOM.createRoot(rootElement); 10 | root.render( 11 | 12 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/src/routes/index.lazy.tsx: -------------------------------------------------------------------------------- 1 | import { createLazyFileRoute } from '@tanstack/react-router'; 2 | 3 | import { trpc } from '@/utils/trpc'; 4 | 5 | export const Route = createLazyFileRoute('/')({ 6 | component: Index, 7 | }); 8 | 9 | function Index() { 10 | const query = trpc.hello.get.useQuery({ name: 'Jonas' }); 11 | 12 | return

Message: {query.data?.message}

; 13 | } 14 | -------------------------------------------------------------------------------- /packages/typescript-config/node.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./base.json", 4 | "compilerOptions": { 5 | "lib": ["esnext", "dom"], 6 | "target": "esnext", 7 | "esModuleInterop": true, 8 | "strict": true, 9 | "moduleResolution": "node", 10 | "noEmit": true, 11 | "skipLibCheck": true, 12 | 13 | "outDir": "dist" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | 6 | "baseUrl": ".", 7 | "paths": { 8 | "@api/*": ["./src/*"] 9 | }, 10 | "rootDir": "./src" 11 | }, 12 | "tsc-alias": { 13 | "resolveFullPaths": true, 14 | "verbose": false 15 | }, 16 | "include": ["src"], 17 | "exclude": ["node_modules"] 18 | } -------------------------------------------------------------------------------- /apps/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Monorepo Starter 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/web/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react-swc'; 3 | import { TanStackRouterVite } from '@tanstack/router-vite-plugin'; 4 | import tailwindcss from 'tailwindcss'; 5 | import tsconfigPaths from 'vite-tsconfig-paths'; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [react(), TanStackRouterVite(), tsconfigPaths()], 10 | css: { 11 | postcss: { 12 | plugins: [tailwindcss()], 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /apps/web/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { RouterProvider, createRouter } from '@tanstack/react-router'; 2 | 3 | import { TrpcWrapper } from './components/TrpcWrapper'; 4 | import './index.css'; 5 | import { routeTree } from './routeTree.gen'; 6 | 7 | const router = createRouter({ routeTree }); 8 | 9 | declare module '@tanstack/react-router' { 10 | interface Register { 11 | router: typeof router; 12 | } 13 | } 14 | 15 | export function App() { 16 | return ( 17 | 18 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /.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 | # Local env files 9 | .env 10 | .env.local 11 | .env.development.local 12 | .env.test.local 13 | .env.production.local 14 | 15 | # Testing 16 | coverage 17 | 18 | # Turbo 19 | .turbo 20 | 21 | # Vercel 22 | .vercel 23 | 24 | # Build Outputs 25 | .next/ 26 | out/ 27 | build 28 | dist 29 | 30 | 31 | # Debug 32 | npm-debug.log* 33 | yarn-debug.log* 34 | yarn-error.log* 35 | 36 | # Misc 37 | .DS_Store 38 | *.pem 39 | .eslintcache -------------------------------------------------------------------------------- /packages/eslint-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/eslint-config", 3 | "version": "1.0.0", 4 | "private": true, 5 | "devDependencies": { 6 | "@typescript-eslint/eslint-plugin": "^6.17.0", 7 | "@typescript-eslint/parser": "^6.17.0", 8 | "eslint-config-prettier": "^9.1.0", 9 | "eslint-config-turbo": "^1.11.3", 10 | "eslint-plugin-import": "^2.29.1", 11 | "eslint-plugin-jsx-a11y": "^6.8.0", 12 | "eslint-plugin-only-warn": "^1.1.0", 13 | "eslint-plugin-react": "^7.34.1", 14 | "eslint-plugin-unicorn": "^52.0.0", 15 | "typescript": "^5.3.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/typescript-config/react.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "module": "EsNext", 7 | "jsx": "react-jsx", 8 | 9 | 10 | "declaration": true, 11 | "declarationMap": true, 12 | "esModuleInterop": true, 13 | "incremental": false, 14 | "isolatedModules": true, 15 | "moduleDetection": "force", 16 | "resolveJsonModule": true, 17 | "skipLibCheck": true, 18 | 19 | "strict": true, 20 | "noImplicitAny": true, 21 | "noUnusedParameters": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/api/src/trpc.ts: -------------------------------------------------------------------------------- 1 | import { initTRPC, type inferRouterInputs, type inferRouterOutputs } from '@trpc/server'; 2 | import type { Request, Response } from 'express'; 3 | import superjson from 'superjson'; 4 | 5 | import type { AppRouter } from '@api/router'; 6 | 7 | type Context = { 8 | req: Request; 9 | res: Response; 10 | }; 11 | 12 | const trpc = initTRPC.context().create({ 13 | transformer: superjson, 14 | }); 15 | 16 | export const publicProcedure = trpc.procedure; 17 | export const router = trpc.router; 18 | 19 | export type RouterInput = inferRouterInputs; 20 | export type RouterOutput = inferRouterOutputs; 21 | -------------------------------------------------------------------------------- /packages/eslint-config/react.js: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | extends: ['./eslint', 'plugin:react/recommended', 'plugin:jsx-a11y/recommended'], 4 | plugins: ['jsx-a11y'], 5 | globals: { 6 | React: true, 7 | JSX: true, 8 | }, 9 | rules: { 10 | 'react/react-in-jsx-scope': 'off', 11 | 'prefer-template': 'off', 12 | 'react/prop-types': 'off', 13 | 'jsx-a11y/anchor-is-valid': 'off', 14 | }, 15 | env: { 16 | browser: true, 17 | }, 18 | settings: { react: { version: '18.2.0' } }, 19 | overrides: [ 20 | // Force ESLint to detect .tsx files 21 | { files: ['*.js?(x)', '*.ts?(x)'] }, 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /apps/api/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://docs.npmjs.com/cli/shrinkwrap#caveats 27 | node_modules 28 | 29 | # Debug log from npm 30 | npm-debug.log 31 | 32 | .DS_Store 33 | 34 | .env -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-trpc-turbo", 3 | "version": "1.0.0", 4 | "private": true, 5 | "packageManager": "pnpm@8.9.0", 6 | "engines": { 7 | "node": ">=18" 8 | }, 9 | "scripts": { 10 | "build": "turbo build", 11 | "dev": "turbo dev", 12 | "lint": "turbo lint", 13 | "format": "prettier --write \"**/*.{ts,tsx,md}\"", 14 | "start:backend": "node apps/api/dist/server.js" 15 | }, 16 | "devDependencies": { 17 | "@repo/eslint-config": "workspace:*", 18 | "@repo/tailwind-config": "workspace:*", 19 | "@repo/typescript-config": "workspace:*", 20 | "prettier": "^3.3.3", 21 | "prettier-plugin-tailwindcss": "^0.6.5", 22 | "turbo": "latest" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/react.json", 3 | "compilerOptions": { 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "module": "ESNext", 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | // Custom settings 18 | "baseUrl": ".", 19 | "paths": { 20 | "@/*": ["./src/*"], 21 | "@api/*": ["../api/src/*"] 22 | } 23 | }, 24 | "include": ["src"], 25 | "references": [{ "path": "./tsconfig.node.json" }] 26 | } 27 | -------------------------------------------------------------------------------- /apps/web/src/components/TrpcWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; 2 | import { httpBatchLink } from '@trpc/client'; 3 | import { useState } from 'react'; 4 | import superjson from 'superjson'; 5 | 6 | import { trpc } from '@/utils/trpc'; 7 | 8 | export function TrpcWrapper({ children }: { children: React.ReactNode }) { 9 | const [queryClient] = useState(() => new QueryClient()); 10 | const [trpcClient] = useState(() => 11 | trpc.createClient({ 12 | links: [ 13 | httpBatchLink({ 14 | url: import.meta.env.VITE_API_URL + '/trpc', 15 | transformer: superjson, 16 | }), 17 | ], 18 | }) 19 | ); 20 | 21 | return ( 22 | 23 | {children} 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /apps/api/src/server.ts: -------------------------------------------------------------------------------- 1 | import { createExpressMiddleware } from '@trpc/server/adapters/express'; 2 | import cors from 'cors'; 3 | import 'dotenv/config'; 4 | import express from 'express'; 5 | 6 | import { appRouter } from '@api/router'; 7 | 8 | 9 | async function main() { 10 | const port = process.env.PORT || 3000; 11 | 12 | const app = express(); 13 | 14 | app.use(cors()); 15 | 16 | 17 | app.use( 18 | '/trpc', 19 | createExpressMiddleware({ 20 | router: appRouter, 21 | createContext: (ctx) => ({ req: ctx.req, res: ctx.res }), 22 | onError: 23 | process.env.NODE_ENV === 'development' 24 | ? ({ path, error }) => { 25 | console.error(`❌ tRPC failed on ${path ?? ''}: ${error.message}`); 26 | } 27 | : undefined, 28 | }) 29 | ); 30 | 31 | 32 | // For testing purposes, wait-on requests '/' 33 | app.get('/', (req, res) => res.send('Server is running!')); 34 | 35 | app.listen(port, () => { 36 | console.log(`App listening on port: ${port}`); 37 | }); 38 | } 39 | 40 | void main(); 41 | -------------------------------------------------------------------------------- /apps/web/src/routeTree.gen.ts: -------------------------------------------------------------------------------- 1 | /* prettier-ignore-start */ 2 | 3 | /* eslint-disable */ 4 | 5 | // @ts-nocheck 6 | 7 | // noinspection JSUnusedGlobalSymbols 8 | 9 | // This file is auto-generated by TanStack Router 10 | 11 | import { createFileRoute } from '@tanstack/react-router' 12 | 13 | // Import Routes 14 | 15 | import { Route as rootRoute } from './routes/__root' 16 | 17 | // Create Virtual Routes 18 | 19 | const IndexLazyImport = createFileRoute('/')() 20 | 21 | // Create/Update Routes 22 | 23 | const IndexLazyRoute = IndexLazyImport.update({ 24 | path: '/', 25 | getParentRoute: () => rootRoute, 26 | } as any).lazy(() => import('./routes/index.lazy').then((d) => d.Route)) 27 | 28 | // Populate the FileRoutesByPath interface 29 | 30 | declare module '@tanstack/react-router' { 31 | interface FileRoutesByPath { 32 | '/': { 33 | preLoaderRoute: typeof IndexLazyImport 34 | parentRoute: typeof rootRoute 35 | } 36 | } 37 | } 38 | 39 | // Create and export the route tree 40 | 41 | export const routeTree = rootRoute.addChildren([IndexLazyRoute]) 42 | 43 | /* prettier-ignore-end */ 44 | -------------------------------------------------------------------------------- /apps/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/api", 3 | "private": true, 4 | "type": "module", 5 | "exports": { 6 | ".": "./src/index.ts" 7 | }, 8 | "scripts": { 9 | "dev": "NODE_ENV=development tsx watch src/server", 10 | "lint": "eslint --cache --ext \".js,.ts,.tsx\" src", 11 | "type-check": "tsc", 12 | "build": "rimraf dist && tsc --noEmit false && tsc-alias", 13 | "start": "node dist/server.js" 14 | }, 15 | "dependencies": { 16 | "@trpc/server": "11.0.0-next-beta.289", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.4.5", 19 | "express": "^4.17.1", 20 | "jsonwebtoken": "^9.0.2", 21 | "superjson": "^2.2.1", 22 | "zod": "^3.0.0" 23 | }, 24 | "devDependencies": { 25 | "@repo/eslint-config": "workspace:*", 26 | "@repo/typescript-config": "workspace:*", 27 | "@types/cors": "^2.8.17", 28 | "@types/express": "^4.17.17", 29 | "@types/jsonwebtoken": "^9.0.6", 30 | "@types/node": "^20.10.0", 31 | "eslint": "^8.56.0", 32 | "rimraf": "^5.0.5", 33 | "tsc-alias": "^1.8.8", 34 | "tsx": "^4.0.0", 35 | "typescript": "^5.3.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Noah Falk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/eslint-config/eslint.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('node:path'); 2 | 3 | const project = resolve(process.cwd(), 'tsconfig.json'); 4 | 5 | /** @type {import("eslint").Linter.Config} */ 6 | module.exports = { 7 | parser: '@typescript-eslint/parser', 8 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 9 | plugins: ['@typescript-eslint', 'import', 'unicorn'], 10 | rules: { 11 | '@typescript-eslint/no-unused-vars': 'error', 12 | '@typescript-eslint/no-explicit-any': 'error', 13 | 'import/first': 'error', 14 | 'import/newline-after-import': 'error', 15 | 'arrow-body-style': ['error', 'as-needed'], 16 | 'no-return-await': ['error'], 17 | 'object-shorthand': ['error'], 18 | 'no-unneeded-ternary': ['error'], 19 | '@typescript-eslint/no-namespace': 'off', 20 | 'prefer-template': ['error'], 21 | '@typescript-eslint/consistent-type-imports': ['error', { fixStyle: 'inline-type-imports' }], 22 | 'no-empty': ['error', { allowEmptyCatch: true }], 23 | }, 24 | settings: { 25 | 'import/resolver': { 26 | typescript: { 27 | project, 28 | }, 29 | }, 30 | }, 31 | ignorePatterns: [ 32 | // Ignore dotfiles 33 | '.*.js', 34 | 'node_modules/', 35 | 'dist/', 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /apps/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/web", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "tsc && vite build", 8 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@tanstack/react-query": "^5.20.5", 13 | "@tanstack/react-router": "^1.16.2", 14 | "@trpc/client": "11.0.0-next-beta.289", 15 | "@trpc/react-query": "11.0.0-next-beta.289", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "superjson": "^2.2.1", 19 | "zod": "^3.0.0" 20 | }, 21 | "devDependencies": { 22 | "@repo/api": "workspace:*", 23 | "@repo/tailwind-config": "workspace:*", 24 | "@repo/typescript-config": "workspace:*", 25 | "@tanstack/router-vite-plugin": "^1.16.3", 26 | "@types/react": "^18.2.55", 27 | "@types/react-dom": "^18.2.19", 28 | "@vitejs/plugin-react-swc": "^3.5.0", 29 | "autoprefixer": "^10.4.17", 30 | "eslint": "^8.56.0", 31 | "eslint-plugin-react-hooks": "^4.6.0", 32 | "eslint-plugin-react-refresh": "^0.4.5", 33 | "postcss": "^8.4.35", 34 | "tailwind-merge": "^2.2.1", 35 | "tailwindcss": "^3.4.0", 36 | "typescript": "^5.2.2", 37 | "vite": "^5.1.0", 38 | "vite-tsconfig-paths": "^4.3.2" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-trpc-turbo 2 | 3 | ## Technologies used 4 | 5 | - Turborepo 6 | - React Vite 7 | - Express.js 8 | - tRPC 9 | - TanStack Router 10 | - Tailwind CSS 11 | 12 | ### Apps and Packages 13 | 14 | - `@repo/web`: Vite, React, TanStack Router and tRPC Client 15 | - `@repo/api`: Express.js, Drizzle and tRPC Server 16 | - `@repo/eslint-config`: `eslint` configurations 17 | - `@repo/typescript-config`: `tsconfig.json`s used throughout the monorepo 18 | - `@repo/tailwind-config`: shared Tailwind configuration 19 | 20 | Each package/app is 100% [TypeScript](https://www.typescriptlang.org/). 21 | 22 | ### Utilities 23 | 24 | This Turborepo has some additional tools already setup for you: 25 | 26 | - [TypeScript](https://www.typescriptlang.org/) for static type checking 27 | - [ESLint](https://eslint.org/) for code linting 28 | - [Prettier](https://prettier.io) for code formatting 29 | 30 | ## Setup 31 | 32 | To get started, clone the repository and install the dependencies: 33 | 34 | ``` 35 | pnpm install 36 | ``` 37 | 38 | Then, copy the `.env.example` file to `.env` in the web/ folder and fill in the necessary environment variables. For local development, the defaul value will work. If you want to deploy the app, you will need to specify where the backend is hosted. 39 | 40 | ``` 41 | cp ./apps/web/.env.example ./apps/web/.env 42 | ``` 43 | 44 | ### Build 45 | 46 | To build all apps and packages, run the following command: 47 | 48 | ``` 49 | pnpm build 50 | ``` 51 | 52 | ### Develop 53 | 54 | To run all apps and packages in development mode, run the following command: 55 | 56 | ``` 57 | pnpm dev 58 | ``` 59 | --------------------------------------------------------------------------------