Compare commits

..

361 Commits

Author SHA1 Message Date
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
wobsoriano
fd38c4f983 chore: release v0.4.1 2022-11-12 20:16:38 -08:00
wobsoriano
48d4c6342f update playground 2022-11-12 20:16:29 -08:00
wobsoriano
297c3d52f4 docs: update usage 2022-11-12 20:15:52 -08:00
wobsoriano
0f9653d1a4 remove /module 2022-11-12 20:12:36 -08:00
wobsoriano
85998101c7 feat: remove nuxt 3 wrappers for client 2022-11-12 20:10:50 -08:00
wobsoriano
d6cb770154 update installation instructions 2022-11-12 18:41:44 -08:00
wobsoriano
c222008524 update installation instructions 2022-11-12 18:41:16 -08:00
wobsoriano
9cd21eb300 update readme docs url 2022-11-12 18:40:15 -08:00
wobsoriano
b4e83dbb0b add back preset 2022-11-12 18:38:25 -08:00
wobsoriano
0fa63c86ba test node preset 2022-11-12 18:31:35 -08:00
wobsoriano
7371dc4c10 update docs 2022-11-12 18:28:26 -08:00
wobsoriano
77ab5f0dc6 reinstall deps 2022-11-12 18:20:07 -08:00
wobsoriano
ba15d15d67 remove patched @vueuse/head 2022-11-12 18:17:10 -08:00
wobsoriano
7999039088 update github config 2022-11-12 18:13:49 -08:00
wobsoriano
dc15500074 dont prerender index 2022-11-12 18:13:00 -08:00
wobsoriano
99a83562aa add tmp docs link 2022-11-12 18:09:34 -08:00
wobsoriano
e04344d508 docs: remove package manager 2022-11-12 18:08:22 -08:00
wobsoriano
0f9d26ab60 chore: release v0.4.0 2022-11-12 18:01:55 -08:00
wobsoriano
e6b4386bd8 remove tag 2022-11-12 18:01:10 -08:00
wobsoriano
813b9ff7d7 move eslint config to package.json 2022-11-12 17:55:32 -08:00
wobsoriano
5dce1f4681 update readme 2022-11-12 17:54:17 -08:00
wobsoriano
470ba96be2 transpile github module 2022-11-12 16:32:18 -08:00
wobsoriano
4532add2a7 docs: patch vueuse/head 2022-11-12 16:26:43 -08:00
wobsoriano
876ed1d8d2 bump local trpc deps to rc.7 2022-11-08 09:30:58 -08:00
wobsoriano
611e22d205 delete merge conflicts 2022-11-08 09:27:22 -08:00
wobsoriano
c1857f1079 eslint fix 2022-11-08 09:25:21 -08:00
wobsoriano
796fa43a83 fix lockfile 2022-11-08 09:23:14 -08:00
wobsoriano
e3ce0bcaae merge conflicts 2022-11-08 09:22:30 -08:00
wobsoriano
e916f17d60 update docs 2022-11-08 09:06:08 -08:00
wobsoriano
0fdb9e0e17 update colors 2022-11-08 09:05:36 -08:00
wobsoriano
5bd5756d26 update og image 2022-11-08 09:00:41 -08:00
wobsoriano
67f95756ab fix docs 2022-11-08 08:59:32 -08:00
wobsoriano
1fc4d9d0d0 Revert "feat: add file routing option"
This reverts commit 750783e860.
2022-11-05 21:24:24 -07:00
wobsoriano
c11a5cfa8a remove examples folder 2022-11-05 20:13:48 -07:00
wobsoriano
92c9f6390c disable file routing by default 2022-11-05 20:06:02 -07:00
wobsoriano
3a6c5b659b update function names 2022-11-05 19:50:39 -07:00
wobsoriano
750783e860 feat: add file routing option 2022-11-05 19:22:19 -07:00
wobsoriano
d9c8f877d3 eslint fixes 2022-11-05 12:57:45 -07:00
wobsoriano
e359702c41 eslint fixes 2022-11-05 11:56:14 -07:00
wobsoriano
e9081d00ad replace eslint config 2022-11-05 11:49:46 -07:00
wobsoriano
83f98e34fa more eslint fixes 2022-11-04 08:04:58 -07:00
wobsoriano
512a0ae7b1 more eslint fixes 2022-11-04 08:04:46 -07:00
wobsoriano
c89f8747ea bump local deps 2022-11-04 08:04:28 -07:00
wobsoriano
3e8a2de6e5 more eslint fixes 2022-11-04 08:02:52 -07:00
wobsoriano
5d98873cc3 eslint fix 2022-11-04 08:01:24 -07:00
wobsoriano
a8e5538b05 release v0.3.3 2022-11-04 07:58:20 -07:00
wobsoriano
24ecb41429 eslint fix 2022-11-04 07:58:16 -07:00
wobsoriano
e8bf427d2b fix types 2022-11-04 07:57:38 -07:00
wobsoriano
347d49482f reinstall deps 2022-11-04 07:55:39 -07:00
wobsoriano
2f7a1276ad feat(deps): bump @nuxt/kit to 3.0.0-rc.13 2022-11-04 07:53:42 -07:00
wobsoriano
f6db0c78cd add .vercel to .gitignore 2022-11-04 07:51:44 -07:00
wobsoriano
d6012eb5c8 feat: import vue functions from #imports 2022-11-04 07:49:37 -07:00
wobsoriano
c1ab3ecf77 update scripts 2022-11-04 07:44:09 -07:00
wobsoriano
0e8f6cf9f6 update playground 2022-11-04 07:41:27 -07:00
wobsoriano
0f5c09c7e0 update docs 2022-11-04 07:40:49 -07:00
wobsoriano
5080e9a4a7 bump local trpc deps to 10.0.0-rc.4 2022-11-04 07:39:50 -07:00
wobsoriano
7490dbd090 chore: bump to 0.4.0-beta.15 2022-11-04 07:37:49 -07:00
wobsoriano
1dc0ca7808 feat: remove custom async data error handler 2022-11-04 07:37:08 -07:00
wobsoriano
ffb692a168 feat(deps): bump nuxt to 3.0.0-rc.13 2022-11-04 07:34:47 -07:00
wobsoriano
d3203f0536 remove docs 2022-11-02 09:43:35 -07:00
wobsoriano
83e2b2810d release v0.3.2 2022-11-02 09:42:08 -07:00
wobsoriano
2b4fff9faa feat(deps): bump h3 to 0.8.6 2022-11-02 09:41:39 -07:00
wobsoriano
752fe721a2 feat(deps): lock @trpc/server and @trpc/client to version 9 2022-11-02 09:40:48 -07:00
wobsoriano
808cac4756 eslint fixes 2022-11-02 09:38:14 -07:00
wobsoriano
0c44f73c9f log info about plugin installation 2022-11-02 09:34:16 -07:00
wobsoriano
182875a781 feat: add option to specify if plugin should be installed 2022-11-02 09:30:40 -07:00
wobsoriano
0298f836b8 more eslint fixes 2022-11-02 09:21:15 -07:00
wobsoriano
ea50430945 eslint fixes 2022-11-02 09:18:17 -07:00
wobsoriano
0e240663ca remove local pnpm deps 2022-11-02 09:18:00 -07:00
wobsoriano
bb5e02383e remove unused moduile 2022-11-02 08:59:55 -07:00
wobsoriano
c01dddea02 remove local pnpm 2022-11-02 08:56:58 -07:00
wobsoriano
f92aec5b91 feat(deps): bump h3 to 0.8.6 2022-11-02 08:55:52 -07:00
wobsoriano
b6f92d954c update local trpc dependencies to 10.0.0-rc.3 2022-11-02 08:54:58 -07:00
wobsoriano
9108dcbb1d feat: never cache mutation 2022-10-31 22:39:40 -07:00
Robert Soriano
07f415640f bump to 0.4.0-beta.14 2022-10-31 10:09:07 -07:00
Robert Soriano
d5d211d2a8 feat: add subscription types 2022-10-31 10:06:41 -07:00
Robert Soriano
3bfd3585b3 remove unused types 2022-10-31 09:58:51 -07:00
Robert Soriano
8ba8d79114 remove unused generic type 2022-10-31 09:55:00 -07:00
Robert Soriano
b7f72eac0a feat: respect user added abort signal 2022-10-31 09:53:00 -07:00
Robert Soriano
4c8a6d7b35 feat: add abortOnUnmount option 2022-10-31 09:50:15 -07:00
Robert Soriano
76619ac541 bump to 0.4.0-beta.13 2022-10-31 01:01:44 -07:00
Robert Soriano
d5384cef02 add .vercel to ignored 2022-10-31 00:50:10 -07:00
Robert Soriano
fb3a0a834e add vercel static output to ignored 2022-10-31 00:43:08 -07:00
Robert Soriano
b58d662edc update docs 2022-10-31 00:37:45 -07:00
Robert Soriano
5d560867bb update http handler params 2022-10-30 22:24:49 -07:00
Robert Soriano
ac7fbf3998 update trpc param error handler 2022-10-30 22:21:08 -07:00
Robert Soriano
45fbab4c5f update docs 2022-10-30 21:40:20 -07:00
Robert Soriano
05ad1f96f6 export server types 2022-10-30 21:31:57 -07:00
Robert Soriano
41e6117113 add zod error formatting for playground 2022-10-30 21:30:07 -07:00
Robert Soriano
81c7608fb0 add batching and other opts 2022-10-30 21:25:37 -07:00
Robert Soriano
a614bc588e allow passing of other client options 2022-10-30 21:13:54 -07:00
Robert Soriano
3b0609e395 allow passing of other client options 2022-10-30 21:09:42 -07:00
Robert Soriano
0819c21b94 eslint fix 2022-10-30 18:19:32 -07:00
Robert Soriano
deea70b743 check trpc path via h3 context params 2022-10-30 18:17:14 -07:00
Robert Soriano
0ee322c8a2 run dev playground with concurrently 2022-10-30 17:35:23 -07:00
Robert Soriano
fc225ced0a bump to beta.12 2022-10-30 17:06:43 -07:00
Robert Soriano
b1d00c1f88 update compat 2022-10-30 17:06:35 -07:00
Robert Soriano
e0a6bb3576 bump to beta.11 2022-10-30 17:03:59 -07:00
Robert Soriano
fcd6cb1546 fix module compat 2022-10-30 17:03:49 -07:00
Robert Soriano
471ac30040 bump to beta.10 2022-10-30 17:00:27 -07:00
Robert Soriano
a7013db712 add module helper 2022-10-30 17:00:19 -07:00
Robert Soriano
bdcff360f0 bump to beta.9 2022-10-30 14:24:50 -07:00
Robert Soriano
1490790edc import composables from nuxt/app 2022-10-30 14:24:39 -07:00
Robert Soriano
4c5e6812b1 bump to beta.8 2022-10-30 13:36:28 -07:00
Robert Soriano
23d2954aae import composables from #imports 2022-10-30 13:35:58 -07:00
Robert Soriano
3c39746fa2 bump to beta.7 2022-10-30 13:31:04 -07:00
Robert Soriano
5fb7a9d444 import composables from nuxt/app 2022-10-30 13:30:57 -07:00
Robert Soriano
a2275567e8 bump to beta.6 2022-10-30 13:25:11 -07:00
Robert Soriano
c040db4308 update esm extensions to mjs 2022-10-30 13:24:39 -07:00
Robert Soriano
72755315a5 bump to beta.5 2022-10-30 13:09:58 -07:00
Robert Soriano
3d49810641 switch package type to module 2022-10-30 13:09:45 -07:00
Robert Soriano
ebb6637238 chore: release v0.4.0-beta.4 2022-10-30 13:02:11 -07:00
Robert Soriano
0c83ade918 update release script 2022-10-30 13:02:01 -07:00
Robert Soriano
97d9b3ddf8 import useAsyncData and useState from #app 2022-10-30 12:57:57 -07:00
Robert Soriano
eaa61f3700 bump local pnpm to 7.14.1 2022-10-30 12:35:30 -07:00
Robert Soriano
a5a9f430d5 chore: release v0.4.0-beta.3 2022-10-30 12:34:40 -07:00
Robert Soriano
94f59c396a update local deps 2022-10-30 12:30:03 -07:00
Robert Soriano
fe4c10ae25 remove outDir 2022-10-30 12:29:01 -07:00
Robert Soriano
7a97127353 update directory structure 2022-10-30 12:25:28 -07:00
Robert Soriano
0c4a43ec19 update playground 2022-10-30 12:19:57 -07:00
Robert Soriano
da6d738406 update docs 2022-10-30 12:00:21 -07:00
Robert Soriano
70c0b35c48 complete usage docs 2022-10-30 11:18:57 -07:00
Robert Soriano
da52b350ba add initial docs 2022-10-30 11:00:09 -07:00
Robert Soriano
b45c613ce7 immediately execute mutations 2022-10-30 01:52:20 -07:00
Robert Soriano
025ce7e028 chore: release v0.4.0-beta.2 2022-10-29 23:10:16 -07:00
Robert Soriano
4e637a4ec3 add root tsconfig 2022-10-29 23:09:44 -07:00
Robert Soriano
2b5daf54fa chore: release v0.4.0-beta.1 2022-10-29 22:52:42 -07:00
Robert Soriano
217c3fd8e9 chore: release v0.4.0-beta.0 2022-10-29 22:51:51 -07:00
Robert Soriano
14617d864d fix build 2022-10-29 22:48:13 -07:00
Robert Soriano
3522cc9327 move to pnpm workspace 2022-10-29 22:45:57 -07:00
Robert Soriano
09500bc868 update readme 2022-10-29 22:39:43 -07:00
Robert Soriano
6ddfbf9629 update playground 2022-10-29 22:38:10 -07:00
Robert Soriano
97dfefe6cd add superjson to playground 2022-10-29 22:33:59 -07:00
Robert Soriano
b61dbd1786 update playground 2022-10-29 22:29:21 -07:00
Robert Soriano
4bb2cdc2d2 save trpc server error to client 2022-10-29 22:16:06 -07:00
Robert Soriano
29909328c4 update playground 2022-10-29 21:58:00 -07:00
Robert Soriano
da44e5429f update devDeps 2022-10-29 21:51:27 -07:00
Robert Soriano
ae920a37fd make trpc-nuxt flexible by exporting server and client functions instead of nuxt modules 2022-10-29 21:50:27 -07:00
Robert Soriano
bad2de134b remove queryKey for mutation 2022-10-29 19:38:29 -07:00
Robert Soriano
14da8bbda6 update example 2022-10-29 19:31:42 -07:00
Robert Soriano
e69dacf07a import useAsyncData from #app 2022-10-29 19:26:32 -07:00
Robert Soriano
f8de361eaf add inspiration comment 2022-10-29 19:24:43 -07:00
Robert Soriano
0a5735125f move types to another file 2022-10-29 19:23:54 -07:00
Robert Soriano
a04ed72c45 update playground 2022-10-29 19:20:47 -07:00
Robert Soriano
d435c7c9c5 update export paths 2022-10-29 19:18:15 -07:00
Robert Soriano
a3ce2c9a92 remove unused module imports 2022-10-29 19:15:59 -07:00
Robert Soriano
b0e7cc1854 rename api to server 2022-10-29 19:12:44 -07:00
Robert Soriano
9d07dab57d add custom fetch function for node 18 2022-10-29 19:12:01 -07:00
Robert Soriano
7257842438 rewrite client 2022-10-29 19:02:14 -07:00
Robert Soriano
b72b0449c1 update client types 2022-10-28 15:50:54 -07:00
Robert Soriano
d47fbc4537 add exported router type to plugin 2022-10-28 15:40:31 -07:00
Robert Soriano
c0b570ca09 api support for trpc v10 2022-10-28 15:39:31 -07:00
Robert Soriano
95f5478cdc update server routes in playground 2022-10-28 15:27:50 -07:00
Robert Soriano
000ac03295 update client to use v10 client 2022-10-28 15:13:29 -07:00
Robert Soriano
91421c1849 move @trpc/client and @trpc/server to peerDependencies 2022-10-28 15:06:13 -07:00
Robert Soriano
bdeada0e6e Merge pull request #32 from littlejon85/9-server-error-discard
fix: don't discard error in client
2022-10-28 14:53:09 -07:00
Robert Soriano
fd9b86fb18 release v0.3.1 2022-10-21 12:27:16 -07:00
Robert Soriano
5fab0e54b0 update local deps 2022-10-21 12:24:18 -07:00
Robert Soriano
7d2f464699 Merge pull request #34 from mahdiboomeri/chore
chore: bump nuxt to RC 12
2022-10-21 08:10:53 -07:00
Mahdi Boomeri
fdb9c2b474 refactor: playground nuxt.config 2022-10-21 17:00:22 +03:30
Mahdi Boomeri
4ed504e1e9 fix: h3 CompatibilityEvent 2022-10-21 16:56:09 +03:30
Mahdi Boomeri
92747711c6 chore: bump other deps 2022-10-21 16:53:53 +03:30
Mahdi Boomeri
3646e8cdfe chore: nump nuxt 2022-10-21 16:43:22 +03:30
Jan Dlouhý
88214185a8 fix: use useAsyncData error in client 2022-10-15 22:36:28 +02:00
Jan Dlouhý
ad03ae7b65 fix: don't discard error in client 2022-10-11 14:35:21 +02:00
Robert Soriano
09e8ab536a feat: replace useBody with readBody 2022-10-01 09:15:36 -07:00
Robert Soriano
f7dadd482d Update README.md 2022-09-29 10:20:05 -07:00
Robert Soriano
7b69cff1ed Merge pull request #30 from cawa-93/patch-1
docs: Fix `baseURL` in code sample
2022-09-27 10:18:15 -07:00
Alex Kozack
f2d7763fa4 docs: Fix baseURL in code sample 2022-09-27 10:41:38 +03:00
Robert Soriano
20f5a08cbd update readme 2022-09-24 08:26:36 -07:00
Robert Soriano
75980c0953 release v0.3.0 2022-09-24 08:25:33 -07:00
Robert Soriano
a6f61d404f feat(deps): bump h3 to 0.7.21 2022-09-24 08:24:57 -07:00
Robert Soriano
e53d1be61e feat(deps): bump @nuxt/kit to 3.0.0-rc-11 2022-09-24 08:23:29 -07:00
Robert Soriano
d152ebc7be chore: remove volta config 2022-09-24 08:20:24 -07:00
Robert Soriano
f6daa2b7c3 Merge pull request #28 from robinWongM/feature/direct-api-call-during-ssr
feat: use globalThis.$fetch to call API handler directly on server-side
2022-09-24 08:19:16 -07:00
Robin Wong
7962c7f61e fix: return original FetchResponse when ohmyfetch throws error 2022-09-18 09:46:59 +00:00
Robin Wong
453845fb14 fix: eslint errors 2022-09-18 08:44:43 +00:00
Robin Wong
ee3ee485ba feat: use globalThis.$fetch to call API handler directly on server-side 2022-09-16 16:53:15 +00:00
Robert Soriano
cdf29bee67 release v0.2.8 2022-09-08 09:36:32 -07:00
Robert Soriano
d3d35404b1 update release script 2022-09-08 09:36:24 -07:00
Robert Soriano
e57c919157 Merge pull request #26 from elfpie/feature/update-nuxt
Bump nuxt to RC9
2022-09-08 08:11:24 -07:00
Felipe
d0fffbbc64 feat(deps): bump nuxt to 3.0.0-rc.9 2022-09-07 17:59:23 -03:00
Felipe
433d5ada46 feat(deps): bump @trpc/client to 9.27.2 2022-09-07 17:59:23 -03:00
Felipe
5d180460af feat(deps): bump @trpc/server to 9.27.2 2022-09-07 17:59:23 -03:00
Felipe
8991148d39 feat(deps): bump @nuxt/kit to 3.0.0-rc.9 2022-09-07 17:59:15 -03:00
Robert Soriano
dd05285d65 release v0.2.7 2022-08-31 12:10:12 -07:00
Robert Soriano
d058ff241a update playground 2022-08-31 12:09:45 -07:00
Robert Soriano
fb4894e264 Merge pull request #25 from mahdiboomeri/fix/useClientHeaders
Fix useClientHeaders
2022-08-31 12:07:05 -07:00
Mahdi Boomeri
186faff140 docs: replaced useStorage with useState in useClientHeaders 2022-08-31 19:19:27 +04:30
Mahdi Boomeri
b9cdb228b7 refactor: remove vueuse dependency 2022-08-31 19:17:44 +04:30
Mahdi Boomeri
1e67434a8f fix: useClientHeaders returning an empty object 2022-08-31 19:13:29 +04:30
Robert Soriano
019bb20e50 release v0.2.6 2022-08-20 00:10:00 -07:00
Robert Soriano
695ce4c56e feat(deps): bump @trpc/server from 9.27.0 to 9.27.1 2022-08-20 00:09:39 -07:00
Robert Soriano
38649b7023 feat(deps): bump @trpc/client from 9.27.0 to 9.27.1 2022-08-20 00:09:17 -07:00
Robert Soriano
5cfef32baa release v0.2.5 2022-08-14 12:38:56 -07:00
Robert Soriano
8497289c65 update readme 2022-08-14 12:38:31 -07:00
Robert Soriano
224c4b317b move ohash to deps 2022-08-14 12:29:04 -07:00
Robert Soriano
d5d6780ca8 bump ohash 2022-08-14 12:28:03 -07:00
Robert Soriano
252d0f6692 feat(deps): bump vueuse from 8.9.3 to 9.1.0 2022-08-14 12:27:10 -07:00
Robert Soriano
d022710591 bump dev deps 2022-08-14 12:26:33 -07:00
Robert Soriano
a926968f13 feat(deps): bump @nuxt/kit from 3.0.0-rc.5 to 3.0.0-rc.8 2022-08-14 12:25:48 -07:00
Robert Soriano
a8eba5ab8d feat(deps): bump @trpc/server from 9.26.0 to 9.27.0 2022-08-14 12:25:14 -07:00
Robert Soriano
5871528d67 feat(deps): bump @trpc/client from 9.26.0 to 9.27.0 2022-08-14 12:24:52 -07:00
Robert Soriano
def9d0b19d volta pin node@16 2022-08-14 12:24:20 -07:00
Robert Soriano
34f50fc4d6 chore(deps): bump pnpm to 7.5.0 2022-07-14 19:04:19 -07:00
Robert Soriano
9fe1469eba release v0.2.4 2022-07-14 19:03:09 -07:00
Robert Soriano
e48d7cd42c feat(deps): bump @nuxt/kit from 3.0.0-rc.4 to 3.0.0-rc.5 2022-07-14 19:02:54 -07:00
Robert Soriano
2b517ad77b release v0.2.3 2022-07-14 11:19:28 -07:00
Robert Soriano
77ee994e13 update deps 2022-07-14 11:19:19 -07:00
Robert Soriano
67a8a2a3ed feat(deps): bump ufo from 0.8.4 to 0.8.5 2022-07-14 11:19:01 -07:00
Robert Soriano
556b5c8e08 feat(deps): bump @vueuse/nuxt from 8.7.4 to 8.9.3 2022-07-14 11:18:37 -07:00
Robert Soriano
85154f12d7 feat(deps): bump @vueuse/core from 8.7.4 to 8.9.3 2022-07-14 11:18:21 -07:00
Robert Soriano
26ce46e12b feat(deps): bump @trpc/server from 9.25.3 to 9.26.0 2022-07-14 11:17:51 -07:00
Robert Soriano
c2cfd9a214 feat(deps): bump @trpc/client from 9.25.3 to 9.26.0 2022-07-14 11:17:34 -07:00
Robert Soriano
f736545128 ci: add release workflow 2022-07-14 11:16:43 -07:00
Robert Soriano
59a9eb5f95 fix playground workspace 2022-07-05 20:24:40 -07:00
Robert Soriano
48fc19076f Update README.md 2022-06-23 10:03:48 -07:00
Robert Soriano
6b83e5d4ea chore(deps): bump @trpc/server to 9.25.3 2022-06-18 21:06:25 -07:00
Robert Soriano
0358a258d7 chore(deps): bump @trpc/client to 9.25.3 2022-06-18 21:06:11 -07:00
Robert Soriano
8e7f33c9d2 chore(deps): bump h3 to 0.7.10 2022-06-18 21:05:48 -07:00
Robert Soriano
b8f4af2df3 chore(deps): bump @vueuse/nuxt to 8.7.4 2022-06-18 21:05:15 -07:00
Robert Soriano
666751678c chore(deps): bump @vueuse/core to 8.7.4 2022-06-18 21:04:56 -07:00
Robert Soriano
cab89aaa66 chore(deps): bump bumpp to 7.2.0 2022-06-18 21:04:17 -07:00
Robert Soriano
4c9850fafc chore(deps): add @types/dedent 2022-06-18 21:02:44 -07:00
Robert Soriano
07693009f8 release v0.2.2 2022-06-16 06:49:32 -07:00
Robert Soriano
dceade155c chore(deps): bump nuxt to 3.0.0-rc.4 2022-06-16 06:48:56 -07:00
Robert Soriano
4012249f09 chore(deps): bump h3 to 0.7.9 2022-06-16 06:48:20 -07:00
Robert Soriano
38eda2c4d6 chore(deps): bump @trpc/client to 9.25.2 2022-06-16 06:47:39 -07:00
Robert Soriano
9468af75f4 chore(deps): bump @trpc/server to 9.25.2 2022-06-16 06:47:18 -07:00
Robert Soriano
ea24a67a6d chore(deps): bump @nuxt/kit to 3.0.0-rc.4 2022-06-16 06:46:35 -07:00
Robert Soriano
5e8d04741c release v0.2.1 2022-06-13 18:51:41 -07:00
Robert Soriano
1032e65a0d refactor: remove unused import in playground 2022-06-13 18:51:35 -07:00
Robert Soriano
f261d3feeb Merge pull request #12 from cawa-93/patch-1
fix: auto-import `getQueryKey`
2022-06-13 18:49:07 -07:00
Alex Kozack
b1ca09e986 fix: auto-import getQueryKey 2022-06-13 12:06:03 +03:00
Robert Soriano
b804429fc0 update readme 2022-06-12 23:32:55 -07:00
Robert Soriano
7df64296ff update readme 2022-06-12 23:32:41 -07:00
Robert Soriano
a53d823f5e release v0.2.0 2022-06-12 23:30:07 -07:00
Robert Soriano
feef3dde6b update readme 2022-06-12 23:30:03 -07:00
Robert Soriano
82ee2ce672 refactor!: replace trpcURL option with endpoint 2022-06-12 23:29:28 -07:00
Robert Soriano
f62a13766a update readme 2022-06-12 23:19:55 -07:00
Robert Soriano
c7888e81ed refactor: use addAutoImport 2022-06-12 23:18:38 -07:00
Robert Soriano
c77eb68f5d refactor: rename endpoint option 2022-06-12 23:17:13 -07:00
Robert Soriano
333539569c refactor: lint 2022-06-12 23:16:01 -07:00
Robert Soriano
e5c40f183b Merge pull request #7 from cawa-93/feat/extract-get-query-key
feat: extract `getQueryKey` helper
2022-06-12 23:08:21 -07:00
Robert Soriano
977a9e1465 release v0.1.24 2022-06-05 17:05:34 -07:00
Robert Soriano
2620379e02 feat: allow changing of src dir 2022-06-05 17:05:13 -07:00
Robert Soriano
e4f42d5322 Merge pull request #10 from nicolesmileyface/feat/support-src-dir
use nuxt srcDir instead of rootDir
2022-06-05 17:02:41 -07:00
Cole Hollant
ad28a9124e use nuxt srcDir instead of rootDir 2022-06-05 11:55:25 -04:00
cawa-93
273bda980b feat: extract getQueryKey helper
This method will be useful to get a key that can be used to reset the internal nuxt cache
2022-05-31 15:20:21 +03:00
64 changed files with 7203 additions and 4307 deletions

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

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

22
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 16.x
- run: npx changelogithub
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

2
.gitignore vendored
View File

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

163
README.md
View File

@@ -1,12 +1,16 @@
# tRPC-Nuxt
<p align="center">
<figure>
<img src="https://trpc-nuxt.vercel.app/cover.jpg" alt="trpc-nuxt cover image" />
</figure>
</p>
[![Version](https://img.shields.io/npm/v/trpc-nuxt?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/trpc-nuxt)
# tRPC-Nuxt
End-to-end typesafe APIs with [tRPC.io](https://trpc.io/) in Nuxt applications.
<p align="center">
<figure>
<img src="https://i.imgur.com/AjmNUxj.gif" alt="Demo" />
<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.
@@ -15,156 +19,17 @@ End-to-end typesafe APIs with [tRPC.io](https://trpc.io/) in Nuxt applications.
</figure>
</p>
## Install
Docs: https://trpc-nuxt.vercel.app
```bash
npm i trpc-nuxt
```
```ts
// nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: ['trpc-nuxt'],
trpc: {
baseURL: 'http://localhost:3000', // defaults to http://localhost:3000
trpcURL: '/api/trpc', // defaults to /api/trpc
},
typescript: {
strict: true // set this to true to make input/output types work
}
})
```
## Usage
Expose your tRPC [routes](https://trpc.io/docs/router) under `~/server/trpc/index.ts`:
```ts
// ~/server/trpc/index.ts
import type { inferAsyncReturnType } from '@trpc/server'
import * as trpc from '@trpc/server'
import { z } from 'zod' // yup/superstruct/zod/myzod/custom
export const router = trpc.router()
// queries and mutations...
.query('getUsers', {
async resolve(req) {
// use your ORM of choice
return await UserModel.all()
},
})
.mutation('createUser', {
// validate input with Zod
input: z.object({ name: z.string().min(5) }),
async resolve(req) {
// use your ORM of choice
return await UserModel.create({
data: req.input,
})
},
})
```
Use the client like so:
```ts
const client = useClient() // auto-imported
const users = await client.query('getUsers')
const newUser = await client.mutation('createUser', {
name: 'wagmi'
})
```
## useAsyncQuery
A thin wrapper around [`useAsyncData`](https://v3.nuxtjs.org/api/composables/use-async-data/) and `client.query()`.
The first argument is a `[path, input]`-tuple - if the `input` is optional, you can omit the, `input`-part.
You'll notice that you get autocompletion on the `path` and automatic typesafety on the `input`.
```ts
const {
data,
pending,
error,
refresh
} = await useAsyncQuery(['getUser', { id: 69 }], {
// pass useAsyncData options here
lazy: false
})
```
## useClientHeaders
A composable that lets you add additional properties to pass to the tRPC Client. It uses `useStorage` from [@vueuse/core](https://vueuse.org/core/usestorage).
```ts
const headers = useClientHeaders()
const { data: token } = await useAsyncQuery(['auth.login', { username, password }])
headers.value.Authorization = `Bearer ${token}`
// All client calls will now include the Authorization header.
// For SSR, please follow this until I found a solution https://github.com/trpc/trpc/discussions/1686
```
## Options
trpc-nuxt accepts the following options exposed under `~/server/trpc/index.ts`:
```ts
import * as trpc from '@trpc/server'
import type { inferAsyncReturnType } from '@trpc/server'
import type { CompatibilityEvent } from 'h3'
import type { OnErrorPayload } from 'trpc-nuxt/api'
export const router = trpc.router<inferAsyncReturnType<typeof createContext>>()
// Optional
// https://trpc.io/docs/context
export const createContext = (event: CompatibilityEvent) => {
// ...
return {
/** context data */
}
}
// Optional
// https://trpc.io/docs/caching#using-responsemeta--to-cache-responses
export const responseMeta = () => {
// ...
return {
// { headers: ... }
}
}
// Optional
// https://trpc.io/docs/error-handling#handling-errors
export const onError = (payload: OnErrorPayload<typeof router>) => {
// Do whatever here like send to bug reporting and stuff
}
```
## Recipes
- [Validation](/recipes/validation.md)
- [Authorization](/recipes/authorization.md)
- [Merging Routers](/recipes/merging-routers.md)
- [Error Handling](/recipes/error-handling.md)
- [Error Formatting](/recipes/error-formatting.md)
- [Inference Helpers](/recipes/inference-helpers.md)
Learn more about tRPC.io [here](https://trpc.io/docs).
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
- [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

1
api.d.ts vendored
View File

@@ -1 +0,0 @@
export * from './dist/runtime/api'

1
client.d.ts vendored Normal file
View File

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

1
docs/.gitignore vendored Normal file
View File

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

1
docs/README.md Normal file
View File

@@ -0,0 +1 @@
docs

38
docs/app.config.ts Normal file
View File

@@ -0,0 +1,38 @@
export default defineAppConfig({
docus: {
title: "tRPC Nuxt",
description: "End-to-end typesafe APIs in Nuxt applications.",
image: "https://og-image.vercel.app/tRPC-Nuxt",
alt: "tRPC-Nuxt cover",
url: "https://trpc-nuxt.vercel.app",
debug: false,
socials: {
github: "wobsoriano/trpc-nuxt"
},
cover: {
src: "/cover.jpg",
alt: "tRPC-Nuxt module"
},
header: {
logo: true
},
aside: {
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

@@ -0,0 +1,46 @@
---
title: Installation
description: tRPC-Nuxt provides first class integration with tRPC.
---
# Installation
::code-group
```bash [pnpm]
pnpm add @trpc/server @trpc/client trpc-nuxt zod
```
```bash [npm]
npm install @trpc/server @trpc/client trpc-nuxt zod
```
```bash [yarn]
yarn add @trpc/server @trpc/client trpc-nuxt zod
```
::
```ts [nuxt.config.ts]
export default defineNuxtConfig({
build: {
transpile: ['trpc-nuxt']
}
})
```
#### Why @trpc/server?
For implementing tRPC endpoints and routers.
#### Why @trpc/client?
For making typesafe API calls from your client.
#### Why zod?
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
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

@@ -0,0 +1,153 @@
---
title: Recommended
description: tRPC-Nuxt provides first class integration with tRPC.
---
# Recommended Usage
Recommended but not enforced file structure.
```graphql
.
server
api
trpc
[trpc].ts # <-- tRPC HTTP handler
[..]
trpc
routers
index.ts # <-- main app router
todo.ts # <-- sub routers
[..]
context.ts # <-- create app context
trpc.ts # <-- procedure helpers
plugins
client.ts # <-- tRPC client plugin
[..]
```
## 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'
import { Context } from '~/server/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;
```
```ts [server/trpc/routers/index.ts]
import { z } from 'zod'
import { publicProcedure, router } from '../trpc'
export const appRouter = router({
hello: publicProcedure
.input(
z.object({
text: z.string().nullish(),
}),
)
.query(({ input }) => {
return {
greeting: `hello ${input?.text ?? 'world'}`,
}
}),
})
// export type definition of API
export type AppRouter = typeof appRouter
```
```ts [server/api/trpc/[trpc].ts]
import { createNuxtApiHandler } from 'trpc-nuxt'
import { appRouter } from '~/server/trpc/routers'
import { createContext } from '~/server/trpc/context'
// export API handler
export default createNuxtApiHandler({
router: appRouter,
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}
If you need to split your router into several subrouters, you can implement them in the `server/trpc/routers` directory and import and [merge them](https://trpc.io/docs/v10/merging-routers) to a single root `appRouter`.
::
## 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/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,
},
}
})
```
## 3. Make an API request
```vue [pages/index.vue]
<script setup lang="ts">
const { $client } = useNuxtApp()
const hello = await $client.hello.useQuery({ text: 'client' })
</script>
<template>
<div>
<p>{{ hello.data?.greeting }}</p>
</div>
</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 { router } from '@/server/trpc/trpc'
export default eventHandler(async (event) => {
const { name } = getQuery(event)
const caller = router.createCaller({})
const greeting = await caller.greeting({ name })
return {
greeting
}
})
```
::
## Mutation example
We create the router with a mutation and then we call the asynchronous `post` procedure to get the result.
::code-group
```ts [server/trpc/trpc.ts]
import { initTRPC } from '@trpc/server'
import { z } from 'zod'
const posts = ['One', 'Two', 'Three']
const t = initTRPC.create()
export const router = t.router({
post: t.router({
add: t.procedure.input(z.string()).mutation(({ input }) => {
posts.push(input)
return posts
}),
}),
})
```
```ts [server/api/post.ts]
import { router } from '@/server/trpc/trpc'
export default eventHandler(async (event) => {
const body = await getBody(event)
const caller = router.createCaller({})
const post = await caller.post.add(body.post)
return {
post
}
})
```
::

View File

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

View File

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

View File

@@ -0,0 +1,6 @@
---
title: Basic
description: tRPC-Nuxt provides first class integration with tRPC.
---
# Basic Example

View File

@@ -0,0 +1,6 @@
---
title: Multiple Routers
description: tRPC-Nuxt provides first class integration with tRPC.
---
# Multi Routers

32
docs/content/index.md Normal file
View File

@@ -0,0 +1,32 @@
---
title: "tRPC Nuxt"
description: "End-to-end typesafe APIs in Nuxt applications."
navigation: false
layout: page
---
::block-hero
---
cta:
- Get Started
- /get-started/installation
secondary:
- Star on GitHub ->
- https://github.com/wobsoriano/trpc-nuxt
snippet: npm install trpc-nuxt
---
#title
tRPC [Nuxt]{.text-primary-500}
#description
End-to-end typesafe APIs in Nuxt applications.
#extra
::list
- Automatic typesafety
- Snappy DX
- 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)
::
::

22
docs/nuxt.config.ts Normal file
View File

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

17
docs/package.json Normal file
View File

@@ -0,0 +1,17 @@
{
"name": "docs",
"description": "Docs for TRPC-Nuxt",
"scripts": {
"dev": "nuxi dev",
"build": "nuxi build",
"generate": "nuxi build",
"preview": "nuxi preview"
},
"dependencies": {
"nuxt": "^3.0.0"
},
"devDependencies": {
"@nuxt-themes/docus": "^1.1.10",
"@nuxtlabs/github-module": "^1.5.4"
}
}

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

18
docs/tokens.config.ts Normal file
View File

@@ -0,0 +1,18 @@
import { defineTheme } from 'pinceau'
export default defineTheme({
colors: {
primary: {
50: '#BFEDFC',
100: '#B0E9FB',
200: '#93DEFA',
300: '#76D4F9',
400: '#58C8F7',
500: '#3BBBF6',
600: '#0BA6F3',
700: '#0981C2',
800: '#075E91',
900: '#043D61'
}
}
})

3
docs/tsconfig.json Normal file
View File

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

View File

@@ -1,61 +1,84 @@
{
"name": "trpc-nuxt",
"description": "End-to-end typesafe APIs in Nuxt applications.",
"type": "module",
"version": "0.1.23",
"packageManager": "pnpm@7.1.1",
"packageManager": "pnpm@7.18.2",
"version": "0.8.0",
"license": "MIT",
"main": "./dist/module.cjs",
"types": "./dist/types.d.ts",
"sideEffects": false,
"exports": {
"./package.json": "./package.json",
".": {
"import": "./dist/module.mjs",
"require": "./dist/module.cjs"
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
},
"./api": {
"import": "./dist/runtime/api.mjs",
"types": "./dist/runtime/api.d.ts"
"./client": {
"types": "./dist/client/index.d.ts",
"require": "./dist/client/index.cjs",
"import": "./dist/client/index.mjs"
}
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist",
"*.d.ts"
"client.d.ts"
],
"scripts": {
"prepublishOnly": "nr build",
"build": "nuxt-module-build",
"play": "nr build && nuxi dev playground",
"build:playground": "nuxi build playground",
"dev": "tsup --watch --onSuccess \"pnpm --filter playground dev\"",
"dev:prepare": "pnpm build && nuxt prepare playground",
"prepublishOnly": "pnpm build",
"build": "tsup",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"release": "bumpp --commit --push --tag && pnpm publish",
"prepare": "nuxi prepare playground"
"release": "bumpp && npm publish",
"update-deps": "taze -w && pnpm i"
},
"peerDependencies": {
"@trpc/client": "^10.18.0",
"@trpc/server": "^10.18.0"
},
"dependencies": {
"@nuxt/kit": "^3.0.0-rc.3",
"@trpc/client": "^9.23.3",
"@trpc/server": "^9.23.2",
"@vueuse/core": "^8.5.0",
"@vueuse/nuxt": "^8.5.0",
"defu": "^6.0.0",
"h3": "^0.7.8",
"pathe": "^0.3.0",
"ufo": "^0.8.4"
"h3": "^1.6.2",
"ofetch": "^1.0.1",
"ohash": "^1.0.0",
"ufo": "^1.1.1"
},
"devDependencies": {
"@antfu/eslint-config": "^0.23.1",
"@antfu/ni": "^0.16.2",
"@nuxt/module-builder": "latest",
"bumpp": "^7.1.1",
"eslint": "^8.14.0",
"nuxt": "^3.0.0-rc.3",
"ohash": "^0.1.0",
"pnpm": "^7.1.1",
"superjson": "^1.9.1",
"trpc-nuxt": "workspace:*",
"zod": "^3.16.0"
"@nuxt/eslint-config": "^0.1.1",
"@trpc/client": "^10.18.0",
"@trpc/server": "^10.18.0",
"bumpp": "^9.0.0",
"eslint": "^8.36.0",
"taze": "^0.9.1",
"tsup": "6.7.0",
"typescript": "^5.0.2"
},
"eslintConfig": {
"extends": "@antfu"
"extends": [
"@nuxt/eslint-config"
],
"rules": {
"@typescript-eslint/no-unused-vars": [
"off"
],
"vue/multi-word-component-names": "off",
"vue/no-multiple-template-root": "off"
}
},
"eslintIgnore": [
"*.json",
"node_modules",
"*.md",
"dist",
".output"
],
"pnpm": {
"overrides": {
"nuxt": "3.2.2"
}
},
"engines": {
"node": "^16.13.0 || ^18.12.0"
}
}

View File

@@ -1,13 +1,6 @@
import { defineNuxtConfig } from 'nuxt'
import Module from '../src/module'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
modules: [Module],
runtimeConfig: {
baseURL: 'http://localhost:3000',
},
typescript: {
strict: true,
},
build: {
transpile: ['trpc-nuxt']
}
})

View File

@@ -1,4 +1,21 @@
{
"name": "playground",
"private": true
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@trpc/client": "^10.18.0",
"@trpc/server": "^10.18.0",
"superjson": "^1.12.2",
"trpc-nuxt": "workspace:*",
"zod": "^3.21.4"
},
"devDependencies": {
"nuxt": "^3.2.2"
}
}

View File

@@ -1,19 +0,0 @@
<script setup lang="ts">
const counter = useCookie('counter')
counter.value = counter.value || Math.round(Math.random() * 1000)
</script>
<template>
<div>
<h1> Counter: {{ counter || '-' }}</h1>
<button @click="counter = null">
reset
</button>
<button @click="counter--">
-
</button>
<button @click="counter++">
+
</button>
</div>
</template>

View File

@@ -1,55 +1,54 @@
<script setup lang="ts">
const client = useClient()
const headers = useClientHeaders()
const { data: todos, pending, error, refresh } = await useAsyncQuery(['getTodos'])
const addHeader = () => {
// headers.value.cookie = 'counter=69'
console.log(headers.value)
}
const { $client } = useNuxtApp()
const addTodo = async () => {
const title = Math.random().toString(36).slice(2, 7)
try {
const result = await client.mutation('addTodo', {
const x = await $client.todo.addTodo.mutate({
id: Date.now(),
userId: 69,
title,
completed: false,
completed: false
})
console.log('Todo: ', result)
}
catch (e) {
console.log(x)
} catch (e) {
console.log(e)
}
}
const { data: todos, pending, error, refresh } = await $client.todo.getTodos.useQuery()
</script>
<template>
<div v-if="pending">
Loading...
</div>
<div v-else-if="error?.data?.code">
Error: {{ error.data.code }}
</div>
<div v-else-if="todos">
<ul>
<li v-for="t in todos.slice(0, 10)" :key="t.id">
<NuxtLink :class="{ completed: t.completed }" :to="`/todo/${t.id}`">
Title: {{ t.title }}
</NuxtLink>
</li>
</ul>
<button @click="addTodo">
Add Todo
</button>
<button @click="() => refresh()">
Refresh
</button>
<button @click="addHeader">
Add header
</button>
<div>
<div v-if="pending">
Loading...
</div>
<div v-else-if="error?.data?.code">
Error: {{ error.data.code }}
</div>
<div v-else>
<ul>
<li
v-for="t in todos?.slice(0, 10)"
:key="t.id"
>
<NuxtLink
:class="{ completed: t.completed }"
:to="`/todo/${t.id}`"
>
Title: {{ t.title }}
</NuxtLink>
</li>
</ul>
<button @click="addTodo">
Add Todo
</button>
<button @click="() => refresh()">
Refresh
</button>
</div>
</div>
</template>

View File

@@ -1,18 +1,19 @@
<script setup lang="ts">
const route = useRoute()
const { data: todo, pending, error } = await useAsyncQuery(['getTodo', Number(route.params.id)])
const { $client } = useNuxtApp()
const { data: todo, pending, error } = await useAsyncData(() => $client.todo.getTodo.query(Number(route.params.id)))
</script>
<template>
<div v-if="pending">
Loading...
</div>
<div v-else-if="error?.data?.code">
{{ error.data.code }}
<div v-else-if="error">
{{ error.message }} - {{ error.cause }}
</div>
<div v-else>
ID: {{ todo.id }} <br>
Title: {{ todo.title }} <br>
Completed: {{ todo.completed }}
ID: {{ todo?.id }} <br>
Title: {{ todo?.title }} <br>
Completed: {{ todo?.completed }}
</div>
</template>

View File

@@ -0,0 +1,25 @@
import { loggerLink } from '@trpc/client'
import superjson from 'superjson'
import { createTRPCNuxtClient, httpBatchLink } from 'trpc-nuxt/client'
import type { AppRouter } from '~~/server/trpc/routers'
export default defineNuxtPlugin(() => {
const client = createTRPCNuxtClient<AppRouter>({
transformer: superjson,
links: [
// adds pretty logs to your console in development and logs errors in production
loggerLink({
enabled: opts =>
process.env.NODE_ENV === 'development' ||
(opts.direction === 'down' && opts.result instanceof Error)
}),
httpBatchLink()
]
})
return {
provide: {
client
}
}
})

View File

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

View File

@@ -0,0 +1,18 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { inferAsyncReturnType } from '@trpc/server'
import type { H3Event } from 'h3'
export type Context = inferAsyncReturnType<typeof createContext>
/**
* Creates context for an incoming request
* @link https://trpc.io/docs/context
*/
export function createContext (
event: H3Event
) {
// for API-response caching see https://trpc.io/docs/caching
// console.log('cookies', parseCookies(event))
return {}
}

View File

@@ -1,53 +0,0 @@
import * as trpc from '@trpc/server'
import type { inferAsyncReturnType } from '@trpc/server'
import { z } from 'zod'
import type { CompatibilityEvent } from 'h3'
import { useCookies } from 'h3'
const baseURL = 'https://jsonplaceholder.typicode.com'
const TodoShape = z.object({
userId: z.number(),
id: z.number(),
title: z.string(),
completed: z.boolean(),
})
export type Todo = z.infer<typeof TodoShape>
export const router = trpc.router<Context>()
.query('getTodos', {
async resolve() {
return await $fetch<Todo[]>(`${baseURL}/todos`)
},
})
.query('getTodo', {
input: z.number(),
async resolve(req) {
return await $fetch<Todo>(`${baseURL}/todos/${req.input}`)
},
})
.mutation('addTodo', {
input: TodoShape,
async resolve(req) {
return await $fetch<Todo>(`${baseURL}/todos`, {
method: 'POST',
body: req.input,
})
},
})
export async function createContext(event: CompatibilityEvent) {
// Create your context based on the request object
// Will be available as `ctx` in all your resolvers
// This is just an example of something you'd might want to do in your ctx fn
// const x = useCookies(event)
console.log(event.req.headers)
return {
}
}
type Context = inferAsyncReturnType<typeof createContext>

View File

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

View File

@@ -0,0 +1,33 @@
import { z } from 'zod'
import { publicProcedure, router } from '../trpc'
const baseURL = 'https://jsonplaceholder.typicode.com'
const TodoShape = z.object({
userId: z.number(),
id: z.number(),
title: z.string(),
completed: z.boolean()
})
export type Todo = z.infer<typeof TodoShape>
export const todoRouter = router({
getTodos: publicProcedure
.query(() => {
return $fetch<Todo[]>(`${baseURL}/todos`)
}),
getTodo: publicProcedure
.input(z.number())
.query((req) => {
return $fetch<Todo>(`${baseURL}/todos/${req.input}`)
}),
addTodo: publicProcedure
.input(TodoShape)
.mutation((req) => {
return $fetch<Todo>(`${baseURL}/todos`, {
method: 'POST',
body: req.input
})
})
})

View File

@@ -0,0 +1,33 @@
import { z } from 'zod'
import { publicProcedure, router } from '../trpc'
const baseURL = 'https://jsonplaceholder.typicode.com'
const UserShape = z.object({
id: z.number(),
name: z.string(),
username: z.string(),
email: z.string()
})
export type User = z.infer<typeof UserShape>
export const userRouter = router({
getUsers: publicProcedure
.query(() => {
return $fetch<User[]>(`${baseURL}/users`)
}),
getUser: publicProcedure
.input(z.number())
.query((req) => {
return $fetch<User>(`${baseURL}/users/${req.input}`)
}),
addUser: publicProcedure
.input(UserShape)
.mutation((req) => {
return $fetch<User>(`${baseURL}/users`, {
method: 'POST',
body: req.input
})
})
})

View File

@@ -0,0 +1,43 @@
import { initTRPC } from '@trpc/server'
import superjson from 'superjson'
import { ZodError } from 'zod'
import type { Context } from './context'
const t = initTRPC.context<Context>().create({
transformer: superjson,
errorFormatter ({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.code === 'BAD_REQUEST' &&
error.cause instanceof ZodError
? error.cause!.flatten()
: null
}
}
}
})
/**
* Create a router
* @see https://trpc.io/docs/v10/router
*/
export const router = t.router
/**
* Create an unprotected procedure
* @see https://trpc.io/docs/v10/procedures
**/
export const publicProcedure = t.procedure
/**
* @see https://trpc.io/docs/v10/middlewares
*/
export const middleware = t.middleware
/**
* @see https://trpc.io/docs/v10/merging-routers
*/
export const mergeRouters = t.mergeRouters

4
playground/tsconfig.json Normal file
View File

@@ -0,0 +1,4 @@
{
// https://v3.nuxtjs.org/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}

8784
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,102 +0,0 @@
## 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.
### Create context from request headers
```ts
// ~/server/trpc/index.ts
import type { inferAsyncReturnType } from '@trpc/server'
import type { CompatibilityEvent } from 'h3'
import { decodeAndVerifyJwtToken } from '~/somewhere/in/your/app/utils'
// The app's context - is generated for each incoming request
export async function createContext({ req }: CompatibilityEvent) {
// Create your context based on the request object
// Will be available as `ctx` in all your resolvers
// This is just an example of something you'd might want to do in your ctx fn
async function getUserFromHeader() {
if (req.headers.authorization) {
const user = await decodeAndVerifyJwtToken(req.headers.authorization.split(' ')[1])
return user
}
return null
}
const user = await getUserFromHeader()
return {
user,
}
}
type Context = inferAsyncReturnType<typeof createContext>
// [..] Define API handler and app router
```
### Option 1: Authorize using resolver
```ts
import { TRPCError } from '@trpc/server'
export const router = trpc
.router<Context>()
// open for anyone
.query('hello', {
input: z.string().nullish(),
resolve: ({ input, ctx }) => {
return `hello ${input ?? ctx.user?.name ?? 'world'}`
},
})
// checked in resolver
.query('secret', {
resolve: ({ ctx }) => {
if (!ctx.user)
throw new TRPCError({ code: 'UNAUTHORIZED' })
return {
secret: 'sauce',
}
},
})
```
### Option 2: Authorize using middleware
```ts
import * as trpc from '@trpc/server'
import { TRPCError } from '@trpc/server'
// Merging routers: https://trpc.io/docs/merging-routers
export const router = trpc
.router<Context>()
// this is accessible for everyone
.query('hello', {
input: z.string().nullish(),
resolve: ({ input, ctx }) => {
return `hello ${input ?? ctx.user?.name ?? 'world'}`
},
})
.merge(
'admin.',
trpc.router<Context>()
// this protects all procedures defined next in this router
.middleware(async ({ ctx, next }) => {
if (!ctx.user?.isAdmin)
throw new TRPCError({ code: 'UNAUTHORIZED' })
return next()
})
.query('secret', {
resolve: ({ ctx }) => {
return {
secret: 'sauce',
}
},
}),
)
```
Learn more about authorization [here](https://trpc.io/docs/authorization).

View File

@@ -1,41 +0,0 @@
## Error Formatting
The error formatting in your router will be inferred all the way to your client (& Vue components).
### Adding custom formatting
```ts
// ~/server/trpc/index.ts
import * as trpc from '@trpc/server'
export const router = trpc.router<Context>()
.formatError(({ shape, error }) => {
return {
...shape,
data: {
...shape.data,
zodError:
error.code === 'BAD_REQUEST'
&& error.cause instanceof ZodError
? error.cause.flatten()
: null,
}
}
})
```
### Usage in Vue
```html
<script setup lang="ts">
const { error } = await useAsyncQuery(['getUser', { id: 69 }])
</script>
<template>
<pre v-if="error?.data?.zodError">
{{ JSON.stringify(error.data.zodError, null, 2) }}
</pre>
</template>
```
Learn more about error formatting [here](https://trpc.io/docs/error-formatting).

View File

@@ -1,15 +0,0 @@
## Handling errors
All errors that occur in a procedure go through the `onError` method before being sent to the client. Here you can handle or change errors.
```ts
// ~/server/trpc/index.ts
import * as trpc from '@trpc/server'
export function onError({ error, type, path, input, ctx, req }) {
console.error('Error:', error)
if (error.code === 'INTERNAL_SERVER_ERROR') {
// send to bug reporting
}
}
```

View File

@@ -1,80 +0,0 @@
## Inference Helpers
`@trpc/server` exports the following helper types to assist with inferring these types from the `router` exported in `~/server/trpc/index.ts`:
- `inferProcedureOutput<TProcedure>`
- `inferProcedureInput<TProcedure>`
- `inferSubscriptionOutput<TRouter, TPath>`
```ts
// ~/utils/trpc.ts
import type { router } from '~/server/trpc/index.ts'
type AppRouter = typeof router
/**
* Enum containing all api query paths
*/
export type TQuery = keyof AppRouter['_def']['queries']
/**
* Enum containing all api mutation paths
*/
export type TMutation = keyof AppRouter['_def']['mutations']
/**
* Enum containing all api subscription paths
*/
export type TSubscription = keyof AppRouter['_def']['subscriptions']
/**
* This is a helper method to infer the output of a query resolver
* @example type HelloOutput = InferQueryOutput<'hello'>
*/
export type InferQueryOutput<TRouteKey extends TQuery> = inferProcedureOutput<
AppRouter['_def']['queries'][TRouteKey]
>
/**
* This is a helper method to infer the input of a query resolver
* @example type HelloInput = InferQueryInput<'hello'>
*/
export type InferQueryInput<TRouteKey extends TQuery> = inferProcedureInput<
AppRouter['_def']['queries'][TRouteKey]
>
/**
* This is a helper method to infer the output of a mutation resolver
* @example type HelloOutput = InferMutationOutput<'hello'>
*/
export type InferMutationOutput<TRouteKey extends TMutation> =
inferProcedureOutput<AppRouter['_def']['mutations'][TRouteKey]>
/**
* This is a helper method to infer the input of a mutation resolver
* @example type HelloInput = InferMutationInput<'hello'>
*/
export type InferMutationInput<TRouteKey extends TMutation> =
inferProcedureInput<AppRouter['_def']['mutations'][TRouteKey]>
/**
* This is a helper method to infer the output of a subscription resolver
* @example type HelloOutput = InferSubscriptionOutput<'hello'>
*/
export type InferSubscriptionOutput<TRouteKey extends TSubscription> =
inferProcedureOutput<AppRouter['_def']['subscriptions'][TRouteKey]>
/**
* This is a helper method to infer the asynchronous output of a subscription resolver
* @example type HelloAsyncOutput = InferAsyncSubscriptionOutput<'hello'>
*/
export type InferAsyncSubscriptionOutput<TRouteKey extends TSubscription> =
inferSubscriptionOutput<AppRouter, TRouteKey>
/**
* This is a helper method to infer the input of a subscription resolver
* @example type HelloInput = InferSubscriptionInput<'hello'>
*/
export type InferSubscriptionInput<TRouteKey extends TSubscription> =
inferProcedureInput<AppRouter['_def']['subscriptions'][TRouteKey]>
```

View File

@@ -1,46 +0,0 @@
# Merging Routers
Writing all API-code in your code in the same file is not a great idea. It's easy to merge routers with other routers.
Define your routes:
```ts
// ~/server/trpc/routes/posts.ts
export const posts = trpc.router()
.query('list', {
resolve() {
// ..
return []
}
})
```
```ts
// ~/server/trpc/routes/users.ts
export const users = trpc.router()
.query('list', {
resolve() {
// ..
return []
}
})
```
```ts
// ~/server/trpc/index.ts
import { users } from './routes/users'
import { posts } from './routes/posts'
export const router = trpc.router()
.merge('user.', users) // prefix user procedures with "user."
.merge('post.', posts) // prefix post procedures with "post."
```
and use it like this:
```html
<script setup lang="ts">
const { data: users } = await useAsyncQuery(['user.list'])
const { data: posts } = await useAsyncQuery(['post.list'])
</script>
```

View File

@@ -1,49 +0,0 @@
## Validation
tRPC works out-of-the-box with yup/superstruct/zod/myzod/custom validators.
### Input Validation
```ts
// ~/server/trpc/index.ts
import { z } from 'zod'
export const router = trpc
.router()
.mutation('createUser', {
// validate input with Zod
input: z.object({
name: z.string().min(5)
}),
async resolve(req) {
// use your ORM of choice
return await UserModel.create({
data: req.input,
})
},
})
```
### Output Validation
```ts
// ~/server/trpc/index.ts
import { z } from 'zod'
export const router = trpc
.router()
.query('hello', {
// validate output with Zod
output: z.object({
greeting: z.string()
}),
// expects return type of { greeting: string }
resolve() {
return {
greeting: 'hello!',
}
},
})
```
Learn more about input validation [here](https://trpc.io/docs/router#input-validation).

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

@@ -0,0 +1,81 @@
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 } 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, input)
return useAsyncData(queryKey, () => (client as any)[path].query(input, {
signal: controller?.signal,
...trpc
}), asyncDataOptions)
}
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'

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

@@ -0,0 +1,79 @@
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/internals/httpUtils'
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',
headers () {
return headers
},
fetch: customFetch as FetchEsque,
...opts,
})
}

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

@@ -0,0 +1,80 @@
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,
KeyOfRes,
PickFrom,
_Transform
} from 'nuxt/dist/app/composables/asyncData'
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 DecorateProcedure<
TProcedure extends AnyProcedure,
TRouter extends AnyRouter,
> = TProcedure extends AnyQueryProcedure
? {
useQuery: <
TData = inferTransformedProcedureOutput<TProcedure>,
Transform extends _Transform<TData> = _Transform<TData, TData>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>,
>(
input: inferProcedureInput<TProcedure>,
opts?: AsyncDataOptions<TData, Transform, PickKeys> & { trpc?: TRPCRequestOptions },
) => AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, TRPCClientErrorLike<TProcedure>>,
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;
}

130
src/index.ts Normal file
View File

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

View File

@@ -1,69 +0,0 @@
import { fileURLToPath } from 'url'
import { join, resolve } from 'pathe'
import { defu } from 'defu'
import { addPlugin, addServerHandler, addTemplate, defineNuxtModule } from '@nuxt/kit'
export interface ModuleOptions {
baseURL: string
trpcURL: string
}
export default defineNuxtModule<ModuleOptions>({
meta: {
name: 'trpc-nuxt',
configKey: 'trpc',
},
defaults: {
baseURL: 'http://localhost:3000',
trpcURL: '/api/trpc',
},
async setup(options, nuxt) {
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
nuxt.options.build.transpile.push(runtimeDir, '#build/trpc-handler')
const handlerPath = join(nuxt.options.buildDir, 'trpc-handler.ts')
const trpcOptionsPath = join(nuxt.options.rootDir, 'server/trpc')
// Add vueuse
nuxt.options.modules.push('@vueuse/nuxt')
// Final resolved configuration
const finalConfig = nuxt.options.runtimeConfig.public.trpc = defu(nuxt.options.runtimeConfig.public.trpc, {
baseURL: options.baseURL,
trpcURL: options.trpcURL,
})
nuxt.hook('autoImports:extend', (imports) => {
imports.push(
{ name: 'useClient', from: join(runtimeDir, 'client') },
{ name: 'useAsyncQuery', from: join(runtimeDir, 'client') },
{ name: 'useClientHeaders', from: join(runtimeDir, 'client') },
)
})
addServerHandler({
route: `${finalConfig.trpcURL}/*`,
handler: handlerPath,
})
addPlugin(resolve(runtimeDir, 'plugin'))
addTemplate({
filename: 'trpc-handler.ts',
write: true,
getContents() {
return `
import { createTRPCHandler } from 'trpc-nuxt/api'
import * as functions from '${trpcOptionsPath}'
export default createTRPCHandler({
...functions,
trpcURL: '${finalConfig.trpcURL}'
})
`
},
})
},
})

View File

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

View File

@@ -1,73 +0,0 @@
import type {
AsyncData,
AsyncDataOptions,
KeyOfRes,
PickFrom,
_Transform,
} from 'nuxt/dist/app/composables/asyncData'
import type { ProcedureRecord, inferHandlerInput, inferProcedureInput, inferProcedureOutput } from '@trpc/server'
import type { TRPCClient, TRPCClientErrorLike } from '@trpc/client'
import { objectHash } from 'ohash'
import type { Ref } from 'vue'
import { useStorage } from '@vueuse/core'
import { useAsyncData, useNuxtApp, useState } from '#app'
import type { router } from '~/server/trpc'
type MaybeRef<T> = T | Ref<T>
type AppRouter = typeof router
export type inferProcedures<
TObj extends ProcedureRecord<any, any, any, any, any, any>,
> = {
[TPath in keyof TObj]: {
input: inferProcedureInput<TObj[TPath]>
output: inferProcedureOutput<TObj[TPath]>
};
}
export type TQueries = AppRouter['_def']['queries']
export type TError = TRPCClientErrorLike<AppRouter>
export type TQueryValues = inferProcedures<AppRouter['_def']['queries']>
export async function useAsyncQuery<
TPath extends keyof TQueryValues & string,
TOutput extends TQueryValues[TPath]['output'] = TQueryValues[TPath]['output'],
Transform extends _Transform<TOutput> = _Transform<TOutput, TOutput>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>,
>(
pathAndInput: [path: TPath, ...args: inferHandlerInput<TQueries[TPath]>],
options: AsyncDataOptions<TOutput, Transform, PickKeys> = {},
): Promise<AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, TError>> {
const { $client } = useNuxtApp()
const key = `${pathAndInput[0]}-${objectHash(pathAndInput[1] ? JSON.stringify(pathAndInput[1]) : '')}`
const serverError = useState<TError | null>(`error-${key}`, () => null)
const { error, data, ...rest } = await useAsyncData(
key,
() => $client.query(...pathAndInput),
// @ts-expect-error: Internal
options,
)
if (process.server && error.value && !serverError.value)
serverError.value = error.value as any
if (data.value)
serverError.value = null
return {
...rest,
data,
error: serverError,
} as any
}
export function useClient(): TRPCClient<AppRouter> {
const { $client } = useNuxtApp()
return $client
}
export function useClientHeaders(initialValue: MaybeRef<Record<string, any>> = {}): Ref<Record<string, any>> {
return useStorage('trpc-nuxt-header', initialValue)
}

View File

@@ -1,30 +0,0 @@
import * as trpc from '@trpc/client'
import { unref } from 'vue'
import { useClientHeaders } from './client'
import { defineNuxtPlugin, useRequestHeaders, useRuntimeConfig } from '#app'
import type { router } from '~/server/trpc'
declare type AppRouter = typeof router
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig().public.trpc
const headers = useRequestHeaders()
const otherHeaders = useClientHeaders()
const client = trpc.createTRPCClient<AppRouter>({
url: `${config.baseURL}${config.trpcURL}`,
headers: () => {
return {
...unref(otherHeaders),
...headers,
}
},
})
nuxtApp.provide('client', client)
})
declare module '#app' {
interface NuxtApp {
$client: trpc.TRPCClient<AppRouter>
}
}

View File

@@ -1,3 +1,16 @@
{
"extends": "./playground/.nuxt/tsconfig.json"
"compilerOptions": {
"target": "es2020",
"module": "esnext",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"skipLibCheck": true,
"noUnusedLocals": true,
"noImplicitAny": true,
"allowJs": true,
"noEmit": true,
"resolveJsonModule": true,
"skipDefaultLibCheck": true
}
}

15
tsup.config.ts Normal file
View File

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