Compare commits

...

156 Commits

Author SHA1 Message Date
wobsoriano
2c2df9e2bd chore: release v0.4.3 2022-11-24 13:12:18 -08:00
wobsoriano
5cf1acfa7e feat(deps): update minimum required @trpc/server and @trpc/nuxt version to 10.0.0 2022-11-24 13:12:14 -08:00
wobsoriano
f67f7fc5f4 update docs 2022-11-24 13:10:10 -08:00
wobsoriano
e11edc59eb update docs 2022-11-24 13:08:28 -08:00
wobsoriano
e522a59a4c update docs 2022-11-24 13:07:25 -08:00
wobsoriano
1d7be4642d update docs 2022-11-24 13:07:13 -08:00
wobsoriano
3552017e4f update docs 2022-11-24 13:05:32 -08:00
wobsoriano
de690a7914 update docs 2022-11-24 13:00:40 -08:00
wobsoriano
d2666650de update docs 2022-11-24 12:57:29 -08:00
wobsoriano
e3d35c6b04 update docs 2022-11-24 12:56:59 -08:00
wobsoriano
b569afde50 update docs 2022-11-24 12:53:17 -08:00
wobsoriano
d4f3942fff update docs 2022-11-24 12:52:17 -08:00
wobsoriano
68003e9c3e update docs 2022-11-24 12:51:28 -08:00
Robert Soriano
b27938f108 Merge pull request #44 from benfavre/patch-1
Update package.json
2022-11-21 20:15:33 -08:00
Webdesign29
9e443ac559 Update package.json
Fix calling --watch for tsup when using "dev" command
2022-11-19 22:11:12 +01:00
wobsoriano
8ba002407a update lockfile 2022-11-17 02:44:00 -08:00
wobsoriano
f061b0531f chore: release v0.4.2 2022-11-17 02:42:20 -08:00
wobsoriano
f08b724df7 feat: get req,res values from event.node 2022-11-17 02:42:01 -08:00
wobsoriano
4085d5fe26 fix: request types 2022-11-17 02:41:01 -08:00
wobsoriano
13713343ce fix: request types 2022-11-17 02:39:45 -08:00
wobsoriano
d6f11bb301 feat(deps): bump h3 to 1.0.1 2022-11-17 02:38:27 -08:00
wobsoriano
c5c1f1cdba feat(deps): bump h3, ohash and ufo to 1.0.0 2022-11-17 02:38:17 -08:00
wobsoriano
0681dd554d Merge branch 'next' of https://github.com/wobsoriano/trpc-nuxt into next 2022-11-16 08:48:14 -08:00
wobsoriano
e7a76694af update docs 2022-11-16 08:48:09 -08:00
wobsoriano
da75c18ebe update local nuxt dep to stable 2022-11-16 08:42:45 -08:00
wobsoriano
6289575900 bump local trpc deps to rc.8 2022-11-16 08:40:35 -08:00
Robert Soriano
ac212672e4 Update 2.server-side-calls.md 2022-11-14 12:26:48 -08:00
Robert Soriano
b8e2a83e26 Merge pull request #42 from nkhdo/patch-1
Update 2.server-side-calls.md
2022-11-14 10:43:06 -08:00
Hoang Do
fd98b0d414 Update 2.server-side-calls.md
Fix wrong variable import
2022-11-15 00:46:06 +07:00
wobsoriano
20ab235856 docs: add auth tips 2022-11-13 11:18:57 -08:00
wobsoriano
7205c356f8 docs: update aborting procs 2022-11-13 11:08:28 -08:00
wobsoriano
e14827e0c7 docs: add aborting procs 2022-11-13 11:05:47 -08:00
wobsoriano
eea6406d5a docs: update example 2022-11-13 10:58:27 -08:00
wobsoriano
2d3409a25b docs: update example 2022-11-13 10:58:08 -08:00
wobsoriano
7a71a6e128 docs: add mutation example 2022-11-13 10:55:23 -08:00
wobsoriano
d8e89297cd docs: typo fix 2022-11-13 10:41:35 -08:00
wobsoriano
6768205318 docs: update composables 2022-11-13 10:41:22 -08:00
wobsoriano
c20a092a31 docs: add tips section 2022-11-13 10:37:24 -08:00
wobsoriano
64df8fb7ad infer error in playground 2022-11-13 09:43:23 -08:00
wobsoriano
d5567e5826 remove character 2022-11-12 21:16:19 -08:00
wobsoriano
6b898e836c update readme 2022-11-12 21:15:19 -08:00
wobsoriano
fd38c4f983 chore: release v0.4.1 2022-11-12 20:16:38 -08:00
wobsoriano
48d4c6342f update playground 2022-11-12 20:16:29 -08:00
wobsoriano
297c3d52f4 docs: update usage 2022-11-12 20:15:52 -08:00
wobsoriano
0f9653d1a4 remove /module 2022-11-12 20:12:36 -08:00
wobsoriano
85998101c7 feat: remove nuxt 3 wrappers for client 2022-11-12 20:10:50 -08:00
wobsoriano
d6cb770154 update installation instructions 2022-11-12 18:41:44 -08:00
wobsoriano
c222008524 update installation instructions 2022-11-12 18:41:16 -08:00
wobsoriano
9cd21eb300 update readme docs url 2022-11-12 18:40:15 -08:00
wobsoriano
b4e83dbb0b add back preset 2022-11-12 18:38:25 -08:00
wobsoriano
0fa63c86ba test node preset 2022-11-12 18:31:35 -08:00
wobsoriano
7371dc4c10 update docs 2022-11-12 18:28:26 -08:00
wobsoriano
77ab5f0dc6 reinstall deps 2022-11-12 18:20:07 -08:00
wobsoriano
ba15d15d67 remove patched @vueuse/head 2022-11-12 18:17:10 -08:00
wobsoriano
7999039088 update github config 2022-11-12 18:13:49 -08:00
wobsoriano
dc15500074 dont prerender index 2022-11-12 18:13:00 -08:00
wobsoriano
99a83562aa add tmp docs link 2022-11-12 18:09:34 -08:00
wobsoriano
e04344d508 docs: remove package manager 2022-11-12 18:08:22 -08:00
wobsoriano
0f9d26ab60 chore: release v0.4.0 2022-11-12 18:01:55 -08:00
wobsoriano
e6b4386bd8 remove tag 2022-11-12 18:01:10 -08:00
wobsoriano
813b9ff7d7 move eslint config to package.json 2022-11-12 17:55:32 -08:00
wobsoriano
5dce1f4681 update readme 2022-11-12 17:54:17 -08:00
wobsoriano
470ba96be2 transpile github module 2022-11-12 16:32:18 -08:00
wobsoriano
4532add2a7 docs: patch vueuse/head 2022-11-12 16:26:43 -08:00
wobsoriano
876ed1d8d2 bump local trpc deps to rc.7 2022-11-08 09:30:58 -08:00
wobsoriano
611e22d205 delete merge conflicts 2022-11-08 09:27:22 -08:00
wobsoriano
c1857f1079 eslint fix 2022-11-08 09:25:21 -08:00
wobsoriano
796fa43a83 fix lockfile 2022-11-08 09:23:14 -08:00
wobsoriano
e3ce0bcaae merge conflicts 2022-11-08 09:22:30 -08:00
wobsoriano
e916f17d60 update docs 2022-11-08 09:06:08 -08:00
wobsoriano
0fdb9e0e17 update colors 2022-11-08 09:05:36 -08:00
wobsoriano
5bd5756d26 update og image 2022-11-08 09:00:41 -08:00
wobsoriano
67f95756ab fix docs 2022-11-08 08:59:32 -08:00
wobsoriano
1fc4d9d0d0 Revert "feat: add file routing option"
This reverts commit 750783e860.
2022-11-05 21:24:24 -07:00
wobsoriano
c11a5cfa8a remove examples folder 2022-11-05 20:13:48 -07:00
wobsoriano
92c9f6390c disable file routing by default 2022-11-05 20:06:02 -07:00
wobsoriano
3a6c5b659b update function names 2022-11-05 19:50:39 -07:00
wobsoriano
750783e860 feat: add file routing option 2022-11-05 19:22:19 -07:00
wobsoriano
d9c8f877d3 eslint fixes 2022-11-05 12:57:45 -07:00
wobsoriano
e359702c41 eslint fixes 2022-11-05 11:56:14 -07:00
wobsoriano
e9081d00ad replace eslint config 2022-11-05 11:49:46 -07:00
wobsoriano
83f98e34fa more eslint fixes 2022-11-04 08:04:58 -07:00
wobsoriano
512a0ae7b1 more eslint fixes 2022-11-04 08:04:46 -07:00
wobsoriano
c89f8747ea bump local deps 2022-11-04 08:04:28 -07:00
wobsoriano
3e8a2de6e5 more eslint fixes 2022-11-04 08:02:52 -07:00
wobsoriano
5d98873cc3 eslint fix 2022-11-04 08:01:24 -07:00
wobsoriano
a8e5538b05 release v0.3.3 2022-11-04 07:58:20 -07:00
wobsoriano
24ecb41429 eslint fix 2022-11-04 07:58:16 -07:00
wobsoriano
e8bf427d2b fix types 2022-11-04 07:57:38 -07:00
wobsoriano
347d49482f reinstall deps 2022-11-04 07:55:39 -07:00
wobsoriano
2f7a1276ad feat(deps): bump @nuxt/kit to 3.0.0-rc.13 2022-11-04 07:53:42 -07:00
wobsoriano
f6db0c78cd add .vercel to .gitignore 2022-11-04 07:51:44 -07:00
wobsoriano
d6012eb5c8 feat: import vue functions from #imports 2022-11-04 07:49:37 -07:00
wobsoriano
c1ab3ecf77 update scripts 2022-11-04 07:44:09 -07:00
wobsoriano
0e8f6cf9f6 update playground 2022-11-04 07:41:27 -07:00
wobsoriano
0f5c09c7e0 update docs 2022-11-04 07:40:49 -07:00
wobsoriano
5080e9a4a7 bump local trpc deps to 10.0.0-rc.4 2022-11-04 07:39:50 -07:00
wobsoriano
7490dbd090 chore: bump to 0.4.0-beta.15 2022-11-04 07:37:49 -07:00
wobsoriano
1dc0ca7808 feat: remove custom async data error handler 2022-11-04 07:37:08 -07:00
wobsoriano
ffb692a168 feat(deps): bump nuxt to 3.0.0-rc.13 2022-11-04 07:34:47 -07:00
wobsoriano
d3203f0536 remove docs 2022-11-02 09:43:35 -07:00
wobsoriano
83e2b2810d release v0.3.2 2022-11-02 09:42:08 -07:00
wobsoriano
2b4fff9faa feat(deps): bump h3 to 0.8.6 2022-11-02 09:41:39 -07:00
wobsoriano
752fe721a2 feat(deps): lock @trpc/server and @trpc/client to version 9 2022-11-02 09:40:48 -07:00
wobsoriano
808cac4756 eslint fixes 2022-11-02 09:38:14 -07:00
wobsoriano
0c44f73c9f log info about plugin installation 2022-11-02 09:34:16 -07:00
wobsoriano
182875a781 feat: add option to specify if plugin should be installed 2022-11-02 09:30:40 -07:00
wobsoriano
0298f836b8 more eslint fixes 2022-11-02 09:21:15 -07:00
wobsoriano
ea50430945 eslint fixes 2022-11-02 09:18:17 -07:00
wobsoriano
0e240663ca remove local pnpm deps 2022-11-02 09:18:00 -07:00
wobsoriano
bb5e02383e remove unused moduile 2022-11-02 08:59:55 -07:00
wobsoriano
c01dddea02 remove local pnpm 2022-11-02 08:56:58 -07:00
wobsoriano
f92aec5b91 feat(deps): bump h3 to 0.8.6 2022-11-02 08:55:52 -07:00
wobsoriano
b6f92d954c update local trpc dependencies to 10.0.0-rc.3 2022-11-02 08:54:58 -07:00
wobsoriano
9108dcbb1d feat: never cache mutation 2022-10-31 22:39:40 -07:00
Robert Soriano
07f415640f bump to 0.4.0-beta.14 2022-10-31 10:09:07 -07:00
Robert Soriano
d5d211d2a8 feat: add subscription types 2022-10-31 10:06:41 -07:00
Robert Soriano
3bfd3585b3 remove unused types 2022-10-31 09:58:51 -07:00
Robert Soriano
8ba8d79114 remove unused generic type 2022-10-31 09:55:00 -07:00
Robert Soriano
b7f72eac0a feat: respect user added abort signal 2022-10-31 09:53:00 -07:00
Robert Soriano
4c8a6d7b35 feat: add abortOnUnmount option 2022-10-31 09:50:15 -07:00
Robert Soriano
76619ac541 bump to 0.4.0-beta.13 2022-10-31 01:01:44 -07:00
Robert Soriano
d5384cef02 add .vercel to ignored 2022-10-31 00:50:10 -07:00
Robert Soriano
fb3a0a834e add vercel static output to ignored 2022-10-31 00:43:08 -07:00
Robert Soriano
b58d662edc update docs 2022-10-31 00:37:45 -07:00
Robert Soriano
5d560867bb update http handler params 2022-10-30 22:24:49 -07:00
Robert Soriano
ac7fbf3998 update trpc param error handler 2022-10-30 22:21:08 -07:00
Robert Soriano
45fbab4c5f update docs 2022-10-30 21:40:20 -07:00
Robert Soriano
05ad1f96f6 export server types 2022-10-30 21:31:57 -07:00
Robert Soriano
41e6117113 add zod error formatting for playground 2022-10-30 21:30:07 -07:00
Robert Soriano
81c7608fb0 add batching and other opts 2022-10-30 21:25:37 -07:00
Robert Soriano
a614bc588e allow passing of other client options 2022-10-30 21:13:54 -07:00
Robert Soriano
3b0609e395 allow passing of other client options 2022-10-30 21:09:42 -07:00
Robert Soriano
0819c21b94 eslint fix 2022-10-30 18:19:32 -07:00
Robert Soriano
deea70b743 check trpc path via h3 context params 2022-10-30 18:17:14 -07:00
Robert Soriano
0ee322c8a2 run dev playground with concurrently 2022-10-30 17:35:23 -07:00
Robert Soriano
fc225ced0a bump to beta.12 2022-10-30 17:06:43 -07:00
Robert Soriano
b1d00c1f88 update compat 2022-10-30 17:06:35 -07:00
Robert Soriano
e0a6bb3576 bump to beta.11 2022-10-30 17:03:59 -07:00
Robert Soriano
fcd6cb1546 fix module compat 2022-10-30 17:03:49 -07:00
Robert Soriano
471ac30040 bump to beta.10 2022-10-30 17:00:27 -07:00
Robert Soriano
a7013db712 add module helper 2022-10-30 17:00:19 -07:00
Robert Soriano
bdcff360f0 bump to beta.9 2022-10-30 14:24:50 -07:00
Robert Soriano
1490790edc import composables from nuxt/app 2022-10-30 14:24:39 -07:00
Robert Soriano
4c5e6812b1 bump to beta.8 2022-10-30 13:36:28 -07:00
Robert Soriano
23d2954aae import composables from #imports 2022-10-30 13:35:58 -07:00
Robert Soriano
3c39746fa2 bump to beta.7 2022-10-30 13:31:04 -07:00
Robert Soriano
5fb7a9d444 import composables from nuxt/app 2022-10-30 13:30:57 -07:00
Robert Soriano
a2275567e8 bump to beta.6 2022-10-30 13:25:11 -07:00
Robert Soriano
c040db4308 update esm extensions to mjs 2022-10-30 13:24:39 -07:00
Robert Soriano
72755315a5 bump to beta.5 2022-10-30 13:09:58 -07:00
Robert Soriano
3d49810641 switch package type to module 2022-10-30 13:09:45 -07:00
Robert Soriano
ebb6637238 chore: release v0.4.0-beta.4 2022-10-30 13:02:11 -07:00
Robert Soriano
0c83ade918 update release script 2022-10-30 13:02:01 -07:00
Robert Soriano
97d9b3ddf8 import useAsyncData and useState from #app 2022-10-30 12:57:57 -07:00
Robert Soriano
eaa61f3700 bump local pnpm to 7.14.1 2022-10-30 12:35:30 -07:00
35 changed files with 2002 additions and 3411 deletions

2
.gitignore vendored
View File

@@ -49,3 +49,5 @@ coverage
Network Trash Folder Network Trash Folder
Temporary Items Temporary Items
.apdisk .apdisk
.vercel

View File

@@ -1,10 +1,21 @@
# tRPC-Nuxt # tRPC-Nuxt
[![Version](https://img.shields.io/npm/v/trpc-nuxt?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/trpc-nuxt)
End-to-end typesafe APIs with [tRPC.io](https://trpc.io/) in Nuxt applications. End-to-end typesafe APIs with [tRPC.io](https://trpc.io/) in Nuxt applications.
Learn more about tRPC.io [here](https://trpc.io/docs/v10). <p align="center">
<figure>
<img src="https://i.imgur.com/3AZlBZH.gif" alt="Demo" />
<figcaption>
<p align="center">
The client above is <strong>not</strong> importing any code from the server, only its type declarations.
</p>
</figcaption>
</figure>
</p>
Docs: https://trpc-nuxt.vercel.app
For version 3 of this module (tRPC v9, auto-imports, auto handlers), [go here](https://github.com/wobsoriano/trpc-nuxt/tree/v3).
## Recommended IDE Setup ## Recommended IDE Setup

1
client.d.ts vendored
View File

@@ -1 +0,0 @@
export * from './dist/client/index'

1
docs/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.vercel

View File

@@ -1,5 +1,25 @@
export default defineAppConfig({ export default defineAppConfig({
docus: { docus: {
title: 'tRPC Nuxt', title: 'tRPC Nuxt',
}, image: 'https://og-image.vercel.app/tRPC-Nuxt',
alt: 'tRPC-Nuxt cover',
url: 'https://trpc-nuxt.vercel.app',
debug: false,
socials: {
github: 'wobsoriano/trpc-nuxt'
},
aside: {
level: 1
},
footer: {
credits: true,
icons: [
{
label: 'NuxtJS',
href: 'https://nuxtjs.org',
component: 'IconNuxt'
}
]
}
}
}) })

View File

@@ -5,20 +5,18 @@ description: tRPC-Nuxt provides first class integration with tRPC.
# Installation # Installation
## 1. Add to existing Nuxt project
::code-group ::code-group
```bash [pnpm] ```bash [pnpm]
pnpm add @trpc/server@next @trpc/client@next trpc-nuxt@next zod pnpm add @trpc/server@next @trpc/client@next trpc-nuxt zod
``` ```
```bash [npm] ```bash [npm]
npm install @trpc/server@next @trpc/client@next trpc-nuxt@next zod npm install @trpc/server@next @trpc/client@next trpc-nuxt zod
``` ```
```bash [yarn] ```bash [yarn]
yarn add @trpc/server@next @trpc/client@next trpc-nuxt@next zod yarn add @trpc/server@next @trpc/client@next trpc-nuxt zod
``` ```
:: ::
@@ -35,56 +33,6 @@ For making typesafe API calls from your client.
Most examples use [Zod](https://github.com/colinhacks/zod) for input validation and tRPC.io highly recommends it, though it isn't required. Most examples use [Zod](https://github.com/colinhacks/zod) for input validation and tRPC.io highly recommends it, though it isn't required.
## 2. Enable strict mode
If you want to use Zod for input validation, make sure you have enabled strict mode:
::code-group
```json [tsconfig.json]
{
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"strict": true
}
}
```
```ts [nuxt.config.ts]
export default defineNuxtConfig({
typescript: {
strict: true
}
})
```
::
If strict mode is too much, at least enable `strictNullChecks`:
::code-group
```json [tsconfig.json]
{
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"strictNullChecks": true
}
}
```
```ts [nuxt.config.ts]
export default defineNuxtConfig({
typescript: {
tsConfig: {
strictNullChecks: true
}
}
})
```
::
## Next Steps ## Next Steps
Now that you've installed the required dependencies, you are ready to start building your application. Now that you've installed the required dependencies, you are ready to start building your application.

View File

@@ -35,7 +35,7 @@ Initialize your tRPC backend using the `initTRPC` function and create your first
::code-group ::code-group
```ts [server/trpc/trpc.ts] ```ts [server/trpc/trpc.ts]
import { TRPCError, initTRPC } from '@trpc/server' import { initTRPC } from '@trpc/server'
// Avoid exporting the entire t-object since it's not very // Avoid exporting the entire t-object since it's not very
// descriptive and can be confusing to newcomers used to t // descriptive and can be confusing to newcomers used to t
@@ -49,7 +49,7 @@ export const publicProcedure = t.procedure
```ts [server/trpc/routers/index.ts] ```ts [server/trpc/routers/index.ts]
import { z } from 'zod' import { z } from 'zod'
import { publicProcedure, router } from '..' import { publicProcedure, router } from '../trpc'
export const appRouter = router({ export const appRouter = router({
hello: publicProcedure hello: publicProcedure
@@ -71,7 +71,7 @@ export type AppRouter = typeof appRouter
```ts [server/api/trpc/[trpc].ts] ```ts [server/api/trpc/[trpc].ts]
import { createNuxtApiHandler } from 'trpc-nuxt' import { createNuxtApiHandler } from 'trpc-nuxt'
import { appRouter } from '../../trpc/routers' import { appRouter } from '@/server/trpc/routers'
// export API handler // export API handler
export default createNuxtApiHandler({ export default createNuxtApiHandler({
@@ -88,15 +88,14 @@ If you need to split your router into several subrouters, you can implement them
## 2. Create tRPC client plugin ## 2. Create tRPC client plugin
Create a set of strongly-typed composables using your API's type signature. Create a strongly-typed plugin using your API's type signature.
```ts [plugins/client.ts] ```ts [plugins/client.ts]
import { httpBatchLink } from '@trpc/client' import { httpBatchLink, createTRPCProxyClient } from '@trpc/client'
import { createTRPCNuxtProxyClient } from 'trpc-nuxt/client' import type { AppRouter } from '@/server/trpc/routers'
import type { AppRouter } from '~~/server/trpc/routers'
export default defineNuxtPlugin(() => { export default defineNuxtPlugin(() => {
const client = createTRPCNuxtProxyClient<AppRouter>({ const client = createTRPCProxyClient<AppRouter>({
links: [ links: [
httpBatchLink({ httpBatchLink({
/** /**
@@ -122,19 +121,10 @@ export default defineNuxtPlugin(() => {
<script setup lang="ts"> <script setup lang="ts">
const { $client } = useNuxtApp() const { $client } = useNuxtApp()
// query and mutate uses useAsyncData under the hood const data = await $client.hello.query({ text: 'client' })
const { data, pending, error } = await $client.hello.query({ text: 'client' })
</script> </script>
<template> <template>
<div v-if="pending"> <p>{{data?.greeting }}</p>
Loading...
</div>
<div v-else-if="error?.data?.code">
Error: {{ error.data.code }}
</div>
<div v-else>
<p>{{ hello.data?.greeting }}</p>
</div>
</template> </template>
``` ```

View File

@@ -0,0 +1,40 @@
---
title: Composables
---
# Composables
It is often useful to wrap functionality of your `@trpc/client` api within other functions. For this purpose, it's necessary to be able to infer input types and output types generated by your `@trpc/server` router.
## Inference Helpers
`@trpc/server` exports the following helper types to assist with inferring these types from the `AppRouter` exported by your `@trpc/server` router:
- `inferRouterInputs<TRouter>`
- `inferRouterOutputs<TRouter>`
Let's assume we have this example query wrapped within Nuxt's [useAsyncData](https://v3.nuxtjs.org/api/composables/use-async-data/):
```ts
const { data, error } = await useAsyncData(() => $client.todo.getTodos.query())
```
We can wrap this in a composable and also set the client error types:
```ts [composables/useGetTodos.ts]
import { TRPCClientError } from '@trpc/client'
import type { inferRouterOutputs } from '@trpc/server'
import type { AppRouter } from '@/server/trpc/routers'
type RouterOutput = inferRouterOutputs<AppRouter>
type GetTodosOutput = RouterOutput['todo']['getTodos']
type ErrorOutput = TRPCClientError<AppRouter>
export default function useGetTodos() {
const { $client } = useNuxtApp()
return useAsyncData<GetTodosOutput, ErrorOutput>(() => $client.todo.getTodos.query())
}
```
Now, we have a fully-typed composable.

View File

@@ -0,0 +1,40 @@
---
title: Headers
---
# Headers
We can use the built-in [useRequestHeaders](https://v3.nuxtjs.org/api/composables/use-request-headers/) to set outgoing request headers:
```ts [plugins/client.ts]
export default defineNuxtPlugin(() => {
const headers = useRequestHeaders()
const client = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
// headers need to be a function so it gets called dynamically
// every HTTP request
headers() {
// You can add more custom headers here
return headers
}
}),
],
})
return {
provide: {
client,
},
}
})
```
```ts [server/trpc/context.ts]
export function createContext (event: H3Event) {
console.log('cookies', parseCookies(event))
return {}
}
```

View File

@@ -0,0 +1,103 @@
---
title: Authorization
---
# Authorization
The `createContext` function is called for each incoming request so here you can add contextual information about the calling user from the request object.
::alert{type="warning"}
Before you can access request headers in any context or middleware, you need to set the outgoing request headers. See [here](/get-started/tips/headers).
::
## Create context from request headers
```ts [server/trpc/context.ts]
import { inferAsyncReturnType } from '@trpc/server'
import { decodeAndVerifyJwtToken } from './somewhere/in/your/app/utils'
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 might want to do in your ctx fn
const authorization = getRequestHeader(event, 'authorization')
async function getUserFromHeader() {
if (authorization) {
const user = await decodeAndVerifyJwtToken(authorization.split(' ')[1])
return user
}
return null
}
const user = await getUserFromHeader()
return {
user,
}
}
type Context = inferAsyncReturnType<typeof createContext>
```
## Option 1: Authorize using resolver
```ts
import { TRPCError, initTRPC } from '@trpc/server'
import type { Context } from '../context'
export const t = initTRPC.context<Context>().create()
const appRouter = t.router({
// open for anyone
hello: t.procedure
.input(z.string().nullish())
.query(({ input, ctx }) => `hello ${input ?? ctx.user?.name ?? 'world'}`),
// checked in resolver
secret: t.procedure.query(({ ctx }) => {
if (!ctx.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' })
}
return {
secret: 'sauce',
}
}),
})
```
## Option 2: Authorize using middleware
```ts
import { TRPCError, initTRPC } from '@trpc/server'
export const t = initTRPC.context<Context>().create()
const isAuthed = t.middleware(({ next, ctx }) => {
if (!ctx.user?.isAdmin) {
throw new TRPCError({ code: 'UNAUTHORIZED' })
}
return next({
ctx: {
user: ctx.user,
},
})
})
// you can reuse this for any procedure
export const protectedProcedure = t.procedure.use(isAuthed)
t.router({
// this is accessible for everyone
hello: t.procedure
.input(z.string().nullish())
.query(({ input, ctx }) => `hello ${input ?? ctx.user?.name ?? 'world'}`),
admin: t.router({
// this is accessible only to admins
secret: protectedProcedure.query(({ ctx }) => {
return {
secret: 'sauce',
}
}),
}),
})
```
This page is entirely based on [authorization docs](https://trpc.io/docs/v10/authorization) of tRPC with a minimal change made to work with Nuxt.

View File

@@ -0,0 +1,84 @@
---
title: Server Side Calls
---
# Server Side Calls
You may need to call your procedure(s) directly from the server, `createCaller()` function returns you an instance of `RouterCaller` able to execute queries and mutations.
## Input query example
We create the router with a input query and then we call the asynchronous `greeting` procedure to get the result.
::code-group
```ts [server/trpc/trpc.ts]
import { initTRPC } from '@trpc/server'
import { z } from 'zod'
const t = initTRPC.create()
export const router = t.router({
// Create procedure at path 'greeting'
greeting: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => `Hello ${input.name}`),
})
```
```ts [server/api/greeting.ts]
import { router } from '@/server/trpc/trpc'
export default eventHandler(async (event) => {
const { name } = getQuery(event)
const caller = router.createCaller({})
const greeting = await caller.greeting({ name })
return {
greeting
}
})
```
::
## Mutation example
We create the router with a mutation and then we call the asynchronous `post` procedure to get the result.
::code-group
```ts [server/trpc/trpc.ts]
import { initTRPC } from '@trpc/server'
import { z } from 'zod'
const posts = ['One', 'Two', 'Three']
const t = initTRPC.create()
export const router = t.router({
post: t.router({
add: t.procedure.input(z.string()).mutation(({ input }) => {
posts.push(input)
return posts
}),
}),
})
```
```ts [server/api/post.ts]
import { router } from '@/server/trpc/trpc'
export default eventHandler(async (event) => {
const body = await getBody(event)
const caller = router.createCaller({})
const post = await caller.post.add(body.post)
return {
post
}
})
```
::

View File

@@ -0,0 +1,24 @@
---
title: Aborting Procedures
---
# Aborting Procedures
tRPC adheres to the industry standard when it comes to aborting procedures. All you have to do is pass an `AbortSignal` to the query-options and then call its parent `AbortController`'s `abort` method.
```ts [composables/useGetTodo.ts]
export default function useGetTodo(id: number) {
const { $client } = useNuxtApp()
const ac = new AbortController()
onScopeDispose(() => {
ac.abort()
})
return useAsyncData(() => {
return $client.todo.getTodo.query(id, {
signal: ac.signal
})
})
}
```

View File

@@ -0,0 +1,4 @@
---
navigation: false
redirect: /get-started/installation
---

View File

@@ -1,6 +1,6 @@
--- ---
title: "tRPC Nuxt" title: "tRPC Nuxt"
description: "A supa simple wrapper arousnd supabase-js to enable usage and integration within Nuxt." description: "End-to-end typesafe APIs in Nuxt applications."
navigation: false navigation: false
layout: page layout: page
--- ---
@@ -13,7 +13,7 @@ cta:
secondary: secondary:
- Star on GitHub -> - Star on GitHub ->
- https://github.com/wobsoriano/trpc-nuxt - https://github.com/wobsoriano/trpc-nuxt
snippet: npm install trpc-nuxt@next snippet: npm install trpc-nuxt
--- ---
#title #title
@@ -24,8 +24,8 @@ End-to-end typesafe APIs in Nuxt applications.
#extra #extra
::list ::list
- Automatic typesafety - Automatic typesafety
- Snappy DX - Snappy DX
- Autocompletion - Autocompletion on the client, for inputs, outputs and errors
:: ::
:: ::

View File

@@ -1,16 +1,22 @@
export default defineNuxtConfig({ export default defineNuxtConfig({
app: { app: {
pageTransition: false, pageTransition: false,
layoutTransition: false, layoutTransition: false
},
modules: ['@nuxtlabs/github-module'],
extends: process.env.DOCUS_THEME_PATH || '@nuxt-themes/docus',
github: {
owner: 'wobsoriano',
repo: 'trpc-nuxt',
branch: 'next'
},
colorMode: {
preference: 'dark'
}, },
extends: '@nuxt-themes/docus',
build: { build: {
transpile: [/content-edge/], transpile: [/content-edge/, /github-module/]
}, },
nitro: { nitro: {
prerender: { preset: 'vercel'
crawlLinks: true, }
routes: ['/'],
},
},
}) })

View File

@@ -7,11 +7,11 @@
"generate": "nuxi build", "generate": "nuxi build",
"preview": "nuxi preview" "preview": "nuxi preview"
}, },
"dependencies": {
"nuxt": "^3.0.0"
},
"devDependencies": { "devDependencies": {
"@docus/github": "npm:@docus/github-edge@latest", "@nuxt-themes/docus": "^0.3.1",
"@nuxt-themes/docus": "npm:@nuxt-themes/docus-edge@0.1.0-2a7c428", "@nuxtlabs/github-module": "^1.5.3"
"@nuxt-themes/website": "0.1.7",
"nuxt": "^3.0.0-rc.12",
"vue-plausible": "^1.3.2"
} }
} }

View File

@@ -1,25 +1,18 @@
import { defineTheme } from 'pinceau' import { defineTheme } from 'pinceau'
export default defineTheme({ export default defineTheme({
title: 'asddsasda 3s', colors: {
cover: { primary: {
src: 'https://res.cloudinary.com/nuxt/image/upload/v1650870623/nuxt3-rc-social_z6qh3m.png', 50: '#BFEDFC',
alt: 'Nuxt 3 cover image', 100: '#B0E9FB',
}, 200: '#93DEFA',
aside: { 300: '#76D4F9',
level: 1, 400: '#58C8F7',
}, 500: '#3BBBF6',
// colors: { 600: '#0BA6F3',
// primary: { 700: '#0981C2',
// 100: '#77b0db', 800: '#075E91',
// 200: '#589ed3', 900: '#043D61'
// 300: '#4e98d0', }
// 400: '#398ccb', }
// 500: '#398ccb',
// 600: '#2BAB71',
// 700: '#317eb9',
// 800: '#2e77af',
// 900: '#266290',
// },
// },
}) })

View File

@@ -1,60 +1,68 @@
{ {
"name": "trpc-nuxt", "name": "trpc-nuxt",
"version": "0.4.0-beta.3", "description": "End-to-end typesafe APIs in Nuxt applications.",
"packageManager": "pnpm@7.5.0", "type": "module",
"version": "0.4.3",
"license": "MIT", "license": "MIT",
"sideEffects": false, "sideEffects": false,
"exports": { "exports": {
"./package.json": "./package.json",
".": { ".": {
"require": "./dist/index.js", "require": "./dist/index.cjs",
"import": "./dist/index.mjs" "import": "./dist/index.mjs"
},
"./client": {
"require": "./dist/client/index.js",
"import": "./dist/client/index.mjs"
} }
}, },
"main": "./dist/index.js", "main": "./dist/index.mjs",
"module": "./dist/index.mjs", "module": "./dist/index.mjs",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"files": [ "files": [
"dist", "dist"
"client.d.ts"
], ],
"scripts": { "scripts": {
"prepublishOnly": "nr build", "dev": "concurrently \"pnpm build -- --watch\" \"pnpm --filter playground dev\"",
"dev:prepare": "pnpm build && nuxt prepare playground",
"prepublishOnly": "pnpm build",
"build": "tsup", "build": "tsup",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"release": "bumpp --commit --push --tag && npm publish" "release": "bumpp && npm publish"
}, },
"peerDependencies": { "peerDependencies": {
"@trpc/client": "^10.0.0-proxy-beta.21", "@trpc/client": "^10.0.0",
"@trpc/server": "^10.0.0-proxy-beta.21", "@trpc/server": "^10.0.0"
"nuxt": "^3.0.0-rc.12"
}, },
"dependencies": { "dependencies": {
"h3": "^0.8.5", "h3": "^1.0.1",
"ohash": "^0.1.5", "nanoid": "^4.0.0",
"ufo": "^0.8.6" "ohash": "^1.0.0",
"ufo": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^0.27.0", "@nuxtjs/eslint-config-typescript": "^11.0.0",
"@antfu/ni": "^0.18.3", "@trpc/client": "^10.1.0",
"@trpc/client": "10.0.0-rc.2", "@trpc/server": "^10.1.0",
"@trpc/server": "10.0.0-rc.2",
"bumpp": "^8.2.1", "bumpp": "^8.2.1",
"concurrently": "^7.5.0",
"eslint": "^8.25.0", "eslint": "^8.25.0",
"nuxt": "3.0.0-rc.12", "tsup": "6.4.0",
"pnpm": "^7.5.0",
"tsup": "6.0.1",
"typescript": "^4.7.4" "typescript": "^4.7.4"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "@antfu", "extends": [
"@nuxtjs/eslint-config-typescript"
],
"rules": { "rules": {
"no-console": "warn" "@typescript-eslint/no-unused-vars": [
"off"
],
"vue/multi-word-component-names": "off",
"vue/no-multiple-template-root": "off"
} }
} },
"eslintIgnore": [
"*.json",
"node_modules",
"*.md",
"dist",
".output"
]
} }

View File

@@ -1,9 +1,3 @@
// https://v3.nuxtjs.org/api/configuration/nuxt.config // https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({ export default defineNuxtConfig({
runtimeConfig: {
baseURL: '',
},
typescript: {
strict: true,
},
}) })

View File

@@ -9,13 +9,13 @@
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare"
}, },
"dependencies": { "dependencies": {
"@trpc/client": "10.0.0-rc.2", "@trpc/client": "^10.1.0",
"@trpc/server": "10.0.0-rc.2", "@trpc/server": "^10.1.0",
"superjson": "^1.11.0", "superjson": "^1.11.0",
"trpc-nuxt": "workspace:*", "trpc-nuxt": "workspace:*",
"zod": "^3.19.1" "zod": "^3.19.1"
}, },
"devDependencies": { "devDependencies": {
"nuxt": "3.0.0-rc.12" "nuxt": "^3.0.0"
} }
} }

View File

@@ -1,11 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
const { $client } = useNuxtApp() import { TRPCClientError } from '@trpc/client';
// const headers = useClientHeaders() import type { inferRouterOutputs } from '@trpc/server';
import type { AppRouter } from '~~/server/trpc/routers';
// const addHeader = () => { const { $client } = useNuxtApp()
// headers.value.authorization = 'Bearer abcdefghijklmnop'
// console.log(headers.value)
// }
const addTodo = async () => { const addTodo = async () => {
const title = Math.random().toString(36).slice(2, 7) const title = Math.random().toString(36).slice(2, 7)
@@ -15,16 +13,18 @@ const addTodo = async () => {
id: Date.now(), id: Date.now(),
userId: 69, userId: 69,
title, title,
completed: false, completed: false
}) })
console.log(x.data.value) console.log(x)
} } catch (e) {
catch (e) {
console.log(e) console.log(e)
} }
} }
const { data: todos, pending, error, refresh } = await $client.todo.getTodos.query() type RouterOutput = inferRouterOutputs<AppRouter>;
type ErrorOutput = TRPCClientError<AppRouter>
const { data: todos, pending, error, refresh } = await useAsyncData<RouterOutput['todo']['getTodos'], ErrorOutput>(() => $client.todo.getTodos.query())
</script> </script>
<template> <template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
const route = useRoute() const route = useRoute()
const { $client } = useNuxtApp() const { $client } = useNuxtApp()
const { data: todo, pending, error } = await $client.todo.getTodo.query(Number(route.params.id)) const { data: todo, pending, error } = await useAsyncData(() => $client.todo.getTodo.query(Number(route.params.id)))
</script> </script>
<template> <template>

View File

@@ -1,50 +1,30 @@
import { httpBatchLink, loggerLink } from '@trpc/client' import { createTRPCProxyClient, httpBatchLink, loggerLink } from '@trpc/client'
import { createTRPCNuxtProxyClient } from 'trpc-nuxt/client'
import superjson from 'superjson' import superjson from 'superjson'
import type { AppRouter } from '~~/server/trpc/routers' import type { AppRouter } from '~~/server/trpc/routers'
export default defineNuxtPlugin((nuxtApp) => { export default defineNuxtPlugin(() => {
const client = createTRPCNuxtProxyClient<AppRouter>({ const headers = useRequestHeaders()
const client = createTRPCProxyClient<AppRouter>({
transformer: superjson, transformer: superjson,
links: [ links: [
// adds pretty logs to your console in development and logs errors in production // adds pretty logs to your console in development and logs errors in production
loggerLink({ loggerLink({
enabled: opts => enabled: opts =>
process.env.NODE_ENV === 'development' process.env.NODE_ENV === 'development' ||
|| (opts.direction === 'down' && opts.result instanceof Error), (opts.direction === 'down' && opts.result instanceof Error)
}), }),
httpBatchLink({ httpBatchLink({
url: 'http://localhost:3000/api/trpc', url: 'http://localhost:3000/api/trpc',
/** headers () {
* Set custom request headers on every request from tRPC return headers
* @link https://trpc.io/docs/ssr }
*/ })
headers() { ]
if (nuxtApp.ssrContext?.event?.req) {
// To use SSR properly, you need to forward the client's headers to the server
// This is so you can pass through things like cookies when we're server-side rendering
// If you're using Node 18, omit the "connection" header
const {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
connection: _connection,
...headers
} = nuxtApp.ssrContext.event.req.headers
return {
...headers,
// Optional: inform server that it's an SSR request
'x-ssr': '1',
}
}
return {}
},
}),
],
}) })
return { return {
provide: { provide: {
client, client
}, }
} }
}) })

View File

@@ -1,6 +1,6 @@
import { createNuxtApiHandler } from 'trpc-nuxt' import { createNuxtApiHandler } from 'trpc-nuxt'
import { appRouter } from '../../trpc/routers' import { appRouter } from '@/server/trpc/routers'
import { createContext } from '~~/server/trpc/context' import { createContext } from '@/server/trpc/context'
export default createNuxtApiHandler({ export default createNuxtApiHandler({
router: appRouter, router: appRouter,
@@ -8,12 +8,12 @@ export default createNuxtApiHandler({
* @link https://trpc.io/docs/context * @link https://trpc.io/docs/context
*/ */
createContext, createContext,
onError({ error }) { onError ({ error }) {
if (error.code === 'INTERNAL_SERVER_ERROR') { if (error.code === 'INTERNAL_SERVER_ERROR') {
// send to bug reporting // send to bug reporting
console.error('Something went wrong', error) console.error('Something went wrong', error)
} }
}, }
/** /**
* @link https://trpc.io/docs/caching#api-response-caching * @link https://trpc.io/docs/caching#api-response-caching
*/ */

View File

@@ -8,10 +8,11 @@ export type Context = inferAsyncReturnType<typeof createContext>
* Creates context for an incoming request * Creates context for an incoming request
* @link https://trpc.io/docs/context * @link https://trpc.io/docs/context
*/ */
export async function createContext( export function createContext (
opts: H3Event, event: H3Event
) { ) {
// for API-response caching see https://trpc.io/docs/caching // for API-response caching see https://trpc.io/docs/caching
console.log('cookies', parseCookies(event))
return {} return {}
} }

View File

@@ -4,7 +4,7 @@ import { userRouter } from './user'
export const appRouter = router({ export const appRouter = router({
todo: todoRouter, todo: todoRouter,
user: userRouter, user: userRouter
}) })
export type AppRouter = typeof appRouter export type AppRouter = typeof appRouter

View File

@@ -7,7 +7,7 @@ const TodoShape = z.object({
userId: z.number(), userId: z.number(),
id: z.number(), id: z.number(),
title: z.string(), title: z.string(),
completed: z.boolean(), completed: z.boolean()
}) })
export type Todo = z.infer<typeof TodoShape> export type Todo = z.infer<typeof TodoShape>
@@ -27,7 +27,7 @@ export const todoRouter = router({
.mutation((req) => { .mutation((req) => {
return $fetch<Todo>(`${baseURL}/todos`, { return $fetch<Todo>(`${baseURL}/todos`, {
method: 'POST', method: 'POST',
body: req.input, body: req.input
}) })
}), })
}) })

View File

@@ -7,7 +7,7 @@ const UserShape = z.object({
id: z.number(), id: z.number(),
name: z.string(), name: z.string(),
username: z.string(), username: z.string(),
email: z.string(), email: z.string()
}) })
export type User = z.infer<typeof UserShape> export type User = z.infer<typeof UserShape>
@@ -27,7 +27,7 @@ export const userRouter = router({
.mutation((req) => { .mutation((req) => {
return $fetch<User>(`${baseURL}/users`, { return $fetch<User>(`${baseURL}/users`, {
method: 'POST', method: 'POST',
body: req.input, body: req.input
}) })
}), })
}) })

View File

@@ -1,9 +1,23 @@
import { initTRPC } from '@trpc/server' import { initTRPC } from '@trpc/server'
import superjson from 'superjson' import superjson from 'superjson'
import { ZodError } from 'zod'
import type { Context } from './context' import type { Context } from './context'
const t = initTRPC.context<Context>().create({ const t = initTRPC.context<Context>().create({
transformer: superjson, transformer: superjson,
errorFormatter ({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.code === 'BAD_REQUEST' &&
error.cause instanceof ZodError
? error.cause!.flatten()
: null
}
}
}
}) })
/** /**

4360
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,73 +0,0 @@
import type { CreateTRPCClientOptions, inferRouterProxyClient } from '@trpc/client'
import { createTRPCProxyClient } from '@trpc/client'
import type {
AnyRouter,
} from '@trpc/server'
import { createFlatProxy, createRecursiveProxy } from '@trpc/server/shared'
import { hash } from 'ohash'
import type { DecoratedProcedureRecord } from './types'
/**
* Calculates the key used for `useAsyncData` call
*/
export function getQueryKey(
path: string,
input: unknown,
): string {
return input === undefined ? path : `${path}-${hash(input || '')}`
}
/**
* @internal
*/
export function createNuxtProxyDecoration<TRouter extends AnyRouter>(name: string, client: inferRouterProxyClient<TRouter>) {
return createRecursiveProxy((opts) => {
const args = opts.args
const pathCopy = [name, ...opts.path]
// The last arg is for instance `.mutate` or `.query()`
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const lastArg = pathCopy.pop()!
const path = pathCopy.join('.')
const [input, asyncDataOptions] = args
const queryKey = getQueryKey(path, input)
return useAsyncDataWithError(queryKey, () => (client as any)[path][lastArg](input), asyncDataOptions)
})
}
/**
* Custom useAsyncData to add server error to client
*/
async function useAsyncDataWithError(queryKey: string, cb: any, asyncDataOptions: any) {
// @ts-ignore: nuxt internal
const serverError = useState(`error-${queryKey}`, () => null)
// @ts-ignore: nuxt internal
const { error, data, ...rest } = await useAsyncData(queryKey, cb, asyncDataOptions)
if (error.value && !serverError.value)
serverError.value = error.value as any
if (data.value)
serverError.value = null
return {
...rest,
data,
error: serverError,
}
}
export function createTRPCNuxtProxyClient<TRouter extends AnyRouter>(opts: CreateTRPCClientOptions<TRouter>) {
const client = createTRPCProxyClient(opts)
const decoratedClient = createFlatProxy((key) => {
return createNuxtProxyDecoration(key, client)
}) as DecoratedProcedureRecord<TRouter['_def']['record']>
return decoratedClient
}

View File

@@ -1,64 +0,0 @@
import type { TRPCClientErrorLike } from '@trpc/client'
import type {
AnyMutationProcedure,
AnyProcedure,
AnyQueryProcedure,
AnyRouter,
ProcedureRouterRecord,
inferProcedureInput,
inferProcedureOutput,
} from '@trpc/server'
import type {
AsyncData,
AsyncDataOptions,
KeyOfRes,
PickFrom,
_Transform,
} from 'nuxt/dist/app/composables/asyncData'
// Inspired by trpc/react-query client types
// https://github.com/trpc/trpc/blob/next/packages/react-query/src/createTRPCReact.tsx
/**
* @internal
*/
export type DecorateProcedure<
TProcedure extends AnyProcedure,
TPath extends string,
> = TProcedure extends AnyQueryProcedure
? {
query: <
TData = inferProcedureOutput<TProcedure>,
Transform extends _Transform<TData> = _Transform<TData, TData>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>,
>(
input: inferProcedureInput<TProcedure>,
opts?: AsyncDataOptions<TData, Transform, PickKeys>,
) => AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, TRPCClientErrorLike<TProcedure>>
} : TProcedure extends AnyMutationProcedure ? {
mutate: <
TData = inferProcedureOutput<TProcedure>,
Transform extends _Transform<TData> = _Transform<TData, TData>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>,
>(
input: inferProcedureInput<TProcedure>,
opts?: AsyncDataOptions<TData, Transform, PickKeys>,
) => AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, TRPCClientErrorLike<TProcedure>>
} : never
/**
* @internal
*/
export type DecoratedProcedureRecord<
TProcedures extends ProcedureRouterRecord,
TPath extends string = '',
> = {
[TKey in keyof TProcedures]: TProcedures[TKey] extends AnyRouter
? DecoratedProcedureRecord<
TProcedures[TKey]['_def']['record'],
`${TPath}${TKey & string}.`
>
: TProcedures[TKey] extends AnyProcedure
? DecorateProcedure<TProcedures[TKey], `${TPath}${TKey & string}`>
: never;
}

View File

@@ -1 +1,126 @@
export * from './server' import type { ResponseMeta } from '@trpc/server/http'
import { resolveHTTPResponse } from '@trpc/server/http'
import type {
AnyRouter,
ProcedureType,
inferRouterContext,
inferRouterError
} from '@trpc/server'
import {
TRPCError
} from '@trpc/server'
import { createURL } from 'ufo'
import type { H3Event } from 'h3'
import { createError, defineEventHandler, isMethod, readBody } from 'h3'
import type { TRPCResponse } from '@trpc/server/rpc'
type MaybePromise<T> = T | Promise<T>
export type CreateContextFn<TRouter extends AnyRouter> = (event: H3Event) => MaybePromise<inferRouterContext<TRouter>>
export interface ResponseMetaFnPayload<TRouter extends AnyRouter> {
data: TRPCResponse<unknown, inferRouterError<TRouter>>[]
ctx?: inferRouterContext<TRouter>
paths?: string[]
type: ProcedureType | 'unknown'
errors: TRPCError[]
}
export type ResponseMetaFn<TRouter extends AnyRouter> = (opts: ResponseMetaFnPayload<TRouter>) => ResponseMeta
export interface OnErrorPayload<TRouter extends AnyRouter> {
error: TRPCError
type: ProcedureType | 'unknown'
path: string | undefined
req: H3Event['node']['req']
input: unknown
ctx: undefined | inferRouterContext<TRouter>
}
export type OnErrorFn<TRouter extends AnyRouter> = (opts: OnErrorPayload<TRouter>) => void
export interface ResolveHTTPRequestOptions<TRouter extends AnyRouter> {
router: TRouter
createContext?: CreateContextFn<TRouter>
responseMeta?: ResponseMetaFn<TRouter>
onError?: OnErrorFn<TRouter>
batching?: {
enabled: boolean
}
}
function getPath (event: H3Event): string | null {
if (typeof event.context.params.trpc === 'string') { return event.context.params.trpc }
if (Array.isArray(event.context.params.trpc)) { return event.context.params.trpc.join('/') }
return null
}
export function createNuxtApiHandler<TRouter extends AnyRouter> ({
router,
createContext,
responseMeta,
onError,
batching
}: ResolveHTTPRequestOptions<TRouter>) {
return defineEventHandler(async (event) => {
const {
req,
res
} = event.node
const $url = createURL(req.url!)
const path = getPath(event)
if (path === null) {
const error = router.getErrorShape({
error: new TRPCError({
message:
'Param "trpc" not found - is the file named `[trpc]`.ts or `[...trpc].ts`?',
code: 'INTERNAL_SERVER_ERROR'
}),
type: 'unknown',
ctx: undefined,
path: undefined,
input: undefined
})
throw createError({
statusCode: 500,
statusMessage: JSON.stringify(error)
})
}
const httpResponse = await resolveHTTPResponse({
batching,
router,
req: {
method: req.method!,
headers: req.headers,
body: isMethod(event, 'GET') ? null : await readBody(event),
query: $url.searchParams
},
path,
createContext: async () => await createContext?.(event),
responseMeta,
onError: (o) => {
onError?.({
...o,
req
})
}
})
const { status, headers, body } = httpResponse
res.statusCode = status
headers && Object.keys(headers).forEach((key) => {
res.setHeader(key, headers[key]!)
})
return body
})
}

View File

@@ -1,90 +0,0 @@
import type { ResponseMeta } from '@trpc/server/http'
import { resolveHTTPResponse } from '@trpc/server/http'
import type {
AnyRouter,
ProcedureType,
TRPCError,
inferRouterContext,
inferRouterError,
} from '@trpc/server'
import { createURL } from 'ufo'
import type { H3Event } from 'h3'
import { defineEventHandler, isMethod, readBody } from 'h3'
import type { TRPCResponse } from '@trpc/server/rpc'
type MaybePromise<T> = T | Promise<T>
export type CreateContextFn<TRouter extends AnyRouter> = (event: H3Event) => MaybePromise<inferRouterContext<TRouter>>
export interface ResponseMetaFnPayload<TRouter extends AnyRouter> {
data: TRPCResponse<unknown, inferRouterError<TRouter>>[]
ctx?: inferRouterContext<TRouter>
paths?: string[]
type: ProcedureType | 'unknown'
errors: TRPCError[]
}
export type ResponseMetaFn<TRouter extends AnyRouter> = (opts: ResponseMetaFnPayload<TRouter>) => ResponseMeta
export interface OnErrorPayload<TRouter extends AnyRouter> {
error: TRPCError
type: ProcedureType | 'unknown'
path: string | undefined
req: H3Event['req']
input: unknown
ctx: undefined | inferRouterContext<TRouter>
}
export type OnErrorFn<TRouter extends AnyRouter> = (opts: OnErrorPayload<TRouter>) => void
export function createNuxtApiHandler<TRouter extends AnyRouter>({
router,
createContext,
responseMeta,
onError,
url = '/api/trpc',
}: {
router: TRouter
createContext?: CreateContextFn<TRouter>
responseMeta?: ResponseMetaFn<TRouter>
onError?: OnErrorFn<TRouter>
url?: string
}) {
return defineEventHandler(async (event) => {
const {
req,
res,
} = event
const $url = createURL(req.url!)
const httpResponse = await resolveHTTPResponse({
router,
req: {
method: req.method!,
headers: req.headers,
body: isMethod(event, 'GET') ? null : await readBody(event),
query: $url.searchParams,
},
path: $url.pathname.substring(url.length + 1),
createContext: async () => createContext?.(event),
responseMeta,
onError: (o) => {
onError?.({
...o,
req,
})
},
})
const { status, headers, body } = httpResponse
res.statusCode = status
headers && Object.keys(headers).forEach((key) => {
res.setHeader(key, headers[key]!)
})
return body
})
}

View File

@@ -1,10 +1,15 @@
import { defineConfig } from 'tsup' import { defineConfig } from 'tsup'
export default defineConfig({ export default defineConfig({
entry: ['src/index.ts', 'src/client/index.ts'], entry: ['src/index.ts'],
format: ['cjs', 'esm'], format: ['cjs', 'esm'],
splitting: false, splitting: false,
clean: true, clean: true,
external: ['#app'], external: ['#app', '#imports'],
dts: true, dts: true,
outExtension ({ format }) {
return {
js: format === 'esm' ? '.mjs' : `.${format}`
}
}
}) })