Compare commits

...

190 Commits
v0.4.1 ... main

Author SHA1 Message Date
25e09b2e30 Update example and playground dependencies 2023-08-31 14:11:23 +02:00
0612716187 Update dependencies 2023-08-31 13:56:19 +02:00
wobsoriano
55712e3027 chore(release): v0.10.12 2023-08-23 14:18:33 -07:00
wobsoriano
0fec55a3ae refactor: explicitly copy headers to custom fetcher 2023-08-23 14:18:12 -07:00
wobsoriano
8f9e398ae2 fix: expect ofetch missing error response type 2023-08-23 14:12:29 -07:00
wobsoriano
092e3495fd build(deps): bump ofetch to 1.3.2 2023-08-23 13:44:22 -07:00
wobsoriano
230422bb16 docs: fix incorrect syntax 2023-08-23 12:49:09 -07:00
wobsoriano
057c8f8d3a docs: fix missing title 2023-08-23 12:48:32 -07:00
wobsoriano
9808375f31 docs: add response caching page 2023-08-23 12:46:22 -07:00
wobsoriano
8e893a2e30 chore(release): v0.10.11 2023-08-22 10:52:53 -07:00
wobsoriano
f74273190f bump local deps 2023-08-22 10:52:41 -07:00
wobsoriano
283400d41a chore(deps): bump ohash to 1.1.3 2023-08-22 10:51:54 -07:00
wobsoriano
5221dc0515 chore(deps): bump ofetch to 1.2.0 2023-08-22 10:51:29 -07:00
wobsoriano
299ae558ab fix: add missing useLazyQuery type 2023-08-22 10:48:44 -07:00
wobsoriano
f7eeb104b3 docs: bump @nuxt-themes/docus to 1.14.6 2023-08-22 10:36:36 -07:00
wobsoriano
c5c762190b chore(release): v0.10.10 2023-08-20 22:57:35 -07:00
wobsoriano
93cb44288c feat: add useLazyQuery composable 2023-08-20 22:57:31 -07:00
wobsoriano
ede749414d chore(release): v0.10.9 2023-08-20 22:51:01 -07:00
wobsoriano
f2bcf9b68b feat: add custom query key option 2023-08-20 22:50:48 -07:00
wobsoriano
4289dcbfab chore(release): v0.10.8 2023-08-20 22:30:24 -07:00
wobsoriano
dc317c2ded build: bump local deps 2023-08-20 21:57:59 -07:00
wobsoriano
3db0b31d31 refactor: use stand-alone getErrorShape 2023-08-20 21:55:25 -07:00
wobsoriano
f86ebcd6d6 docs: add handler param description 2023-08-20 21:48:39 -07:00
wobsoriano
252f6108cf refactor: use built-in getRequestURL from h3 instead of ufo 2023-08-20 21:41:57 -07:00
wobsoriano
3a395a7d4a build(deps): bump h3 to 1.8.0 2023-08-20 21:36:23 -07:00
wobsoriano
ec7e0255c2 chore(release): v0.10.7 2023-08-10 21:01:35 -07:00
Robert Soriano
1386832b4a Merge pull request #115 from Blechlawine/fix/usequery-nullable
fix: make useQuery return data nullable (#113)
2023-08-10 08:15:24 -07:00
Blechlawine
120ebc5760 fix: make useQuery return data nullable 2023-08-10 15:16:46 +02:00
Robert Soriano
adeb5a5afd Merge pull request #84 from Colonel-Sandvich/patch-1
docs: Update Server Side Calls docs
2023-08-03 13:07:20 -07:00
wobsoriano
6e47431f35 bump local deps 2023-07-20 22:16:57 -07:00
wobsoriano
7c9392b209 add examples to workspace 2023-07-12 10:47:53 -07:00
wobsoriano
117f7b791f bump local deps 2023-07-12 10:46:39 -07:00
wobsoriano
735e362958 remove prettier config 2023-07-12 10:46:20 -07:00
Robert Soriano
0738a133ca Merge pull request #108 from RyanPaulGannon/next
Example: Added a basic example with a product call
2023-07-12 10:44:56 -07:00
Robert Soriano
97ef8c90b8 Update CHANGELOG.md 2023-07-09 17:41:35 -07:00
wobsoriano
bec300b750 chore(release): v0.10.6 2023-07-09 17:38:40 -07:00
wobsoriano
7bbe767495 bump local deps 2023-07-09 17:38:07 -07:00
wobsoriano
811a634010 chore(deps): update ofetch to 1.1.1 2023-07-09 17:36:54 -07:00
wobsoriano
12d8c97f71 chore(deps): update h3 to 1.7.1 2023-07-09 17:36:23 -07:00
Ryan Gannon
3fc261d5e7 Example: Added a basic example with a product call 2023-06-13 15:00:38 +01:00
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
Colonel-Sandvich
1c638a8489 docs: Update Server Side Calls docs 2023-04-08 22:56:57 +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
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
55 changed files with 9648 additions and 5298 deletions

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

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

165
CHANGELOG.md Normal file
View File

@@ -0,0 +1,165 @@
# Changelog
## v0.10.12
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.11...v0.10.12)
### 🩹 Fixes
- Expect ofetch missing error response type ([8f9e398](https://github.com/wobsoriano/trpc-nuxt/commit/8f9e398))
### 💅 Refactors
- Explicitly copy headers to custom fetcher ([0fec55a](https://github.com/wobsoriano/trpc-nuxt/commit/0fec55a))
### 📖 Documentation
- Add response caching page ([9808375](https://github.com/wobsoriano/trpc-nuxt/commit/9808375))
- Fix missing title ([057c8f8](https://github.com/wobsoriano/trpc-nuxt/commit/057c8f8))
- Fix incorrect syntax ([230422b](https://github.com/wobsoriano/trpc-nuxt/commit/230422b))
### 📦 Build
- **deps:** Bump ofetch to 1.3.2 ([092e349](https://github.com/wobsoriano/trpc-nuxt/commit/092e349))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))
## v0.10.11
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.10...v0.10.11)
### 🩹 Fixes
- Add missing useLazyQuery type ([299ae55](https://github.com/wobsoriano/trpc-nuxt/commit/299ae55))
### 📖 Documentation
- Bump @nuxt-themes/docus to 1.14.6 ([f7eeb10](https://github.com/wobsoriano/trpc-nuxt/commit/f7eeb10))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))
## v0.10.10
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.9...v0.10.10)
### 🚀 Enhancements
- Add useLazyQuery composable ([93cb442](https://github.com/wobsoriano/trpc-nuxt/commit/93cb442))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))
## v0.10.9
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.8...v0.10.9)
### 🚀 Enhancements
- Add custom query key option ([f2bcf9b](https://github.com/wobsoriano/trpc-nuxt/commit/f2bcf9b))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))
## v0.10.8
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.7...v0.10.8)
### 💅 Refactors
- Use built-in getRequestURL from h3 instead of ufo ([252f610](https://github.com/wobsoriano/trpc-nuxt/commit/252f610))
- Use stand-alone getErrorShape ([3db0b31](https://github.com/wobsoriano/trpc-nuxt/commit/3db0b31))
### 📖 Documentation
- Add handler param description ([f86ebcd](https://github.com/wobsoriano/trpc-nuxt/commit/f86ebcd))
### 📦 Build
- **deps:** Bump h3 to 1.8.0 ([3a395a7](https://github.com/wobsoriano/trpc-nuxt/commit/3a395a7))
- Bump local deps ([dc317c2](https://github.com/wobsoriano/trpc-nuxt/commit/dc317c2))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))
## v0.10.7
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.6...v0.10.7)
### 🩹 Fixes
- Make useQuery return data nullable ([120ebc5](https://github.com/wobsoriano/trpc-nuxt/commit/120ebc5))
### 📖 Documentation
- Update Server Side Calls docs ([1c638a8](https://github.com/wobsoriano/trpc-nuxt/commit/1c638a8))
### ❤️ Contributors
- Blechlawine <marczinser@gmx.de>
- Colonel-Sandvich
## v0.10.6
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.5...v0.10.6)
### 🏡 Chore
- **deps**:
- Bump h3 to 1.7.1 &nbsp;-&nbsp; by @wobsoriano [<samp>(12d8c)</samp>](https://github.com/wobsoriano/trpc-nuxt/commit/12d8c97f71bedb025b2e7914db13e69e5f62c8a2)
- Bump ofetch to 1.1.1 &nbsp;-&nbsp; by @wobsoriano [<samp>(811a6)</samp>](https://github.com/wobsoriano/trpc-nuxt/commit/811a6340107a2bea53f2cf488416bcec915f88b0)
## 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,14 +1,35 @@
<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.
<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 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). 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
- [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,26 +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",
aside: { debug: false,
level: 1 socials: {
}, github: "wobsoriano/trpc-nuxt"
footer: { },
credits: { cover: {
icon: 'IconDocus', src: "/cover.jpg",
text: 'Powered by Docus', alt: "tRPC-Nuxt module"
href: 'https://docus.com' },
}, header: {
icons: [ logo: true
{ },
label: 'NuxtJS', aside: {
href: 'https://nuxtjs.org', level: 1
component: 'IconNuxt' },
} 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.
@@ -33,8 +41,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.
::
## 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

@@ -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

@@ -0,0 +1,44 @@
---
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.
::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
`@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,44 @@
---
title: Headers
---
# 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]
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 { appRouter } from '@/server/trpc/routers'
export default defineEventHandler(async (event) => {
const { name } = getQuery(event)
const caller = appRouter.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 { appRouter } from '@/server/trpc/routers'
export default defineEventHandler(async (event) => {
const body = await getBody(event)
const caller = appRouter.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,51 @@
---
title: Response Caching
---
## Response Caching
Your server responses must [satisfy some criteria](https://vercel.com/docs/concepts/functions/serverless-functions/edge-caching) in order for them to be cached (i.e. by Vercel's Edge Network). Please refer to [this section of the tRPC.io documentation](https://trpc.io/docs/caching) for more information.
The `createNuxtApiHandler` function conveniently allows you to specify a `responseMeta` function.
```ts [server/api/trpc/[trpc].ts]
import { createNuxtApiHandler } from 'trpc-nuxt'
import { appRouter } from '~/server/trpc/routers'
export default createNuxtApiHandler({
router: appRouter,
/**
* @link https://trpc.io/docs/caching#api-response-caching
*/
responseMeta(opts) {
// cache request for 1 day + revalidate once every second
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
return {
headers: {
'cache-control': `s-maxage=1, stale-while-revalidate=${ONE_DAY_IN_SECONDS}`,
},
};
},
})
```
You can also take advantage of Nitro's [Cache API](https://nitro.unjs.io/guide/cache#cache-api) if doing server-side calls:
```ts
import { appRouter } from '@/server/trpc/routers'
const caller = appRouter.createCaller({})
export default cachedEventHandler(async (event) => {
const { name } = getQuery(event)
const greeting = await caller.greeting({ name })
return {
greeting
}
}, {
swr: true, maxAge: 10
})
```

View File

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

View File

@@ -26,6 +26,7 @@ End-to-end typesafe APIs in Nuxt applications.
::list ::list
- Automatic typesafety - Automatic typesafety
- Snappy DX - Snappy DX
- Autocompletion - 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,13 +7,9 @@
"generate": "nuxi build", "generate": "nuxi build",
"preview": "nuxi preview" "preview": "nuxi preview"
}, },
"dependencies": {
"@vueuse/head": "^1.0.3",
"nuxt": "^3.0.0-rc.13",
"@nuxtjs/tailwindcss": "^6.1.3"
},
"devDependencies": { "devDependencies": {
"@nuxt-themes/docus": "npm:@nuxt-themes/docus-edge@latest", "@nuxt-themes/docus": "^1.14.6",
"@nuxtlabs/github-module": "npm:@nuxtlabs/github-module-edge@latest" "@nuxtlabs/github-module": "^1.6.3",
"nuxt": "3.6.5"
} }
} }

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

23
examples/basic-example/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# Nuxt dev/build outputs
.output
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

View File

@@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false

View File

@@ -0,0 +1,63 @@
# Nuxt 3 Minimal Starter
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install the dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm run dev
# yarn
yarn dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm run build
# yarn
yarn build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm run preview
# yarn
yarn preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

View File

@@ -0,0 +1,127 @@
<script setup lang="ts">
const { $client } = useNuxtApp()
const { data: product } = await $client.products.useQuery()
function formatPrice(num: number) {
return `£ ${num.toFixed(2)}`
}
</script>
<template>
<div class="product-page">
<header class="header">
<h1 class="header-title">
TRPC-Nuxt Basic Example
</h1>
</header>
<div class="card">
<div class="card-content">
<h3 class="card-title">
{{ product.title }}
</h3>
<p class="card-description">
{{ product.description }}
</p>
<p class="card-price">
{{ formatPrice(product.price) }}
</p>
<button class="card-button">
Add to Cart
</button>
</div>
</div>
</div>
</template>
<style>
body {
margin: 0;
}
.product-page {
display: flex;
flex-direction: column;
align-items: center;
height: 100vh;
}
.header {
width: 100%;
background-color: #41b883;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.header-title {
margin: 0;
font-size: 24px;
font-weight: bold;
color: #fff;
text-align: center;
font-family: 'Roboto', sans-serif;
}
.card {
width: 300px;
border: 1px solid #eaeaea;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-top: 16px;
}
.card-image {
width: 100%;
height: auto;
}
.card-content {
padding: 16px;
background-color: #f9f9f9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.card-title {
margin: 0;
font-size: 20px;
color: #333;
font-family: 'Roboto', sans-serif;
}
.card-description {
margin: 8px 0;
font-size: 14px;
color: #666;
font-family: 'Roboto', sans-serif;
}
.card-price {
margin: 8px 0;
font-size: 18px;
font-weight: bold;
color: #333;
font-family: 'Roboto', sans-serif;
}
.card-button {
display: block;
width: 100%;
padding: 8px;
margin-top: 16px;
font-size: 16px;
font-weight: bold;
text-align: center;
color: #fff;
background-color: #41b883;
border: none;
border-radius: 4px;
cursor: pointer;
}
.card-button:hover {
background-color: #399d7d;
}
</style>

View File

@@ -0,0 +1,7 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
build: {
transpile: ['trpc-nuxt'],
},
})

View File

@@ -0,0 +1,22 @@
{
"name": "nuxt-app",
"private": true,
"scripts": {
"build": "nuxi build",
"dev": "nuxi dev",
"generate": "nuxi generate",
"preview": "nuxi preview",
"postinstall": "nuxi prepare"
},
"devDependencies": {
"@nuxt/devtools": "latest",
"@types/node": "20.5.7",
"nuxt": "^3.7.0"
},
"dependencies": {
"@trpc/client": "^10.38.1",
"@trpc/server": "^10.38.1",
"trpc-nuxt": "workspace:*",
"zod": "^3.22.2"
}
}

View File

@@ -0,0 +1,22 @@
import { createTRPCNuxtClient, httpBatchLink } from 'trpc-nuxt/client'
import type { AppRouter } from '~/server/trpc/routers'
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,
},
}
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,9 @@
import { createNuxtApiHandler } from 'trpc-nuxt'
import { appRouter } from '../../trpc/routers'
import { createContext } from '../../trpc/context'
// export API handler
export default createNuxtApiHandler({
router: appRouter,
createContext,
})

View File

@@ -0,0 +1,9 @@
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>

View File

@@ -0,0 +1,27 @@
import { z } from 'zod'
import { publicProcedure, router } from '../trpc'
export const appRouter = router({
hello: publicProcedure
.input(
z.object({
text: z.string().nullish(),
})
)
.query(({ input }) => {
console.log(input)
return {
greeting: `hello ${input?.text ?? 'world'}`,
}
}),
products: publicProcedure.query(async () => {
return {
title: 'TRPC Nuxt',
price: 12.0,
description: 'Check out the trpc-nuxt repo',
}
}),
})
// export type definition of API
export type AppRouter = typeof appRouter

View File

@@ -0,0 +1,20 @@
/**
* 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 { Context } from '../trpc/context'
const t = initTRPC.context<Context>().create()
/**
* Unprotected procedure
**/
export const publicProcedure = t.procedure
export const router = t.router
export const middleware = t.middleware

View File

@@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}

View File

@@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}

View File

@@ -2,55 +2,60 @@
"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.1", "packageManager": "pnpm@8.6.9",
"version": "0.10.12",
"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-proxy-beta.21", "@trpc/client": "^10.38.1",
"@trpc/server": "^10.0.0-proxy-beta.21" "@trpc/server": "^10.38.1"
}, },
"dependencies": { "dependencies": {
"h3": "^0.8.6", "h3": "^1.8.1",
"nanoid": "^4.0.0", "ofetch": "^1.3.3",
"nuxt": "^3.0.0-rc.13", "ohash": "^1.1.3"
"ohash": "^0.1.5",
"ufo": "^0.8.6"
}, },
"devDependencies": { "devDependencies": {
"@nuxt/kit": "3.0.0-rc.13", "@nuxt/eslint-config": "^0.2.0",
"@nuxtjs/eslint-config-typescript": "^11.0.0", "@trpc/client": "^10.38.1",
"@trpc/client": "10.0.0-rc.7", "@trpc/server": "^10.38.1",
"@trpc/server": "10.0.0-rc.7", "changelogen": "^0.5.5",
"bumpp": "^8.2.1", "eslint": "^8.48.0",
"concurrently": "^7.5.0", "taze": "^0.11.2",
"eslint": "^8.25.0", "tsup": "7.2.0",
"tsup": "6.4.0", "typescript": "^5.2.2"
"typescript": "^4.7.4"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
"@nuxtjs/eslint-config-typescript" "@nuxt/eslint-config"
], ],
"rules": { "rules": {
"@typescript-eslint/no-unused-vars": [ "@typescript-eslint/no-unused-vars": [
@@ -66,5 +71,12 @@
"*.md", "*.md",
"dist", "dist",
".output" ".output"
] ],
"pnpm": {
"overrides": {
"nuxt": "3.7.0",
"@trpc/client": "10.38.1",
"@trpc/server": "10.38.1"
}
}
} }

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

@@ -2,20 +2,21 @@
"name": "playground", "name": "playground",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "nuxt build", "build": "nuxi build",
"dev": "nuxt dev", "dev": "nuxi dev",
"generate": "nuxt generate", "generate": "nuxi generate",
"preview": "nuxt preview", "preview": "nuxi preview",
"postinstall": "nuxt prepare" "postinstall": "nuxi prepare"
}, },
"dependencies": { "dependencies": {
"@trpc/client": "10.0.0-rc.7", "@trpc/client": "10.38.1",
"@trpc/server": "10.0.0-rc.7", "@trpc/server": "10.38.1",
"superjson": "^1.11.0", "superjson": "^1.13.1",
"trpc-nuxt": "workspace:*", "trpc-nuxt": "workspace:*",
"zod": "^3.19.1" "zod": "^3.22.2"
}, },
"devDependencies": { "devDependencies": {
"nuxt": "^3.0.0-rc.13" "@types/node": "20.5.7",
"nuxt": "3.7.0"
} }
} }

View File

@@ -1,11 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const { $client } = useNuxtApp() const { $client } = useNuxtApp()
// const headers = useClientHeaders()
// const addHeader = () => {
// 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)
@@ -23,7 +17,7 @@ const addTodo = async () => {
} }
} }
const { data: todos, pending, error, refresh } = await useAsyncData(() => $client.todo.getTodos.query()) const { data: todos, pending, error, refresh } = await $client.todo.getTodos.useLazyQuery()
</script> </script>
<template> <template>
@@ -36,8 +30,14 @@ const { data: todos, pending, error, refresh } = await useAsyncData(() => $clien
</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,9 +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 client = createTRPCProxyClient<AppRouter>({ const client = createTRPCNuxtClient<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
@@ -12,9 +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'
})
] ]
}) })

View File

@@ -1,6 +1,6 @@
import { createNuxtApiHandler } from 'trpc-nuxt' import { createNuxtApiHandler } from 'trpc-nuxt'
import { appRouter } from '@/server/trpc/routers' import { appRouter } from '../../trpc/routers'
import { createContext } from '@/server/trpc/context' import { createContext } from '../../trpc/context'
export default createNuxtApiHandler({ export default createNuxtApiHandler({
router: appRouter, router: appRouter,

View File

@@ -9,9 +9,10 @@ export type Context = inferAsyncReturnType<typeof createContext>
* @link https://trpc.io/docs/context * @link https://trpc.io/docs/context
*/ */
export 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

@@ -1,5 +1,6 @@
import { z } from 'zod' import { z } from 'zod'
import { publicProcedure, router } from '../trpc' import { publicProcedure, router } from '../trpc'
import { $fetch } from 'ofetch'
const baseURL = 'https://jsonplaceholder.typicode.com' const baseURL = 'https://jsonplaceholder.typicode.com'

View File

@@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}

13010
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
packages: packages:
- playground - playground
- examples/*
- docs - docs

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

@@ -0,0 +1,88 @@
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 (['useQuery', 'useLazyQuery'].includes(lastArg)) {
const { trpc, queryKey: customQueryKey, ...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 = customQueryKey || getQueryKey(path, unref(input))
const watch = isRef(input) ? [...(asyncDataOptions.watch || []), input] : asyncDataOptions.watch
const isLazy = lastArg === 'useLazyQuery' ? true : (asyncDataOptions.lazy || false)
return useAsyncData(queryKey, () => (client as any)[path].query(unref(input), {
signal: controller?.signal,
...trpc
}), {
...asyncDataOptions,
watch,
lazy: isLazy
})
}
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'

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

@@ -0,0 +1,81 @@
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,
headers: response.headers,
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,
})
}

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

@@ -0,0 +1,106 @@
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
/**
* The custom unique key to use.
* @see https://nuxt.com/docs/api/composables/use-async-data#params
*/
queryKey?: string
},
) => AsyncData<PickFrom<DataT, PickKeys> | null, DataE>,
useLazyQuery: <
ResT = inferTransformedProcedureOutput<TProcedure>,
DataE = TRPCClientErrorLike<TProcedure>,
DataT = ResT,
PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
>(
input: MaybeRef<inferProcedureInput<TProcedure>>,
opts?: Omit<AsyncDataOptions<ResT, DataT, PickKeys>, 'lazy'> & {
trpc?: TRPCRequestOptions
/**
* The custom unique key to use.
* @see https://nuxt.com/docs/api/composables/use-async-data#params
*/
queryKey?: string
},
) => AsyncData<PickFrom<DataT, PickKeys> | null, 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

@@ -9,10 +9,10 @@ import type {
import { import {
TRPCError TRPCError
} from '@trpc/server' } from '@trpc/server'
import { createURL } from 'ufo'
import type { H3Event } from 'h3' import type { H3Event } from 'h3'
import { createError, defineEventHandler, isMethod, readBody } from 'h3' import { createError, defineEventHandler, getRequestURL, isMethod, readBody } from 'h3'
import type { TRPCResponse } from '@trpc/server/rpc' import type { TRPCResponse } from '@trpc/server/rpc'
import { getErrorShape } from '@trpc/server/shared'
type MaybePromise<T> = T | Promise<T> type MaybePromise<T> = T | Promise<T>
@@ -32,7 +32,7 @@ export interface OnErrorPayload<TRouter extends AnyRouter> {
error: TRPCError error: TRPCError
type: ProcedureType | 'unknown' type: ProcedureType | 'unknown'
path: string | undefined path: string | undefined
req: H3Event['req'] req: H3Event['node']['req']
input: unknown input: unknown
ctx: undefined | inferRouterContext<TRouter> ctx: undefined | inferRouterContext<TRouter>
} }
@@ -40,9 +40,25 @@ export interface OnErrorPayload<TRouter extends AnyRouter> {
export type OnErrorFn<TRouter extends AnyRouter> = (opts: OnErrorPayload<TRouter>) => void export type OnErrorFn<TRouter extends AnyRouter> = (opts: OnErrorPayload<TRouter>) => void
export interface ResolveHTTPRequestOptions<TRouter extends AnyRouter> { export interface ResolveHTTPRequestOptions<TRouter extends AnyRouter> {
/**
* The tRPC router to use.
* @see https://trpc.io/docs/router
*/
router: TRouter router: TRouter
/**
* An async function that returns the tRPC context.
* @see https://trpc.io/docs/context
*/
createContext?: CreateContextFn<TRouter> createContext?: CreateContextFn<TRouter>
/**
* A function that returns the response meta.
* @see https://trpc.io/docs/caching#using-responsemeta-to-cache-responses
*/
responseMeta?: ResponseMetaFn<TRouter> responseMeta?: ResponseMetaFn<TRouter>
/**
* A function that is called when an error occurs.
* @see https://trpc.io/docs/error-handling#handling-errors
*/
onError?: OnErrorFn<TRouter> onError?: OnErrorFn<TRouter>
batching?: { batching?: {
enabled: boolean enabled: boolean
@@ -50,9 +66,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
} }
@@ -68,17 +88,18 @@ export function createNuxtApiHandler<TRouter extends AnyRouter> ({
const { const {
req, req,
res res
} = event } = event.node
const $url = createURL(req.url!) const $url = getRequestURL(event)
const path = getPath(event) const path = getPath(event)
if (path === null) { if (path === null) {
const error = router.getErrorShape({ const error = getErrorShape({
config: router._def._config,
error: new TRPCError({ error: new TRPCError({
message: message:
'Param "trpc" not found - is the file named `[trpc]`.ts or `[...trpc].ts`?', 'Query "trpc" not found - is the file named `[trpc]`.ts or `[...trpc].ts`?',
code: 'INTERNAL_SERVER_ERROR' code: 'INTERNAL_SERVER_ERROR'
}), }),
type: 'unknown', type: 'unknown',
@@ -124,3 +145,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 {