mirror of
https://github.com/ArthurDanjou/trpc-nuxt.git
synced 2026-01-14 12:14:40 +01:00
make trpc-nuxt flexible by exporting server and client functions instead of nuxt modules
This commit is contained in:
2
client.d.ts
vendored
2
client.d.ts
vendored
@@ -1 +1 @@
|
||||
export * from './dist/runtime/client'
|
||||
export * from './dist/client'
|
||||
|
||||
41
package.json
41
package.json
@@ -1,24 +1,22 @@
|
||||
{
|
||||
"name": "trpc-nuxt",
|
||||
"type": "module",
|
||||
"version": "0.3.1",
|
||||
"packageManager": "pnpm@7.5.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/module.cjs",
|
||||
"types": "./dist/types.d.ts",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./dist/module.mjs",
|
||||
"require": "./dist/module.cjs"
|
||||
},
|
||||
"./server": {
|
||||
"import": "./dist/runtime/server.mjs",
|
||||
"types": "./dist/runtime/server.d.ts"
|
||||
"require": "./dist/index.js",
|
||||
"import": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./client": {
|
||||
"import": "./dist/runtime/client.mjs",
|
||||
"types": "./dist/runtime/client.d.ts"
|
||||
"require": "./dist/client.js",
|
||||
"import": "./dist/client.mjs",
|
||||
"types": "./dist/client.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
@@ -27,40 +25,33 @@
|
||||
],
|
||||
"scripts": {
|
||||
"prepublishOnly": "nr build",
|
||||
"build": "nuxt-module-build",
|
||||
"play": "nr build && nuxi dev playground",
|
||||
"build:playground": "nuxi build playground",
|
||||
"build": "tsup",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"release": "bumpp --commit --push --tag && npm publish",
|
||||
"prepare": "nuxi prepare playground"
|
||||
"release": "bumpp --commit --push --tag && npm publish"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trpc/client": "^10.0.0-proxy-beta.21",
|
||||
"@trpc/server": "^10.0.0-proxy-beta.21"
|
||||
"@trpc/server": "^10.0.0-proxy-beta.21",
|
||||
"nuxt": "^3.0.0-rc.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/kit": "3.0.0-rc.12",
|
||||
"dedent": "^0.7.0",
|
||||
"defu": "^6.1.0",
|
||||
"h3": "^0.8.5",
|
||||
"ohash": "^0.1.5",
|
||||
"pathe": "^0.3.9",
|
||||
"ohmyfetch": "^0.4.20",
|
||||
"ufo": "^0.8.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^0.23.1",
|
||||
"@antfu/ni": "^0.16.2",
|
||||
"@nuxt/module-builder": "^0.2.0",
|
||||
"@trpc/client": "10.0.0-rc.1",
|
||||
"@trpc/server": "10.0.0-rc.1",
|
||||
"@types/dedent": "^0.7.0",
|
||||
"bumpp": "^7.2.0",
|
||||
"eslint": "^8.25.0",
|
||||
"nuxt": "3.0.0-rc.12",
|
||||
"pnpm": "^7.5.0",
|
||||
"trpc-nuxt": "workspace:*",
|
||||
"zod": "^3.19.1"
|
||||
"tsup": "^6.3.0",
|
||||
"typescript": "4.5.4"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "@antfu",
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import Module from '../src/module'
|
||||
|
||||
// https://v3.nuxtjs.org/api/configuration/nuxt.config
|
||||
export default defineNuxtConfig({
|
||||
modules: [Module],
|
||||
runtimeConfig: {
|
||||
baseURL: '',
|
||||
},
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
{
|
||||
"name": "playground",
|
||||
"private": true
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"trpc-nuxt": "workspace:*",
|
||||
"zod": "^3.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nuxt": "3.0.0-rc.12"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const { $client } = useNuxtApp()
|
||||
// const headers = useClientHeaders()
|
||||
// const { data: todos, pending, error, refresh } = await useAsyncQuery(['getTodos'])
|
||||
|
||||
// const addHeader = () => {
|
||||
// headers.value.authorization = 'Bearer abcdefghijklmnop'
|
||||
@@ -13,7 +12,7 @@ const addTodo = async () => {
|
||||
|
||||
try {
|
||||
const result = await $client.todo.addTodo.mutate({
|
||||
id: 69,
|
||||
id: Date.now(),
|
||||
userId: 69,
|
||||
title,
|
||||
completed: false,
|
||||
@@ -26,7 +25,7 @@ const addTodo = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const { data: todos, pending, error, refresh } = await $client.getTodos.query()
|
||||
const { data: todos, pending, error, refresh } = await $client.todo.getTodos.query()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { httpBatchLink } from '@trpc/client'
|
||||
import { createTRPCNuxtProxyClient } from 'trpc-nuxt/client'
|
||||
import type { AppRouter } from '~~/server/trpc'
|
||||
import type { AppRouter } from '~~/server/trpc/routers'
|
||||
|
||||
export default defineNuxtPlugin(() => {
|
||||
const client = createTRPCNuxtProxyClient<AppRouter>({
|
||||
links: [
|
||||
httpBatchLink({
|
||||
url: 'http://localhost:3000/trpc',
|
||||
url: 'http://localhost:3000/api/trpc',
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
12
playground/server/api/trpc/[trpc].ts
Normal file
12
playground/server/api/trpc/[trpc].ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createNuxtApiHandler } from 'trpc-nuxt'
|
||||
import { appRouter } from '../../trpc/routers'
|
||||
|
||||
export default createNuxtApiHandler({
|
||||
router: appRouter,
|
||||
createContext: async () => {
|
||||
return {}
|
||||
},
|
||||
onError({ error }) {
|
||||
console.log('Error', error)
|
||||
},
|
||||
})
|
||||
@@ -1,69 +1,25 @@
|
||||
import { initTRPC } from '@trpc/server'
|
||||
import { z } from 'zod'
|
||||
import type { H3Event } from 'h3'
|
||||
import type { inferAsyncReturnType } from '@trpc/server'
|
||||
|
||||
const baseURL = 'https://jsonplaceholder.typicode.com'
|
||||
const t = initTRPC.context<any>().create()
|
||||
|
||||
const TodoShape = z.object({
|
||||
userId: z.number(),
|
||||
id: z.number(),
|
||||
title: z.string(),
|
||||
completed: z.boolean(),
|
||||
})
|
||||
|
||||
export type Todo = z.infer<typeof TodoShape>
|
||||
|
||||
const t = initTRPC.context<Context>().create()
|
||||
|
||||
// We explicitly export the methods we use here
|
||||
// This allows us to create reusable & protected base procedures
|
||||
export const middleware = t.middleware
|
||||
/**
|
||||
* Create a router
|
||||
* @see https://trpc.io/docs/v10/router
|
||||
*/
|
||||
export const router = t.router
|
||||
|
||||
/**
|
||||
* Create an unprotected procedure
|
||||
* @see https://trpc.io/docs/v10/procedures
|
||||
**/
|
||||
export const publicProcedure = t.procedure
|
||||
|
||||
const anotherRouter = router({
|
||||
getTodo: publicProcedure
|
||||
.input(z.number())
|
||||
.query((req) => {
|
||||
return $fetch<Todo>(`${baseURL}/todos/${req.input}`)
|
||||
}),
|
||||
addTodo: publicProcedure
|
||||
.input(TodoShape)
|
||||
.mutation((req) => {
|
||||
return $fetch<Todo>(`${baseURL}/todos`, {
|
||||
method: 'POST',
|
||||
body: req.input,
|
||||
})
|
||||
}),
|
||||
})
|
||||
/**
|
||||
* @see https://trpc.io/docs/v10/middlewares
|
||||
*/
|
||||
export const middleware = t.middleware
|
||||
|
||||
export const appRouter = router({
|
||||
todo: anotherRouter,
|
||||
getTodos: publicProcedure.query(() => {
|
||||
return $fetch<Todo[]>(`${baseURL}/todos`)
|
||||
}),
|
||||
getTodo: publicProcedure
|
||||
.input(z.number())
|
||||
.query((req) => {
|
||||
console.log('REQ', req)
|
||||
return $fetch<Todo>(`${baseURL}/todos/${req.input}`)
|
||||
}),
|
||||
})
|
||||
|
||||
export type AppRouter = typeof appRouter
|
||||
|
||||
export async function createContext(event: H3Event) {
|
||||
// Create your context based on the request object
|
||||
// Will be available as `ctx` in all your resolvers
|
||||
|
||||
// This is just an example of something you'd might want to do in your ctx fn
|
||||
// const x = useCookies(event)
|
||||
console.log(event.req.headers)
|
||||
|
||||
return {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
type Context = inferAsyncReturnType<typeof createContext>
|
||||
/**
|
||||
* @see https://trpc.io/docs/v10/merging-routers
|
||||
*/
|
||||
export const mergeRouters = t.mergeRouters
|
||||
|
||||
10
playground/server/trpc/routers/index.ts
Normal file
10
playground/server/trpc/routers/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { router } from '..'
|
||||
import { todoRouter } from './todo'
|
||||
import { userRouter } from './user'
|
||||
|
||||
export const appRouter = router({
|
||||
todo: todoRouter,
|
||||
user: userRouter,
|
||||
})
|
||||
|
||||
export type AppRouter = typeof appRouter
|
||||
33
playground/server/trpc/routers/todo.ts
Normal file
33
playground/server/trpc/routers/todo.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { z } from 'zod'
|
||||
import { publicProcedure, router } from '..'
|
||||
|
||||
const baseURL = 'https://jsonplaceholder.typicode.com'
|
||||
|
||||
const TodoShape = z.object({
|
||||
userId: z.number(),
|
||||
id: z.number(),
|
||||
title: z.string(),
|
||||
completed: z.boolean(),
|
||||
})
|
||||
|
||||
export type Todo = z.infer<typeof TodoShape>
|
||||
|
||||
export const todoRouter = router({
|
||||
getTodos: publicProcedure
|
||||
.query(() => {
|
||||
return $fetch<Todo[]>(`${baseURL}/todos`)
|
||||
}),
|
||||
getTodo: publicProcedure
|
||||
.input(z.number())
|
||||
.query((req) => {
|
||||
return $fetch<Todo>(`${baseURL}/todos/${req.input}`)
|
||||
}),
|
||||
addTodo: publicProcedure
|
||||
.input(TodoShape)
|
||||
.mutation((req) => {
|
||||
return $fetch<Todo>(`${baseURL}/todos`, {
|
||||
method: 'POST',
|
||||
body: req.input,
|
||||
})
|
||||
}),
|
||||
})
|
||||
33
playground/server/trpc/routers/user.ts
Normal file
33
playground/server/trpc/routers/user.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { z } from 'zod'
|
||||
import { publicProcedure, router } from '..'
|
||||
|
||||
const baseURL = 'https://jsonplaceholder.typicode.com'
|
||||
|
||||
const UserShape = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
username: z.string(),
|
||||
email: z.string(),
|
||||
})
|
||||
|
||||
export type User = z.infer<typeof UserShape>
|
||||
|
||||
export const userRouter = router({
|
||||
getUsers: publicProcedure
|
||||
.query(() => {
|
||||
return $fetch<User[]>(`${baseURL}/users`)
|
||||
}),
|
||||
getUser: publicProcedure
|
||||
.input(z.number())
|
||||
.query((req) => {
|
||||
return $fetch<User>(`${baseURL}/users/${req.input}`)
|
||||
}),
|
||||
addUser: publicProcedure
|
||||
.input(UserShape)
|
||||
.mutation((req) => {
|
||||
return $fetch<User>(`${baseURL}/users`, {
|
||||
method: 'POST',
|
||||
body: req.input,
|
||||
})
|
||||
}),
|
||||
})
|
||||
4
playground/tsconfig.json
Normal file
4
playground/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// https://v3.nuxtjs.org/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
||||
1160
pnpm-lock.yaml
generated
1160
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
1
server.d.ts
vendored
1
server.d.ts
vendored
@@ -1 +0,0 @@
|
||||
export * from './dist/runtime/server'
|
||||
@@ -7,8 +7,6 @@ import type {
|
||||
import { createFlatProxy, createRecursiveProxy } from '@trpc/server/shared'
|
||||
import { hash } from 'ohash'
|
||||
import type { DecoratedProcedureRecord } from './types'
|
||||
// @ts-expect-error: Nuxt internal
|
||||
import { useAsyncData } from '#app'
|
||||
|
||||
/**
|
||||
* Calculates the key used for `useAsyncData` call
|
||||
@@ -40,12 +38,14 @@ export function createNuxtProxyDecoration<TRouter extends AnyRouter>(name: strin
|
||||
const queryKey = getQueryKey(path, input)
|
||||
|
||||
if (lastArg === 'mutate') {
|
||||
// @ts-ignore: nuxt internal
|
||||
return useAsyncData(() => (client as any)[path][lastArg](input), {
|
||||
...asyncDataOptions as Record<string, any>,
|
||||
immediate: false,
|
||||
})
|
||||
}
|
||||
|
||||
// @ts-ignore: nuxt internal
|
||||
return useAsyncData(queryKey, () => (client as any)[path][lastArg](input), asyncDataOptions as Record<string, any>)
|
||||
})
|
||||
}
|
||||
1
src/index.ts
Normal file
1
src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './server'
|
||||
@@ -1,58 +0,0 @@
|
||||
import { fileURLToPath } from 'url'
|
||||
import { join } from 'pathe'
|
||||
import { defu } from 'defu'
|
||||
import dedent from 'dedent'
|
||||
|
||||
import { addServerHandler, addTemplate, defineNuxtModule } from '@nuxt/kit'
|
||||
|
||||
export interface ModuleOptions {
|
||||
baseURL: string
|
||||
endpoint: string
|
||||
}
|
||||
|
||||
export default defineNuxtModule<ModuleOptions>({
|
||||
meta: {
|
||||
name: 'trpc-nuxt',
|
||||
configKey: 'trpc',
|
||||
},
|
||||
defaults: {
|
||||
baseURL: '',
|
||||
endpoint: '/trpc',
|
||||
},
|
||||
async setup(options, nuxt) {
|
||||
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
|
||||
nuxt.options.build.transpile.push(runtimeDir, '#build/trpc-handler')
|
||||
|
||||
const handlerPath = join(nuxt.options.buildDir, 'trpc-handler.ts')
|
||||
const trpcOptionsPath = join(nuxt.options.srcDir, 'server/trpc')
|
||||
|
||||
// Final resolved configuration
|
||||
const finalConfig = nuxt.options.runtimeConfig.public.trpc = defu(nuxt.options.runtimeConfig.public.trpc, {
|
||||
baseURL: options.baseURL,
|
||||
endpoint: options.endpoint,
|
||||
})
|
||||
|
||||
addServerHandler({
|
||||
route: `${finalConfig.endpoint}/*`,
|
||||
handler: handlerPath,
|
||||
})
|
||||
|
||||
addTemplate({
|
||||
filename: 'trpc-handler.ts',
|
||||
write: true,
|
||||
getContents() {
|
||||
return dedent`
|
||||
import { createTRPCHandler } from 'trpc-nuxt/server'
|
||||
import * as functions from '${trpcOptionsPath}'
|
||||
|
||||
export default createTRPCHandler({
|
||||
...functions,
|
||||
router: functions.appRouter,
|
||||
endpoint: '${finalConfig.endpoint}'
|
||||
})
|
||||
`
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -37,18 +37,18 @@ export interface OnErrorPayload<TRouter extends AnyRouter> {
|
||||
|
||||
export type OnErrorFn<TRouter extends AnyRouter> = (opts: OnErrorPayload<TRouter>) => void
|
||||
|
||||
export function createTRPCHandler<Router extends AnyRouter>({
|
||||
export function createNuxtApiHandler<TRouter extends AnyRouter>({
|
||||
router,
|
||||
createContext,
|
||||
responseMeta,
|
||||
onError,
|
||||
endpoint,
|
||||
url = '/api/trpc',
|
||||
}: {
|
||||
router: Router
|
||||
createContext?: CreateContextFn<Router>
|
||||
responseMeta?: ResponseMetaFn<Router>
|
||||
onError?: OnErrorFn<Router>
|
||||
endpoint: string
|
||||
router: TRouter
|
||||
createContext?: CreateContextFn<TRouter>
|
||||
responseMeta?: ResponseMetaFn<TRouter>
|
||||
onError?: OnErrorFn<TRouter>
|
||||
url?: string
|
||||
}) {
|
||||
return defineEventHandler(async (event) => {
|
||||
const {
|
||||
@@ -66,7 +66,7 @@ export function createTRPCHandler<Router extends AnyRouter>({
|
||||
body: isMethod(event, 'GET') ? null : await readBody(event),
|
||||
query: $url.searchParams,
|
||||
},
|
||||
path: $url.pathname.substring(endpoint.length + 1),
|
||||
path: $url.pathname.substring(url.length + 1),
|
||||
createContext: async () => createContext?.(event),
|
||||
responseMeta,
|
||||
onError: (o) => {
|
||||
10
tsup.config.ts
Normal file
10
tsup.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'tsup'
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts', 'src/client.ts'],
|
||||
format: ['cjs', 'esm'],
|
||||
splitting: false,
|
||||
clean: true,
|
||||
external: ['#app'],
|
||||
dts: true,
|
||||
})
|
||||
Reference in New Issue
Block a user