Skip to content

Commit

Permalink
feat: integrate trpc
Browse files Browse the repository at this point in the history
  • Loading branch information
CaliCastle committed Apr 2, 2023
1 parent e8a46f8 commit ad06a94
Show file tree
Hide file tree
Showing 21 changed files with 807 additions and 74 deletions.
39 changes: 39 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path')

/** @type {import("eslint").Linter.Config} */
const config = {
overrides: [
{
extends: [
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
files: ['*.ts', '*.tsx'],
parserOptions: {
project: path.join(__dirname, 'tsconfig.json'),
},
},
],
parser: '@typescript-eslint/parser',
parserOptions: {
project: path.join(__dirname, 'tsconfig.json'),
},
plugins: ['@typescript-eslint', 'simple-import-sort', 'unused-imports'],
extends: ['next/core-web-vitals', 'plugin:@typescript-eslint/recommended'],
rules: {
'@typescript-eslint/consistent-type-imports': [
'warn',
{
prefer: 'type-imports',
fixStyle: 'inline-type-imports',
},
],
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],

'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'warn',
'unused-imports/no-unused-imports': 'error',
},
}

module.exports = config
15 changes: 0 additions & 15 deletions .eslintrc.json

This file was deleted.

3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local
.env
.env

# vercel
.vercel
Expand Down
6 changes: 5 additions & 1 deletion components/Game.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
'use client'

import { useAuth } from '@clerk/nextjs'
import React from 'react'

import { SplashScreen } from '~/components/Splash'
import { api } from '~/utils/api'

export function Game() {
const GameComponent: React.FC = () => {
const { isLoaded, isSignedIn } = useAuth()

if (!isLoaded || !isSignedIn) {
Expand All @@ -20,3 +22,5 @@ export function Game() {
</main>
)
}

export const Game = api.withTRPC(GameComponent)
83 changes: 83 additions & 0 deletions env.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { z } from 'zod'

/**
* Specify server-side environment variables schema here.
*/
const server = z.object({
DATABASE_URL: z.string().url(),
NODE_ENV: z.enum(['development', 'test', 'production']),
CLERK_SECRET_KEY: z.string().min(1),
// DISCORD_CLIENT_ID: z.string().min(1),
// DISCORD_CLIENT_SECRET: z.string().min(1),
// GITHUB_ID: z.string().min(1),
// GITHUB_SECRET: z.string().min(1),
// GOOGLE_CLIENT_ID: z.string().min(1),
// GOOGLE_CLIENT_SECRET: z.string().min(1),
})

/**
* To expose them to the client, prefix them with `NEXT_PUBLIC_`.
*/
const client = z.object({
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
})

/**
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
* middlewares) or client-side so we need to destruct manually.
*
* @type {Record<keyof z.infer<typeof server> | keyof z.infer<typeof client>, string | undefined>}
*/
const processEnv = {
DATABASE_URL: process.env.DATABASE_URL,
NODE_ENV: process.env.NODE_ENV,
CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY,
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:
process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
}

// Don't touch the part below
// --------------------------

const merged = server.merge(client)

/** @typedef {z.input<typeof merged>} MergedInput */
/** @typedef {z.infer<typeof merged>} MergedOutput */
/** @typedef {z.SafeParseReturnType<MergedInput, MergedOutput>} MergedSafeParseReturn */

let env = /** @type {MergedOutput} */ (process.env)

if (!!process.env.SKIP_ENV_VALIDATION == false) {
const isServer = typeof window === 'undefined'

const parsed = /** @type {MergedSafeParseReturn} */ (
isServer
? merged.safeParse(processEnv) // on server we can validate all env vars
: client.safeParse(processEnv) // on client we can only validate the ones that are exposed
)

if (parsed.success === false) {
console.error(
'❌ Invalid environment variables:',
parsed.error.flatten().fieldErrors
)
throw new Error('Invalid environment variables')
}

env = new Proxy(parsed.data, {
get(target, prop) {
if (typeof prop !== 'string') return undefined
// Throw a descriptive error if a server-side env var is accessed on the client
// Otherwise it would just be returning `undefined` and be annoying to debug
if (!isServer && !prop.startsWith('NEXT_PUBLIC_'))
throw new Error(
process.env.NODE_ENV === 'production'
? '❌ Attempted to access a server-side environment variable on the client'
: `❌ Attempted to access server-side environment variable '${prop}' on the client`
)
return target[/** @type {keyof typeof target} */ (prop)]
},
})
}

export { env }
6 changes: 3 additions & 3 deletions middleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { withClerkMiddleware } from '@clerk/nextjs/server'
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
import { createIntlMiddleware } from 'next-intl/server'

import { i18n } from '~/i18n'
Expand All @@ -10,7 +11,7 @@ const intlMiddleware = createIntlMiddleware({
})

export default withClerkMiddleware((req: NextRequest) => {
return intlMiddleware(req)
return NextResponse.next(intlMiddleware(req))
})

// Stop Middleware running on static files and public folder
Expand All @@ -24,7 +25,6 @@ export const config = {
* - favicon.ico (favicon file)
* - public folder
*/
'/((?!assets|static|.*\\..*|_next|favicon.ico).*)',
'/',
'/((?!_next/static|_next/image|favicon.ico|assets).*)',
],
}
8 changes: 0 additions & 8 deletions next.config.js

This file was deleted.

15 changes: 15 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
* This is especially useful for Docker builds.
*/
!process.env.SKIP_ENV_VALIDATION && (await import('./env.mjs'))

/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
experimental: {
appDir: true,
},
}

export default nextConfig
28 changes: 21 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,49 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"postinstall": "prisma generate",
"lint": "next lint"
},
"dependencies": {
"@clerk/nextjs": "^4.13.0",
"@types/node": "18.15.11",
"@types/react": "18.0.31",
"@types/react-dom": "18.0.11",
"@prisma/client": "^4.12.0",
"@tanstack/react-query": "^4.28.0",
"@trpc/client": "^10.18.0",
"@trpc/next": "^10.18.0",
"@trpc/react-query": "^10.18.0",
"@trpc/server": "^10.18.0",
"@zolplay/react": "^0.5.1",
"@zolplay/utils": "^1.3.4",
"eslint": "8.37.0",
"eslint-config-next": "13.2.4",
"framer-motion": "^10.10.0",
"next": "13.2.4",
"next-intl": "^2.12.0",
"next-themes": "^0.2.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "5.0.3",
"zod": "^3.21.4"
"superjson": "^1.12.2",
"zod": "^3.21.4",
"zustand": "^4.3.7"
},
"devDependencies": {
"@types/eslint": "^8.37.0",
"@types/node": "18.15.11",
"@types/prettier": "^2.7.2",
"@types/react": "18.0.31",
"@types/react-dom": "18.0.11",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"autoprefixer": "^10.4.14",
"eslint": "8.37.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"postcss": "^8.4.21",
"prettier": "^2.8.7",
"prettier-plugin-packagejson": "^2.4.3",
"prettier-plugin-tailwindcss": "^0.2.6",
"tailwindcss": "^3.3.1"
"prisma": "^4.12.0",
"tailwindcss": "^3.3.1",
"typescript": "^5.0.3"
}
}
19 changes: 19 additions & 0 deletions pages/api/trpc/[trpc].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createNextApiHandler } from '@trpc/server/adapters/next'

import { env } from '~/env.mjs'
import { appRouter } from '~/server/api/root'
import { createTRPCContext } from '~/server/api/trpc'

// export API handler
export default createNextApiHandler({
router: appRouter,
createContext: createTRPCContext,
onError:
env.NODE_ENV === 'development'
? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? '<no-path>'}: ${error.message}`
)
}
: undefined,
})
Loading

0 comments on commit ad06a94

Please sign in to comment.