Compare commits

...

108 Commits

Author SHA1 Message Date
wobsoriano
ad85a78350 chore(release): v0.10.5 2023-05-27 06:13:49 -07:00
wobsoriano
892d167ff1 feat: pass input to watched sources if it's a ref 2023-05-27 06:13:45 -07:00
wobsoriano
e15ea6b5a3 chore(release): v0.10.4 2023-05-26 20:41:31 -07:00
wobsoriano
054fad952a feat: add reactive inputs 2023-05-26 20:41:22 -07:00
wobsoriano
e15a62a5b2 chore: remove engines property 2023-05-26 10:55:38 -07:00
wobsoriano
3f399f0c58 downgrade playground and docs nuxt version to 3.4.3 2023-05-26 10:54:59 -07:00
wobsoriano
247a6448cb set pnpm nuxt overrides to 3.5.0 2023-05-20 21:21:12 -07:00
wobsoriano
5301221148 reinstall docs 2023-05-20 21:20:11 -07:00
wobsoriano
7683499953 docs: update local deps 2023-05-20 20:06:48 -07:00
wobsoriano
2c28424ecd chore(release): v0.10.3 2023-05-20 11:11:11 -07:00
wobsoriano
18cd492ffd feat: export createH3ApiHandler for h3 apps 2023-05-20 11:11:07 -07:00
wobsoriano
c2ae94b4a7 chore(deps): bump h3 to 1.6.6 2023-05-20 11:09:04 -07:00
wobsoriano
94eb3c6257 replace bumpp with changelogen 2023-05-20 11:08:44 -07:00
wobsoriano
e55670169e remove unused imports in playground 2023-05-15 09:06:45 -07:00
wobsoriano
4f7fbbe108 chore: release v0.10.2 2023-05-15 09:03:34 -07:00
wobsoriano
c32fdf7893 feat: allow providing a transform output type 2023-05-15 09:00:04 -07:00
wobsoriano
10bc1d3a4d chore: release v0.10.1 2023-05-12 13:09:29 -07:00
wobsoriano
7bd62822d3 remove nuxt from peer deps 2023-05-12 13:09:24 -07:00
wobsoriano
967271089f fix docs build 2023-05-12 11:28:07 -07:00
wobsoriano
169c6aa534 chore: release v0.10.0 2023-05-12 11:26:02 -07:00
wobsoriano
2d025788ce feat(deps): add nuxt as a peer dependeny 2023-05-12 11:24:29 -07:00
wobsoriano
4af2b2ef3f feat(deps): bump @trpc/client and @trpc/server to 10.26.0 2023-05-12 11:20:45 -07:00
wobsoriano
ae0b7824f4 fix: asyncData types 2023-05-12 11:19:37 -07:00
wobsoriano
4339bf5059 fix: transform types 2023-05-12 11:07:44 -07:00
wobsoriano
252e2261e9 update pnpm nuxt overrides 2023-05-12 10:58:47 -07:00
wobsoriano
40eaf44f39 bump local deps 2023-05-12 10:42:46 -07:00
wobsoriano
ad827bb716 chore: release v0.9.0 2023-04-14 13:06:07 -07:00
wobsoriano
08125cbf0c fix types 2023-04-14 13:05:53 -07:00
wobsoriano
fb5723b071 feat(deps): bump @trpc/client and @trpc/server to 10.20.0 2023-04-14 11:13:38 -07:00
wobsoriano
8c35279cc2 bump local deps 2023-04-14 11:12:30 -07:00
Robert Soriano
61a5bcd2ee Merge pull request #88 from Colonel-Sandvich/next
Update `HTTPLinkOptions` import
2023-04-14 08:27:22 -07:00
Colonel-Sandvich
eaeab1565d Update HTTPLinkOptions import 2023-04-14 16:18:27 +01:00
Robert Soriano
e7453bfc70 Merge pull request #86 from danielroe/fix/github-module
fix: use latest `@nuxtlabs/github` module
2023-04-13 16:19:14 -07:00
Daniel Roe
54ef27d524 fix: use latest @nuxtlabs/github module 2023-04-13 23:54:49 +01:00
Robert Soriano
38d83dac34 Merge pull request #83 from CRBroughton/trpc-10.19.1
chore: bump dependencies for nuxt 3.3.3 + devtools 0.3.2
2023-04-07 20:37:57 -07:00
CRBroughton
0f7e52248f chore: bump dependencies for nuxt 3.3.3 2023-04-07 21:38:55 +01:00
wobsoriano
be55641338 fix: trpc-nuxt docs 2023-04-03 16:04:08 -07:00
wobsoriano
6ba6e70b92 chore: release v0.8.0 2023-03-27 22:55:56 -07:00
wobsoriano
b710f809c7 bump local deps 2023-03-27 22:54:26 -07:00
Robert Soriano
4f647afa36 Merge pull request #80 from CRBroughton/nuxt-3.2.2-support 2023-03-27 21:25:00 -07:00
CRBroughton
1defbe5954 chore: upgrade dependencies for nuxt 3.2.2 2023-03-27 21:48:14 +01:00
Robert Soriano
fa6ba65d35 Merge pull request #76 from robinWongM/hotfix/subscribe-not-working
fix: pass all arguments to proxied trpc client method
2023-03-18 13:56:08 -07:00
Robert Soriano
d4af221e89 Merge pull request #75 from kedniko/patch-1
Update 2.recommended.md
2023-03-18 13:55:45 -07:00
Robin Wong
6595a1d306 fix: pass all arguments to proxied trpc client method 2023-03-18 03:15:49 +08:00
kedniko
b506cd5f14 Update 2.recommended.md 2023-03-10 18:31:26 +01:00
wobsoriano
7a69573fb9 chore: release v0.7.0 2023-02-20 09:18:07 -08:00
wobsoriano
8e26e781d4 fix type errors 2023-02-20 09:18:03 -08:00
wobsoriano
8efb63e70c bump tsup to 6.6.3 2023-02-20 09:09:10 -08:00
wobsoriano
4f6c07b0a1 feat(deps): require trpc >10.12.0 2023-02-20 09:08:44 -08:00
wobsoriano
aa3fe5527d feat(deps): bump ufo to 1.1.0 2023-02-20 09:07:44 -08:00
wobsoriano
6f17913eab feat(deps): bump ofetch to 1.0.1 2023-02-20 09:07:18 -08:00
wobsoriano
d6c60cb2b3 feat(deps): bump h3 to 1.5.0 2023-02-20 09:06:56 -08:00
wobsoriano
76fbd59525 chore: release v0.6.0 2023-02-04 21:01:26 -08:00
wobsoriano
57faf8749a feat(deps): bump h3 to 1.1.0 2023-02-04 21:00:25 -08:00
wobsoriano
9456342d58 remove unused deps 2023-02-04 20:59:51 -08:00
wobsoriano
f89c8e44ef feat(deps): bump @trpc/client and @trpc/server to 10.10.0 2023-02-04 20:59:39 -08:00
wobsoriano
7f156806d8 add Acknowledgements section 2023-02-01 08:49:10 -08:00
wobsoriano
bd33589d2f add funding.yml 2023-01-31 20:03:14 -08:00
Robert Soriano
16b6ed315a Merge pull request #68 from CodyBontecou/patch-1
Update 1.simple.md
2023-01-25 22:42:38 -05:00
Cody Bontecou
6e74e44233 Update 1.simple.md
Updated to use destructuring syntax.
2023-01-25 15:18:16 -10:00
Cody Bontecou
6485aa42a7 Update 1.simple.md
Walking through the setup with Nuxt v3.1.0 requires accessing `.value` within the template.
2023-01-25 13:00:50 -10:00
Robert Soriano
051d5bb325 Update 1.composables.md 2023-01-09 10:33:17 -08:00
Robert Soriano
b7d4dc7642 Update 1.composables.md 2023-01-09 10:33:07 -08:00
wobsoriano
abb351268a chore: release v0.5.0 2023-01-06 17:51:36 -08:00
wobsoriano
c1a791558e fix: link types 2023-01-06 17:32:16 -08:00
wobsoriano
14a18a783e chore: bump peerDependencies between @trpc/* packages 2023-01-06 16:53:42 -08:00
wobsoriano
209c28513d add getQueryKey doc 2022-12-27 17:12:31 -08:00
Robert Soriano
6157d51d59 Delete public directory 2022-12-26 01:21:06 -08:00
Robert Soriano
0effea5efc Update README.md 2022-12-26 01:20:42 -08:00
Robert Soriano
f6f79ddab4 Merge pull request #58 from therealokoro/next
feature-request: add logo, favicon and cover image to docs
2022-12-26 01:15:44 -08:00
Okoro Redemption
b4eea4393e docs: added logo, cover image, favicon 2022-12-23 21:01:25 +01:00
wobsoriano
ec576865c1 docs: update installation 2022-12-21 09:21:31 -08:00
wobsoriano
83aeeb7332 chore: update deps 2022-12-21 09:19:24 -08:00
wobsoriano
fd977982ec playground cleanup 2022-12-20 20:21:41 -08:00
Robert Soriano
e5178d1634 Update 1.installation.md 2022-12-19 21:49:38 -08:00
wobsoriano
6191a1f925 docs: update homepage 2022-12-19 20:43:23 -08:00
wobsoriano
c100ee171c playground: update build.transpile 2022-12-19 20:41:04 -08:00
wobsoriano
949e122be7 docs: update homepage 2022-12-19 20:40:00 -08:00
wobsoriano
410dcf8a11 docs: fix AppRouter import in simple usage 2022-12-19 20:24:54 -08:00
wobsoriano
a8138602a2 docs: update build.transpile instructions 2022-12-19 20:22:04 -08:00
wobsoriano
bb2d1ef8dd chore: update deps 2022-12-19 11:27:15 -08:00
wobsoriano
7c8664d37d chore: release v0.4.4 2022-12-18 23:43:20 -08:00
Robert Soriano
be20e63b79 Merge pull request #54 from wobsoriano/with-client
Bringing back tRPC Client
2022-12-18 23:38:54 -08:00
wobsoriano
5b1cdb6846 update installation 2022-12-18 23:33:47 -08:00
wobsoriano
80573fb32e move links outside of client 2022-12-18 23:31:55 -08:00
wobsoriano
f01b8d5d8d docs: improve docs 2022-12-18 23:25:46 -08:00
wobsoriano
09e0a6c479 feat: option to select headers to pass to useRequestHeaders in http links 2022-12-18 22:35:36 -08:00
wobsoriano
a5cb0c754e move links to another file 2022-12-18 22:22:15 -08:00
wobsoriano
ff88817168 feat: add convenience link wrappers 2022-12-18 22:17:37 -08:00
wobsoriano
f1ca99521e remove unused imports 2022-12-18 21:53:20 -08:00
wobsoriano
143092a16a add engines property 2022-12-18 21:51:03 -08:00
wobsoriano
27f7ed5fb4 eslint fix 2022-12-18 21:50:10 -08:00
wobsoriano
cf38d0a13b chore: update deps 2022-12-18 21:49:45 -08:00
wobsoriano
c14a154723 update example 2022-12-18 21:42:56 -08:00
wobsoriano
0f00e561a1 docs: add simple and recommended usage pages 2022-12-18 21:42:12 -08:00
wobsoriano
0f3c0fff39 docs: bump @nuxt-themes/docus to 1.1.10 2022-12-18 20:13:54 -08:00
wobsoriano
483b6e8076 feat: remove useMutation 2022-12-18 19:43:51 -08:00
wobsoriano
491c04739a cleanup 2022-12-18 16:14:59 -08:00
wobsoriano
b1ddfc146b cleanup 2022-12-18 15:36:11 -08:00
wobsoriano
ee85f3ccd1 feat: add custom Nuxt client 2022-12-18 15:35:56 -08:00
Robert Soriano
d8d4c92ae8 Merge pull request #53 from cawa-93/patch-1
docs: Add missed import in code sample
2022-12-14 23:21:48 -08:00
Alex Kozack
00da42d77e docs: Add missed import in code sample 2022-12-13 18:07:15 +02:00
Robert Soriano
3db1100c87 Merge pull request #51 from cawa-93/patch-1
docs: Replace regular `fetch` with a `$fetch` from nuxt
2022-12-12 20:42:12 -08:00
Robert Soriano
fdde3f1db4 Merge pull request #52 from cawa-93/next
docs: Infer `Context` type
2022-12-12 19:39:41 -08:00
Alex Kozack
4ff57dfb97 docs: Infer Context type 2022-12-12 15:56:53 +02:00
Alex Kozack
aba32c7541 docs: Replace regular fetch with a $fetch from nuxt
This feature was implementet in https://github.com/wobsoriano/trpc-nuxt/pull/28 and describe better way to work with ssr
2022-12-12 15:17:28 +02:00
Robert Soriano
01c2bd5701 Merge pull request #47 from nkhdo/patch-2
Update 1.installation.md
2022-11-28 10:51:57 -08:00
Hoang Do
fe2e45be70 Update 1.installation.md
`@trpc/server` and `@trpc/client` are now pointing to v10, while the `next` tag is deprecated
2022-11-28 17:01:59 +07:00
35 changed files with 6274 additions and 4131 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: wobsoriano

50
CHANGELOG.md Normal file
View File

@@ -0,0 +1,50 @@
# Changelog
## v0.10.5
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.4...v0.10.5)
### 🚀 Enhancements
- Pass input to watched sources if it's a ref ([892d167](https://github.com/wobsoriano/trpc-nuxt/commit/892d167))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))
## v0.10.4
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.3...v0.10.4)
### 🚀 Enhancements
- Add reactive inputs ([054fad9](https://github.com/wobsoriano/trpc-nuxt/commit/054fad9))
### 📖 Documentation
- Update local deps ([7683499](https://github.com/wobsoriano/trpc-nuxt/commit/7683499))
### 🏡 Chore
- Remove engines property ([e15a62a](https://github.com/wobsoriano/trpc-nuxt/commit/e15a62a))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))
## v0.10.3
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.2...v0.10.3)
### 🚀 Enhancements
- Export createH3ApiHandler for h3 apps ([18cd492](https://github.com/wobsoriano/trpc-nuxt/commit/18cd492))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))

View File

@@ -1,3 +1,9 @@
<p align="center">
<figure>
<img src="https://trpc-nuxt.vercel.app/cover.jpg" alt="trpc-nuxt cover image" />
</figure>
</p>
# tRPC-Nuxt # 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.
@@ -19,7 +25,11 @@ For version 3 of this module (tRPC v9, auto-imports, auto handlers), [go here](h
## Recommended IDE Setup ## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) - [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
## Acknowledgements
Huge thanks to [Alex / KATT](https://github.com/KATT), the author of [tRPC](https://trpc.io/), for being the first sponsor of this project! 🎉
## License ## License

1
client.d.ts vendored Normal file
View File

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

View File

@@ -1,25 +1,38 @@
export default defineAppConfig({ export default defineAppConfig({
docus: { docus: {
title: 'tRPC Nuxt', title: "tRPC Nuxt",
image: 'https://og-image.vercel.app/tRPC-Nuxt', description: "End-to-end typesafe APIs in Nuxt applications.",
alt: 'tRPC-Nuxt cover', image: "https://og-image.vercel.app/tRPC-Nuxt",
url: 'https://trpc-nuxt.vercel.app', alt: "tRPC-Nuxt cover",
debug: false, url: "https://trpc-nuxt.vercel.app",
socials: { debug: false,
github: 'wobsoriano/trpc-nuxt' socials: {
}, github: "wobsoriano/trpc-nuxt"
aside: { },
level: 1 cover: {
}, src: "/cover.jpg",
footer: { alt: "tRPC-Nuxt module"
credits: true, },
icons: [ header: {
{ logo: true
label: 'NuxtJS', },
href: 'https://nuxtjs.org', aside: {
component: 'IconNuxt' level: 1
} },
] footer: {
} credits: {
} icon: "IconDocus",
text: "Powered by Docus",
href: "https://docus.com"
},
iconLinks: [
{
label: "NuxtJS",
href: "https://nuxt.com",
// @ts-expect-error: IDK nuxt
component: "IconNuxtLabs"
}
]
}
}
}) })

99
docs/components/Logo.vue Normal file
View File

@@ -0,0 +1,99 @@
<template>
<div class="logo">
<div v-if="$colorMode.value == 'dark'">
<svg
width="40"
height="40"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 509 454.27"
>
<path
d="M363.3,307.79v.31l.28-.48Z"
fill="#fff"
stroke="#fff"
stroke-miterlimit="10"
stroke-width="9"
/>
<path
d="M16.88,307.79v.3l.27-.46ZM83.1,350.1l.15.25.14-.08Z"
fill="#fff"
stroke="#fff"
stroke-miterlimit="10"
stroke-width="9"
/>
<path
d="M433.22,259.49V139.58l-102-58.85V49.53L254.48,5.2,251.38,7,177.69,49.53V80.79L75.85,139.58V259.44L4.5,300.64v88.68l76.79,44.33,73.27-42.3,100,57.73,100-57.71,73.23,42.28,76.79-44.33V300.64ZM254.48,19.5l57.89,33.41L254,86.37C238.27,77,212.74,61.79,197.24,52.54Zm64.41,43.94v67.62l-58.22,33.61V96.81Zm-128.82-.75c13.83,8.23,44,26.24,58.22,34.69v67.29l-58.22-33.61ZM16.88,307.79l.27-.16-.27.46Zm58.22,108L16.88,382.16V313.8L75.1,348.49Zm5.7-78.29c.71.41-55.37-33-56.76-33.82L81.29,270.6,139.18,304Zm2.45,12.87-.15-.25.29.17Zm62.45,31.81-3.52,2-54.7,31.57V347.92l58.22-33.37Zm205.22-81.52v78.49l-96.39,55.65-96.45-55.69V300.64L88.23,260.32V146.73l89.46-51.65V138.2l76.79,44.33,76.79-44.33V95l89.57,51.71V260.27Zm12.66,7-.28.48v-.31Zm57.94,108.15-54.65-31.55-3.57-2.06V313.79l58.22,34.7Zm5.7-78.29-56.76-33.82,57.25-33.06L485.59,304Zm64.9,44.68L433.9,415.77V347.92l58.22-33.37Z"
fill="#fff"
stroke="#fff"
stroke-miterlimit="10"
stroke-width="9"
/>
<path
d="M83.1,350.1l.15.25.14-.08Zm0,0,.15.25.14-.08Zm0,0,.15.25.14-.08Z"
fill="#fff"
stroke="#fff"
stroke-miterlimit="10"
stroke-width="9"
/>
<path
d="M322.58,277.47l-34.44-57.86a9.2,9.2,0,0,0-3.4-3.3,9.47,9.47,0,0,0-12.67,3.3l-8.81,14.8L246,205.46c-3.46-6-12.61-6-16.07,0l-42.86,72a8.78,8.78,0,0,0,0,9,9.33,9.33,0,0,0,8,4.5h32.19c12.76,0,22.16-5.43,28.64-16,5.44-9.14,18.84-31.66,24.13-40.53l25.26,42.44H271.68L263.26,291h51.29C321.45,291.06,326.22,283.5,322.58,277.47Zm-79-10c-4.3,6.87-9.18,9.38-16.76,9.38H204.34L238,220.24l16.8,28.3Z"
fill="#fff"
stroke="#fff"
stroke-miterlimit="10"
stroke-width="9"
/>
</svg>
</div>
<div v-else>
<svg
width="40"
height="40"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 509 454.27"
>
<path
d="M363.3,307.79v.31l.28-.48Z"
stroke="#000"
stroke-miterlimit="10"
stroke-width="9"
/>
<path
d="M16.88,307.79v.3l.27-.46ZM83.1,350.1l.15.25.14-.08Z"
stroke="#000"
stroke-miterlimit="10"
stroke-width="9"
/>
<path
d="M433.22,259.49V139.58l-102-58.85V49.53L254.48,5.2,251.38,7,177.69,49.53V80.79L75.85,139.58V259.44L4.5,300.64v88.68l76.79,44.33,73.27-42.3,100,57.73,100-57.71,73.23,42.28,76.79-44.33V300.64ZM254.48,19.5l57.89,33.41L254,86.37C238.27,77,212.74,61.79,197.24,52.54Zm64.41,43.94v67.62l-58.22,33.61V96.81Zm-128.82-.75c13.83,8.23,44,26.24,58.22,34.69v67.29l-58.22-33.61ZM16.88,307.79l.27-.16-.27.46Zm58.22,108L16.88,382.16V313.8L75.1,348.49Zm5.7-78.29c.71.41-55.37-33-56.76-33.82L81.29,270.6,139.18,304Zm2.45,12.87-.15-.25.29.17Zm62.45,31.81-3.52,2-54.7,31.57V347.92l58.22-33.37Zm205.22-81.52v78.49l-96.39,55.65-96.45-55.69V300.64L88.23,260.32V146.73l89.46-51.65V138.2l76.79,44.33,76.79-44.33V95l89.57,51.71V260.27Zm12.66,7-.28.48v-.31Zm57.94,108.15-54.65-31.55-3.57-2.06V313.79l58.22,34.7Zm5.7-78.29-56.76-33.82,57.25-33.06L485.59,304Zm64.9,44.68L433.9,415.77V347.92l58.22-33.37Z"
stroke="#000"
stroke-miterlimit="10"
stroke-width="9"
/>
<path
d="M83.1,350.1l.15.25.14-.08Zm0,0,.15.25.14-.08Zm0,0,.15.25.14-.08Z"
stroke="#000"
stroke-miterlimit="10"
stroke-width="9"
/>
<path
d="M322.58,277.47l-34.44-57.86a9.2,9.2,0,0,0-3.4-3.3,9.47,9.47,0,0,0-12.67,3.3l-8.81,14.8L246,205.46c-3.46-6-12.61-6-16.07,0l-42.86,72a8.78,8.78,0,0,0,0,9,9.33,9.33,0,0,0,8,4.5h32.19c12.76,0,22.16-5.43,28.64-16,5.44-9.14,18.84-31.66,24.13-40.53l25.26,42.44H271.68L263.26,291h51.29C321.45,291.06,326.22,283.5,322.58,277.47Zm-79-10c-4.3,6.87-9.18,9.38-16.76,9.38H204.34L238,220.24l16.8,28.3Z"
stroke="#000"
stroke-miterlimit="10"
stroke-width="9"
/>
</svg>
</div>
<ProseStrong>tRPC Nuxt</ProseStrong>
</div>
</template>
<style scoped>
.logo {
display: flex;
align-items: center;
gap: 10px;
font-weight: 800;
}
</style>

View File

@@ -8,19 +8,27 @@ description: tRPC-Nuxt provides first class integration with tRPC.
::code-group ::code-group
```bash [pnpm] ```bash [pnpm]
pnpm add @trpc/server@next @trpc/client@next trpc-nuxt zod pnpm add @trpc/server @trpc/client trpc-nuxt zod
``` ```
```bash [npm] ```bash [npm]
npm install @trpc/server@next @trpc/client@next trpc-nuxt zod npm install @trpc/server @trpc/client trpc-nuxt zod
``` ```
```bash [yarn] ```bash [yarn]
yarn add @trpc/server@next @trpc/client@next trpc-nuxt zod yarn add @trpc/server @trpc/client trpc-nuxt zod
``` ```
:: ::
```ts [nuxt.config.ts]
export default defineNuxtConfig({
build: {
transpile: ['trpc-nuxt']
}
})
```
#### Why @trpc/server? #### Why @trpc/server?
For implementing tRPC endpoints and routers. For implementing tRPC endpoints and routers.

View File

@@ -0,0 +1,118 @@
---
title: Simple
description: tRPC-Nuxt provides first class integration with tRPC.
---
# Simple Usage
## 1. Create a tRPC router
Initialize your tRPC backend using the `initTRPC` function and create your first router.
::code-group
```ts [server/trpc/trpc.ts]
/**
* This is your entry point to setup the root configuration for tRPC on the server.
* - `initTRPC` should only be used once per app.
* - We export only the functionality that we use so we can enforce which base procedures should be used
*
* Learn how to create protected base procedures and other things below:
* @see https://trpc.io/docs/v10/router
* @see https://trpc.io/docs/v10/procedures
*/
import { initTRPC } from '@trpc/server'
const t = initTRPC.create()
/**
* Unprotected procedure
**/
export const publicProcedure = t.procedure;
export const router = t.router;
export const middleware = t.middleware;
```
```ts [server/api/trpc/[trpc].ts]
/**
* This is the API-handler of your app that contains all your API routes.
* On a bigger app, you will probably want to split this file up into multiple files.
*/
import { createNuxtApiHandler } from 'trpc-nuxt'
import { publicProcedure, router } from '~/server/trpc/trpc'
import { z } from 'zod'
export const appRouter = router({
hello: publicProcedure
// This is the input schema of your procedure
.input(
z.object({
text: z.string().nullish(),
}),
)
.query(({ input }) => {
// This is what you're returning to your client
return {
greeting: `hello ${input?.text ?? 'world'}`,
}
}),
})
// export only the type definition of the API
// None of the actual implementation is exposed to the client
export type AppRouter = typeof appRouter;
// export API handler
export default createNuxtApiHandler({
router: appRouter,
createContext: () => ({}),
})
```
::
## 2. Create tRPC client plugin
Create a strongly-typed plugin using your API's type signature.
```ts [plugins/client.ts]
import { createTRPCNuxtClient, httpBatchLink } from 'trpc-nuxt/client'
import type { AppRouter } from '~/server/api/trpc/[trpc]'
export default defineNuxtPlugin(() => {
/**
* createTRPCNuxtClient adds a `useQuery` composable
* built on top of `useAsyncData`.
*/
const client = createTRPCNuxtClient<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
})
return {
provide: {
client,
},
}
})
```
## 3. Make an API request
```vue [pages/index.vue]
<script setup lang="ts">
const { $client } = useNuxtApp()
const { data: hello } = await $client.hello.useQuery({ text: 'client' })
</script>
<template>
<div>
<p>{{ hello?.greeting }}</p>
</div>
</template>
```

View File

@@ -1,13 +1,11 @@
--- ---
title: Usage title: Recommended
description: tRPC-Nuxt provides first class integration with tRPC. description: tRPC-Nuxt provides first class integration with tRPC.
--- ---
# Usage # Recommended Usage
## Recommended file structure Recommended but not enforced file structure.
Recommended but not enforced file structure. This is what you get when starting from [the examples](../main/example-apps.md).
```graphql ```graphql
. .
@@ -24,7 +22,7 @@ Recommended but not enforced file structure. This is what you get when starting
│ │ ├── context.ts # <-- create app context │ │ ├── context.ts # <-- create app context
│ │ └── trpc.ts # <-- procedure helpers │ │ └── trpc.ts # <-- procedure helpers
├── plugins ├── plugins
│ ├── client.ts # <-- tRPC Client as a plugin │ ├── client.ts # <-- tRPC client plugin
└── [..] └── [..]
``` ```
@@ -35,16 +33,26 @@ 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]
/**
* This is your entry point to setup the root configuration for tRPC on the server.
* - `initTRPC` should only be used once per app.
* - We export only the functionality that we use so we can enforce which base procedures should be used
*
* Learn how to create protected base procedures and other things below:
* @see https://trpc.io/docs/v10/router
* @see https://trpc.io/docs/v10/procedures
*/
import { initTRPC } from '@trpc/server' import { initTRPC } from '@trpc/server'
import { Context } from '~/server/trpc/context'
// Avoid exporting the entire t-object since it's not very const t = initTRPC.context<Context>().create()
// descriptive and can be confusing to newcomers used to t
// meaning translation in i18n libraries.
const t = initTRPC.create()
// Base router and procedure helpers /**
export const router = t.router * Unprotected procedure
export const publicProcedure = t.procedure **/
export const publicProcedure = t.procedure;
export const router = t.router;
export const middleware = t.middleware;
``` ```
```ts [server/trpc/routers/index.ts] ```ts [server/trpc/routers/index.ts]
@@ -71,15 +79,28 @@ 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 '@/server/trpc/routers' import { appRouter } from '~/server/trpc/routers'
import { createContext } from '~/server/trpc/context'
// export API handler // export API handler
export default createNuxtApiHandler({ export default createNuxtApiHandler({
router: appRouter, router: appRouter,
createContext: () => ({}), createContext,
}) })
``` ```
```ts [server/trpc/context.ts]
import { inferAsyncReturnType } from '@trpc/server'
/**
* Creates context for an incoming request
* @link https://trpc.io/docs/context
*/
export const createContext = () => ({})
export type Context = inferAsyncReturnType<typeof createContext>;
```
:: ::
::alert{type=info} ::alert{type=info}
@@ -91,18 +112,18 @@ If you need to split your router into several subrouters, you can implement them
Create a strongly-typed plugin 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, createTRPCProxyClient } from '@trpc/client' import { createTRPCNuxtClient, httpBatchLink } 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 = createTRPCProxyClient<AppRouter>({ /**
* createTRPCNuxtClient adds a `useQuery` composable
* built on top of `useAsyncData`.
*/
const client = createTRPCNuxtClient<AppRouter>({
links: [ links: [
httpBatchLink({ httpBatchLink({
/** url: '/api/trpc',
* If you want to use SSR, you need to use the server's full URL
* @link https://trpc.io/docs/ssr
**/
url: 'http://localhost:3000/api/trpc',
}), }),
], ],
}) })
@@ -115,16 +136,18 @@ export default defineNuxtPlugin(() => {
}) })
``` ```
## 3. Make API requests ## 3. Make an API request
```vue [pages/index.vue] ```vue [pages/index.vue]
<script setup lang="ts"> <script setup lang="ts">
const { $client } = useNuxtApp() const { $client } = useNuxtApp()
const data = await $client.hello.query({ text: 'client' }) const hello = await $client.hello.useQuery({ text: 'client' })
</script> </script>
<template> <template>
<p>{{data?.greeting }}</p> <div>
<p>{{ hello.data?.greeting }}</p>
</div>
</template> </template>
``` ```

View File

@@ -0,0 +1,53 @@
---
title: Client
description: tRPC-Nuxt provides first class integration with tRPC.
---
# Nuxt client
The magic of tRPC is making strongly typed API calls without relying on code generation. With full-stack TypeScript projects, you can directly import types from the server into the client! This is a vital part of how tRPC works.
## Initialize a tRPC client
Create a typesafe client via a Nuxt [plugin](https://nuxt.com/docs/guide/directory-structure/plugins) with the `createTRPCNuxtClient` method from `trpc-nuxt/client`, and add a `links` array with a [terminating link](https://trpc.io/docs/links#the-terminating-link). If you want to learn more about tRPC links, check out the docs [here](https://trpc.io/docs/links):
::alert{type="info"}
`createTRPCNuxtClient` extends [createTRPCProxyClient](https://trpc.io/docs/vanilla#initialize-a-trpc-client) and adds a `useQuery` method built on top of [useAsyncData](https://nuxt.com/docs/api/composables/use-async-data).
::
```ts [plugins/client.ts]
import { createTRPCNuxtClient, httpBatchLink } from 'trpc-nuxt/client'
import type { AppRouter } from '~/server/trpc/routers'
export default defineNuxtPlugin(() => {
const client = createTRPCNuxtClient<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
})
return {
provide: {
client,
},
}
})
```
As you can see, we passed `AppRouter` as a type argument of `createTRPCNuxtClient`. This returns a strongly typed `client` instance, a proxy that mirrors the structure of your `AppRouter` on the client:
```vue [pages/index.vue]
<script setup lang="ts">
const { $client } = useNuxtApp()
const getUser = await $client.getUser.useQuery('id_bilbo');
// => { data: { id: 'id_bilbo', name: 'Bilbo' }, pending: false, error: false };
const bilbo = await $client.getUser.query('id_bilbo');
// => { id: 'id_bilbo', name: 'Bilbo' };
const frodo = await $client.createUser.mutate({ name: 'Frodo' });
// => { id: 'id_frodo', name: 'Frodo' };
</script>
```

View File

@@ -0,0 +1,58 @@
---
title: HTTP Link
description: httpLink is a terminating link that sends a tRPC operation to a tRPC procedure over HTTP.
---
# HTTP Link
`httpLink` is a [terminating link](https://trpc.io/docs/links#the-terminating-link) that sends a tRPC operation to a tRPC procedure over HTTP.
`httpLink` supports both POST and GET requests.
::alert{type="info"}
`httpLink` imported from `trpc-nuxt/client` is a convenience wrapper around the original `httpLink` that replaces regular `fetch` with a [`$fetch`](https://nuxt.com/docs/api/utils/dollarfetch) from Nuxt. It also sets the default headers using [`useRequestHeaders`](https://nuxt.com/docs/api/composables/use-request-headers#userequestheaders).
::
## Usage
You can import and add the `httpLink` to the `links` array as such:
```ts
import { createTRPCNuxtClient, httpLink } from 'trpc-nuxt/client'
import type { AppRouter } from '~/server/trpc/routers'
const client = createTRPCNuxtClient<AppRouter>({
links: [
httpLink({
url: '/api/trpc',
}),
],
})
```
## `httpLink` Options
The `httpLink` function takes an options object that has the `HTTPLinkOptions` shape.
```ts
export interface HTTPLinkOptions {
url: string;
/**
* Select headers to pass to `useRequestHeaders`.
*/
pickHeaders?: string[];
/**
* Add ponyfill for fetch.
*/
fetch?: typeof fetch;
/**
* Add ponyfill for AbortController
*/
AbortController?: typeof AbortController | null;
/**
* Headers to be set on outgoing requests or a callback that of said headers
* @link http://trpc.io/docs/v10/header
*/
headers?: HTTPHeaders | (() => HTTPHeaders | Promise<HTTPHeaders>);
}
```

View File

@@ -0,0 +1,88 @@
---
title: HTTP Batch Link
description: httpBatchLink is a terminating link that batches an array of individual tRPC operations into a single HTTP request that's sent to a single tRPC procedure.
---
# HTTP Batch Link
`httpBatchLink` is a [terminating link](https://trpc.io/docs/links#the-terminating-link) that batches an array of individual tRPC operations into a single HTTP request that's sent to a single tRPC procedure.
::alert{type="info"}
`httpBatchLink` imported from `trpc-nuxt/client` is a convenience wrapper around the original `httpBatchLink` that replaces regular `fetch` with a [`$fetch`](https://nuxt.com/docs/api/utils/dollarfetch) from Nuxt. It also sets the default headers using [`useRequestHeaders`](https://nuxt.com/docs/api/composables/use-request-headers#userequestheaders).
::
## Usage
You can import and add the `httpBatchLink` to the `links` array as such:
```ts
import { createTRPCNuxtClient, httpBatchLink } from 'trpc-nuxt/client'
import type { AppRouter } from '~/server/trpc/routers'
const client = createTRPCNuxtClient<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
})
```
After that, you can make use of batching by setting all your procedures in a `Promise.all`. The code below will produce exactly one HTTP request and on the server exactly `one` database query:
```ts
const somePosts = await Promise.all([
$client.post.byId.query(1);
$client.post.byId.query(2);
$client.post.byId.query(3);
])
```
## `httpBatchLink` Options
The `httpBatchLink` function takes an options object that has the `HTTPBatchLinkOptions` shape.
```ts
export interface HttpBatchLinkOptions extends HTTPLinkOptions {
maxURLLength?: number;
}
export interface HTTPLinkOptions {
url: string;
/**
* Select headers to pass to `useRequestHeaders`.
*/
pickHeaders?: string[];
/**
* Add ponyfill for fetch.
*/
fetch?: typeof fetch;
/**
* Add ponyfill for AbortController
*/
AbortController?: typeof AbortController | null;
/**
* Headers to be set on outgoing requests or a callback that of said headers
* @link http://trpc.io/docs/v10/header
*/
headers?: HTTPHeaders | (() => HTTPHeaders | Promise<HTTPHeaders>);
}
```
## Setting a maximum URL length
When sending batch requests, sometimes the URL can become too large causing HTTP errors like [413 Payload Too Large](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413), [414 URI Too Long](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/414), and [404 Not Found](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404). The `maxURLLength` option will limit the number of requests that can be sent together in a batch.
```ts
import { createTRPCNuxtClient, httpBatchLink } from 'trpc-nuxt/client';
import type { AppRouter } from '~/server/trpc/routers'
const client = createTRPCNuxtClient<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc',
maxURLLength: 2083, // a suitable size
}),
],
});
```

View File

@@ -6,6 +6,10 @@ title: 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. 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.
::alert{type="info"}
[createTRPCNuxtClient](/get-started/client/create) already adds a `useQuery` method built on top of [useAsyncData](https://nuxt.com/docs/api/composables/use-async-data/). You might not need this.
::
## Inference Helpers ## Inference Helpers
`@trpc/server` exports the following helper types to assist with inferring these types from the `AppRouter` exported by your `@trpc/server` router: `@trpc/server` exports the following helper types to assist with inferring these types from the `AppRouter` exported by your `@trpc/server` router:

View File

@@ -6,6 +6,10 @@ title: Headers
We can use the built-in [useRequestHeaders](https://v3.nuxtjs.org/api/composables/use-request-headers/) to set outgoing request headers: We can use the built-in [useRequestHeaders](https://v3.nuxtjs.org/api/composables/use-request-headers/) to set outgoing request headers:
::alert{type="info"}
[createTRPCNuxtClient](/get-started/client/create) has this feature by default.
::
```ts [plugins/client.ts] ```ts [plugins/client.ts]
export default defineNuxtPlugin(() => { export default defineNuxtPlugin(() => {
const headers = useRequestHeaders() const headers = useRequestHeaders()

View File

@@ -27,5 +27,6 @@ End-to-end typesafe APIs in Nuxt applications.
- Automatic typesafety - Automatic typesafety
- Snappy DX - Snappy DX
- Autocompletion on the client, for inputs, outputs and errors - Autocompletion on the client, for inputs, outputs and errors
- Leverages [useAsyncData](https://nuxt.com/docs/api/composables/use-async-data) and [$fetch](https://nuxt.com/docs/api/utils/dollarfetch)
:: ::
:: ::

View File

@@ -7,11 +7,9 @@
"generate": "nuxi build", "generate": "nuxi build",
"preview": "nuxi preview" "preview": "nuxi preview"
}, },
"dependencies": {
"nuxt": "^3.0.0"
},
"devDependencies": { "devDependencies": {
"@nuxt-themes/docus": "^0.3.1", "@nuxt-themes/docus": "^1.12.0",
"@nuxtlabs/github-module": "^1.5.3" "@nuxtlabs/github-module": "^1.6.3",
"nuxt": "3.4.3"
} }
} }

BIN
docs/public/cover.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -2,53 +2,61 @@
"name": "trpc-nuxt", "name": "trpc-nuxt",
"description": "End-to-end typesafe APIs in Nuxt applications.", "description": "End-to-end typesafe APIs in Nuxt applications.",
"type": "module", "type": "module",
"version": "0.4.3", "packageManager": "pnpm@8.5.1",
"version": "0.10.5",
"license": "MIT", "license": "MIT",
"sideEffects": false, "sideEffects": false,
"exports": { "exports": {
".": { ".": {
"require": "./dist/index.cjs", "require": "./dist/index.cjs",
"import": "./dist/index.mjs" "import": "./dist/index.mjs"
},
"./client": {
"types": "./dist/client/index.d.ts",
"require": "./dist/client/index.cjs",
"import": "./dist/client/index.mjs"
} }
}, },
"main": "./dist/index.mjs", "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": {
"dev": "concurrently \"pnpm build -- --watch\" \"pnpm --filter playground dev\"", "dev": "tsup --watch --onSuccess \"pnpm --filter playground dev\"",
"dev:prepare": "pnpm build && nuxt prepare playground", "dev:prepare": "pnpm build && nuxt prepare playground",
"prepublishOnly": "pnpm build", "prepublishOnly": "pnpm build",
"build": "tsup", "build": "tsup",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"release": "bumpp && npm publish" "release": "changelogen --release && npm publish && git push --follow-tags",
"update-deps": "taze -w && pnpm i"
}, },
"peerDependencies": { "peerDependencies": {
"@trpc/client": "^10.0.0", "@trpc/client": "^10.26.0",
"@trpc/server": "^10.0.0" "@trpc/server": "^10.26.0"
}, },
"dependencies": { "dependencies": {
"h3": "^1.0.1", "h3": "^1.6.6",
"nanoid": "^4.0.0", "ofetch": "^1.0.1",
"ohash": "^1.0.0", "ohash": "^1.1.2",
"ufo": "^1.0.0" "ufo": "^1.1.2"
}, },
"devDependencies": { "devDependencies": {
"@nuxtjs/eslint-config-typescript": "^11.0.0", "@nuxt/eslint-config": "^0.1.1",
"@trpc/client": "^10.1.0", "@trpc/client": "^10.28.0",
"@trpc/server": "^10.1.0", "@trpc/server": "^10.28.0",
"bumpp": "^8.2.1", "changelogen": "^0.5.3",
"concurrently": "^7.5.0", "eslint": "^8.41.0",
"eslint": "^8.25.0", "taze": "^0.10.1",
"tsup": "6.4.0", "tsup": "6.7.0",
"typescript": "^4.7.4" "typescript": "^5.0.4"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
"@nuxtjs/eslint-config-typescript" "@nuxt/eslint-config"
], ],
"rules": { "rules": {
"@typescript-eslint/no-unused-vars": [ "@typescript-eslint/no-unused-vars": [
@@ -64,5 +72,10 @@
"*.md", "*.md",
"dist", "dist",
".output" ".output"
] ],
} "pnpm": {
"overrides": {
"nuxt": "3.4.3"
}
}
}

View File

@@ -1,3 +1,6 @@
// https://v3.nuxtjs.org/api/configuration/nuxt.config // https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({ export default defineNuxtConfig({
build: {
transpile: ['trpc-nuxt']
}
}) })

View File

@@ -9,13 +9,14 @@
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare"
}, },
"dependencies": { "dependencies": {
"@trpc/client": "^10.1.0", "@trpc/client": "^10.28.0",
"@trpc/server": "^10.1.0", "@trpc/server": "^10.28.0",
"superjson": "^1.11.0", "superjson": "^1.12.3",
"trpc-nuxt": "workspace:*", "trpc-nuxt": "workspace:*",
"zod": "^3.19.1" "zod": "^3.21.4"
}, },
"devDependencies": { "devDependencies": {
"nuxt": "^3.0.0" "@types/node": "^18.16.15",
"nuxt": "3.4.3"
} }
} }

View File

@@ -1,8 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import { TRPCClientError } from '@trpc/client';
import type { inferRouterOutputs } from '@trpc/server';
import type { AppRouter } from '~~/server/trpc/routers';
const { $client } = useNuxtApp() const { $client } = useNuxtApp()
const addTodo = async () => { const addTodo = async () => {
@@ -21,10 +17,7 @@ const addTodo = async () => {
} }
} }
type RouterOutput = inferRouterOutputs<AppRouter>; const { data: todos, pending, error, refresh } = await $client.todo.getTodos.useQuery()
type ErrorOutput = TRPCClientError<AppRouter>
const { data: todos, pending, error, refresh } = await useAsyncData<RouterOutput['todo']['getTodos'], ErrorOutput>(() => $client.todo.getTodos.query())
</script> </script>
<template> <template>
@@ -37,8 +30,14 @@ const { data: todos, pending, error, refresh } = await useAsyncData<RouterOutput
</div> </div>
<div v-else> <div v-else>
<ul> <ul>
<li v-for="t in todos?.slice(0, 10)" :key="t.id"> <li
<NuxtLink :class="{ completed: t.completed }" :to="`/todo/${t.id}`"> v-for="t in todos?.slice(0, 10)"
:key="t.id"
>
<NuxtLink
:class="{ completed: t.completed }"
:to="`/todo/${t.id}`"
>
Title: {{ t.title }} Title: {{ t.title }}
</NuxtLink> </NuxtLink>
</li> </li>

View File

@@ -0,0 +1,27 @@
<script setup lang="ts">
const route = useRoute()
const { $client } = useNuxtApp()
const id = ref(1)
// const { data: todo, pending, error } = await useAsyncData(() => $client.todo.getTodo.query(id.value), {
// watch: [id]
// })
const { data: todo, pending, error, refresh } = await $client.todo.getTodo.useQuery(id)
</script>
<template>
<div v-if="pending">
Loading...
</div>
<div v-else-if="error?.data?.code">
Error: {{ error.data.code }}
</div>
<div v-else>
ID: {{ todo?.id }} <br>
Title: {{ todo?.title }} <br>
Completed: {{ todo?.completed }}
</div>
<button @click="id++">
Next Todo
</button>
</template>

View File

@@ -8,8 +8,8 @@ const { data: todo, pending, error } = await useAsyncData(() => $client.todo.get
<div v-if="pending"> <div v-if="pending">
Loading... Loading...
</div> </div>
<div v-else-if="error?.data?.code"> <div v-else-if="error">
{{ error.data.code }} {{ error.message }} - {{ error.cause }}
</div> </div>
<div v-else> <div v-else>
ID: {{ todo?.id }} <br> ID: {{ todo?.id }} <br>

View File

@@ -1,10 +1,10 @@
import { createTRPCProxyClient, httpBatchLink, loggerLink } from '@trpc/client' import { loggerLink } from '@trpc/client'
import superjson from 'superjson' import superjson from 'superjson'
import { createTRPCNuxtClient, httpBatchLink } 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 headers = useRequestHeaders() const client = createTRPCNuxtClient<AppRouter>({
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
@@ -13,12 +13,7 @@ export default defineNuxtPlugin(() => {
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',
headers () {
return headers
}
})
] ]
}) })

View File

@@ -12,7 +12,7 @@ export function createContext (
event: 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)) // console.log('cookies', parseCookies(event))
return {} return {}
} }

9365
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

84
src/client/index.ts Normal file
View File

@@ -0,0 +1,84 @@
import { type CreateTRPCClientOptions, type inferRouterProxyClient, 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'
// @ts-expect-error: Nuxt auto-imports
import { getCurrentInstance, onScopeDispose, useAsyncData, unref, isRef } from '#imports'
/**
* Calculates the key used for `useAsyncData` call.
*
* @example
*
* ```ts
* import { getQueryKey } from 'trpc-nuxt/client'
*
* $client.todo.getTodo(1)
*
* const queryKey = getQueryKey('todo.getTodo', 1)
* ```
*/
export function getQueryKey (
path: string,
input: unknown
): string {
return input === undefined ? path : `${path}-${hash(input || '')}`
}
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 `.useMutation` or `.useQuery()`
const lastArg = pathCopy.pop()!
// The `path` ends up being something like `post.byId`
const path = pathCopy.join('.')
const [input, otherOptions] = args
if (lastArg === 'useQuery') {
const { trpc, ...asyncDataOptions } = otherOptions || {} as any
let controller: AbortController
if (trpc?.abortOnUnmount) {
if (getCurrentInstance()) {
onScopeDispose(() => {
controller?.abort?.()
})
}
controller = typeof AbortController !== 'undefined' ? new AbortController() : {} as AbortController
}
const queryKey = getQueryKey(path, unref(input))
return useAsyncData(queryKey, () => (client as any)[path].query(unref(input), {
signal: controller?.signal,
...trpc
}), {
...asyncDataOptions,
watch: isRef(input) ? [...(asyncDataOptions.watch || []), input] : asyncDataOptions.watch
})
}
return (client as any)[path][lastArg](...args)
})
}
export function createTRPCNuxtClient<TRouter extends AnyRouter> (opts: CreateTRPCClientOptions<TRouter>) {
const client = createTRPCProxyClient<TRouter>(opts)
const decoratedClient = createFlatProxy((key) => {
return createNuxtProxyDecoration(key, client as any)
}) as DecoratedProcedureRecord<TRouter['_def']['record'], TRouter>
return decoratedClient
}
export {
httpBatchLink,
httpLink
} from './links'

80
src/client/links.ts Normal file
View File

@@ -0,0 +1,80 @@
import { httpLink as _httpLink, httpBatchLink as _httpBatchLink } from '@trpc/client'
import { type AnyRouter } from '@trpc/server'
import { FetchError } from 'ofetch'
// @ts-expect-error: Nuxt auto-imports
import { useRequestHeaders } from '#imports'
import { type HTTPLinkOptions as _HTTPLinkOptions } from '@trpc/client/dist/links/httpLink'
import { type FetchEsque } from '@trpc/client/dist/internals/types'
function customFetch(input: RequestInfo | URL, init?: RequestInit & { method: 'GET' }) {
return globalThis.$fetch.raw(input.toString(), init)
.catch((e) => {
if (e instanceof FetchError && e.response) { return e.response }
throw e
})
.then(response => ({
...response,
json: () => Promise.resolve(response._data)
}))
}
export interface HTTPLinkOptions extends _HTTPLinkOptions {
/**
* Select headers to pass to `useRequestHeaders`.
*/
pickHeaders?: string[]
}
/**
* This is a convenience wrapper around the original httpLink
* that replaces regular `fetch` with a `$fetch` from Nuxt. It
* also sets the default headers based on `useRequestHeaders` values.
*
* During server-side rendering, calling $fetch to fetch your internal API routes
* will directly call the relevant function (emulating the request),
* saving an additional API call.
*
* @see https://nuxt.com/docs/api/utils/dollarfetch
*/
export function httpLink<TRouter extends AnyRouter>(opts?: HTTPLinkOptions) {
const headers = useRequestHeaders(opts?.pickHeaders)
return _httpLink<TRouter>({
url: '/api/trpc',
headers () {
return headers
},
fetch: customFetch as FetchEsque,
...opts,
})
}
export interface HttpBatchLinkOptions extends HTTPLinkOptions {
maxURLLength?: number;
}
/**
* This is a convenience wrapper around the original httpBatchLink
* that replaces regular `fetch` with a `$fetch` from Nuxt. It
* also sets the default headers based on `useRequestHeaders` values.
*
* During server-side rendering, calling $fetch to fetch your internal API routes
* will directly call the relevant function (emulating the request),
* saving an additional API call.
*
* @see https://nuxt.com/docs/api/utils/dollarfetch
*/
export function httpBatchLink<TRouter extends AnyRouter>(opts?: HttpBatchLinkOptions) {
const headers = useRequestHeaders(opts?.pickHeaders)
return _httpBatchLink<TRouter>({
url: '/api/trpc',
// @ts-expect-error: Missing property from batchLink. Fix this later.
headers () {
return headers
},
fetch: customFetch as FetchEsque,
...opts,
})
}

83
src/client/types.ts Normal file
View File

@@ -0,0 +1,83 @@
import type { TRPCClientErrorLike, TRPCRequestOptions as _TRPCRequestOptions } from '@trpc/client'
import { type TRPCSubscriptionObserver } from '@trpc/client/dist/internals/TRPCUntypedClient'
import type {
AnyMutationProcedure,
AnyProcedure,
AnyQueryProcedure,
AnyRouter,
ProcedureRouterRecord,
inferProcedureInput,
inferProcedureOutput,
ProcedureArgs,
AnySubscriptionProcedure
} from '@trpc/server'
import { type inferObservableValue, type Unsubscribable } from '@trpc/server/observable'
import { inferTransformedProcedureOutput } from '@trpc/server/shared'
import type {
AsyncData,
AsyncDataOptions,
KeysOf,
PickFrom,
} from 'nuxt/dist/app/composables/asyncData'
import type { Ref } from 'vue'
interface TRPCRequestOptions extends _TRPCRequestOptions {
abortOnUnmount?: boolean
}
type Resolver<TProcedure extends AnyProcedure> = (
...args: ProcedureArgs<TProcedure['_def']>
) => Promise<inferTransformedProcedureOutput<TProcedure>>;
type SubscriptionResolver<
TProcedure extends AnyProcedure,
TRouter extends AnyRouter,
> = (
...args: [
input: ProcedureArgs<TProcedure['_def']>[0],
opts: ProcedureArgs<TProcedure['_def']>[1] &
Partial<
TRPCSubscriptionObserver<
inferObservableValue<inferProcedureOutput<TProcedure>>,
TRPCClientErrorLike<TRouter>
>
>,
]
) => Unsubscribable
type MaybeRef<T> = T | Ref<T>
type DecorateProcedure<
TProcedure extends AnyProcedure,
TRouter extends AnyRouter,
> = TProcedure extends AnyQueryProcedure
? {
useQuery: <
ResT = inferTransformedProcedureOutput<TProcedure>,
DataE = TRPCClientErrorLike<TProcedure>,
DataT = ResT,
PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
>(
input: MaybeRef<inferProcedureInput<TProcedure>>,
opts?: AsyncDataOptions<ResT, DataT, PickKeys> & { trpc?: TRPCRequestOptions },
) => AsyncData<PickFrom<DataT, PickKeys>, DataE>,
query: Resolver<TProcedure>
} : TProcedure extends AnyMutationProcedure ? {
mutate: Resolver<TProcedure>
} : TProcedure extends AnySubscriptionProcedure ? {
subscribe: SubscriptionResolver<TProcedure, TRouter>
} : never
/**
* @internal
*/
export type DecoratedProcedureRecord<
TProcedures extends ProcedureRouterRecord,
TRouter extends AnyRouter,
> = {
[TKey in keyof TProcedures]: TProcedures[TKey] extends AnyRouter
? DecoratedProcedureRecord<TProcedures[TKey]['_def']['record'], TRouter>
: TProcedures[TKey] extends AnyProcedure
? DecorateProcedure<TProcedures[TKey], TRouter>
: never;
}

View File

@@ -50,9 +50,13 @@ export interface ResolveHTTPRequestOptions<TRouter extends AnyRouter> {
} }
function getPath (event: H3Event): string | null { function getPath (event: H3Event): string | null {
if (typeof event.context.params.trpc === 'string') { return event.context.params.trpc } const { params } = event.context
if (Array.isArray(event.context.params.trpc)) { return event.context.params.trpc.join('/') } if (typeof params?.trpc === 'string') { return params.trpc }
if (params?.trpc && Array.isArray(params.trpc)) {
return (params.trpc as string[]).join('/')
}
return null return null
} }
@@ -124,3 +128,5 @@ export function createNuxtApiHandler<TRouter extends AnyRouter> ({
return body return body
}) })
} }
export const createH3ApiHandler = createNuxtApiHandler

View File

@@ -1,11 +1,11 @@
import { defineConfig } from 'tsup' import { defineConfig } from 'tsup'
export default defineConfig({ export default defineConfig({
entry: ['src/index.ts'], entry: ['src/index.ts', 'src/client/index.ts'],
format: ['cjs', 'esm'], format: ['cjs', 'esm'],
splitting: false, splitting: false,
clean: true, clean: true,
external: ['#app', '#imports'], external: ['#app', '#imports', /@trpc\/client/, /@trpc\/server/],
dts: true, dts: true,
outExtension ({ format }) { outExtension ({ format }) {
return { return {