orpc-nuxt 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -7
- package/dist/module.json +1 -1
- package/dist/module.mjs +5 -4
- package/dist/runtime/client/create.js +7 -10
- package/dist/runtime/composables.d.ts +1 -1
- package/dist/runtime/composables.js +1 -1
- package/dist/runtime/nuxt/composables.d.ts +10 -0
- package/dist/runtime/nuxt/composables.js +10 -0
- package/dist/runtime/nuxt/define-plugin.js +2 -3
- package/dist/runtime/vue-query/query-client.d.ts +12 -0
- package/dist/runtime/vue-query/query-client.js +9 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Inspired by [trpc-nuxt](https://github.com/wobsoriano/trpc-nuxt).
|
|
|
16
16
|
npm install orpc-nuxt @orpc/client@2.0.0-beta.35 @orpc/server@2.0.0-beta.35 @orpc/tanstack-query@2.0.0-beta.35 @tanstack/vue-query
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Install oRPC by version: v2 is still in beta, and this package needs `2.0.0-beta.35` or newer.
|
|
20
20
|
|
|
21
21
|
## Setup
|
|
22
22
|
|
|
@@ -68,8 +68,6 @@ const query = await orpc.blog.posts.get.useQuery({ id: 1 })
|
|
|
68
68
|
- `query.refetch()` fetches the current query again.
|
|
69
69
|
- `query.invalidate()` marks its cached result as stale and refetches it if it is active.
|
|
70
70
|
|
|
71
|
-
You can also access the client as `useNuxtApp().$orpc`; both accessors infer types from your plugin.
|
|
72
|
-
|
|
73
71
|
### Reactive input and options
|
|
74
72
|
|
|
75
73
|
Pass a ref, reactive object, or getter when the input can change.
|
|
@@ -296,6 +294,9 @@ Only headers listed in `forwardHeaders` are forwarded from the incoming SSR requ
|
|
|
296
294
|
Use `createORPCNuxtClient` when you need a custom transport or want SSR to call the router directly.
|
|
297
295
|
Instead of the shared HTTP plugin above, add a browser plugin and a server plugin.
|
|
298
296
|
|
|
297
|
+
Keep `orpc-nuxt` in `modules`: manual setup replaces that plugin, not the module that installs the QueryClient and the composables.
|
|
298
|
+
Both plugins provide the client under the `orpc` key: `useOrpc()` reads it as `useNuxtApp().$orpc` and infers the router type from that injection.
|
|
299
|
+
|
|
299
300
|
The browser plugin sends requests to `/rpc` over HTTP:
|
|
300
301
|
|
|
301
302
|
```ts
|
|
@@ -336,6 +337,9 @@ export default defineNuxtPlugin(() => {
|
|
|
336
337
|
})
|
|
337
338
|
```
|
|
338
339
|
|
|
340
|
+
Let both plugins infer the client type instead of annotating it.
|
|
341
|
+
Nuxt combines what they provide, so a widened type in either one leaves `useOrpc()` without procedure types.
|
|
342
|
+
|
|
339
343
|
### SSR and cache configuration
|
|
340
344
|
|
|
341
345
|
The module gives each server request its own QueryClient, which manages the query cache.
|
|
@@ -390,6 +394,19 @@ export default defineNuxtPlugin({
|
|
|
390
394
|
|
|
391
395
|
Nuxt registers the handlers declared in `hooks` before running plugins, so this handler is ready when the module creates the QueryClient.
|
|
392
396
|
|
|
397
|
+
### Query client
|
|
398
|
+
|
|
399
|
+
`useOrpcQueryClient()` returns the QueryClient installed for the app, whether by the module or by your own plugin.
|
|
400
|
+
Unlike Vue Query's `useQueryClient()`, it also works where Vue injection is unavailable, such as in an event handler or between tests:
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
const queryClient = useOrpcQueryClient()
|
|
404
|
+
queryClient.clear()
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
It needs the Nuxt context, which the browser keeps available once the app has started.
|
|
408
|
+
During server rendering, call it inside `nuxtApp.runWithContext()`.
|
|
409
|
+
|
|
393
410
|
### Existing Vue Query setup
|
|
394
411
|
|
|
395
412
|
If your app already installs Vue Query and transfers its cache between server and browser, disable the module's QueryClient setup:
|
|
@@ -413,9 +430,71 @@ If you have multiple oRPC clients with the same procedure paths, give each a dif
|
|
|
413
430
|
const orpc = createORPCNuxtClient(client, { prefix: "blog" })
|
|
414
431
|
```
|
|
415
432
|
|
|
433
|
+
### Component tests
|
|
434
|
+
|
|
435
|
+
Component tests need the Nuxt environment of `@nuxt/test-utils`:
|
|
436
|
+
|
|
437
|
+
```ts
|
|
438
|
+
// vitest.config.ts
|
|
439
|
+
import { defineVitestConfig } from "@nuxt/test-utils/config"
|
|
440
|
+
|
|
441
|
+
export default defineVitestConfig({})
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
That configuration runs every test under `test/nuxt/` or `tests/nuxt/`, and every test named `*.nuxt.test.ts` or `*.nuxt.spec.ts`, against your application.
|
|
445
|
+
Replace `useOrpc()` with a client that returns fixed data, then mount the component with `mountSuspended()` so its awaited queries resolve before the assertions.
|
|
446
|
+
The replacement can be any nested object of async functions; only the procedures the component calls have to exist.
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
// test/nuxt/post-list.spec.ts
|
|
450
|
+
import { mockNuxtImport, mountSuspended } from "@nuxt/test-utils/runtime"
|
|
451
|
+
import { QueryClient } from "@tanstack/vue-query"
|
|
452
|
+
import { createORPCNuxtClient } from "orpc-nuxt/client"
|
|
453
|
+
import { afterEach, expect, test } from "vitest"
|
|
454
|
+
|
|
455
|
+
import PostList from "~/components/post-list.vue"
|
|
456
|
+
|
|
457
|
+
const queryClient = new QueryClient({
|
|
458
|
+
// Report a failing procedure instead of retrying it until the test times out.
|
|
459
|
+
defaultOptions: { queries: { retry: false } },
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
const orpc = createORPCNuxtClient(
|
|
463
|
+
{
|
|
464
|
+
blog: {
|
|
465
|
+
posts: {
|
|
466
|
+
list: async () => [{ id: 1, title: "First post" }],
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
{ queryClient },
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
// Vitest runs this factory before the file body, so it must return the composable without calling it.
|
|
474
|
+
mockNuxtImport("useOrpc", () => () => orpc)
|
|
475
|
+
|
|
476
|
+
afterEach(() => {
|
|
477
|
+
// Start every test from an empty cache, so an earlier response cannot satisfy a later query.
|
|
478
|
+
queryClient.clear()
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
test("renders the posts", async () => {
|
|
482
|
+
const component = await mountSuspended(PostList)
|
|
483
|
+
expect(component.text()).toContain("First post")
|
|
484
|
+
})
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Passing an explicit `queryClient` gives the tests their own cache:
|
|
488
|
+
|
|
489
|
+
- The application's cache never carries data from one test into the next.
|
|
490
|
+
- Test defaults such as `retry: false` stay in the test file instead of the module options.
|
|
491
|
+
|
|
492
|
+
Omit it to test against the cache the module installs.
|
|
493
|
+
Read that one with `useOrpcQueryClient()`, and set its defaults through the `orpc.queryClient` module options.
|
|
494
|
+
|
|
416
495
|
### Outside Vue components
|
|
417
496
|
|
|
418
|
-
You can call `.useQuery()` and `.useMutation()` outside a component, for example in
|
|
497
|
+
You can call `.useQuery()` and `.useMutation()` outside a component, for example in a script or in a test that never mounts one.
|
|
419
498
|
Create them inside `scope.run()` so Vue can track their reactive subscriptions, then call `scope.stop()` when you are done.
|
|
420
499
|
When Vue injection is unavailable, pass a QueryClient explicitly:
|
|
421
500
|
|
|
@@ -444,9 +523,12 @@ try {
|
|
|
444
523
|
|
|
445
524
|
Install dependencies with `bun install`, then run `bun run build`, `bun run types`, and `bun run test`.
|
|
446
525
|
|
|
526
|
+
Run `bun run test:component` to check the documented component-test recipe in the fixture application; it uses the built package, so build first.
|
|
527
|
+
|
|
447
528
|
To try the package in a Nuxt app, build it and run `bunx nuxt dev tests/fixtures/nuxt`.
|
|
448
529
|
The example app uses the built package, so rebuild after changing its source.
|
|
449
530
|
|
|
450
|
-
Run `bunx playwright install chromium`, then `bun run test:nuxt` to check the packed npm archive with the
|
|
451
|
-
|
|
452
|
-
|
|
531
|
+
Run `bunx playwright install chromium-headless-shell`, then `bun run test:nuxt` to check the packed npm archive with the Nuxt version installed in the workspace.
|
|
532
|
+
It is checked with both module-managed and app-managed QueryClients, including types, SSR, and hydration in development and production.
|
|
533
|
+
|
|
534
|
+
To check other Nuxt versions by hand, pass them explicitly: `bun run test:nuxt 3.14.1592 4.0.1`.
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -14,10 +14,11 @@ const module$1 = defineNuxtModule({
|
|
|
14
14
|
const optimizeDeps = nuxt.options.vite.optimizeDeps ??= {};
|
|
15
15
|
optimizeDeps.exclude ??= [];
|
|
16
16
|
optimizeDeps.exclude.push("orpc-nuxt/client", "@tanstack/vue-query");
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
from:
|
|
20
|
-
|
|
17
|
+
const composables = resolver.resolve("./runtime/composables");
|
|
18
|
+
addImports([
|
|
19
|
+
{ name: "useOrpc", from: composables },
|
|
20
|
+
{ name: "useOrpcQueryClient", from: composables }
|
|
21
|
+
]);
|
|
21
22
|
if (options.queryClient) {
|
|
22
23
|
const config = options.queryClient === true ? {} : options.queryClient;
|
|
23
24
|
addPluginTemplate({
|
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
import { createTanstackQueryUtils } from "@orpc/tanstack-query";
|
|
2
|
-
import {
|
|
3
|
-
useQueryClient,
|
|
4
|
-
VUE_QUERY_CLIENT
|
|
5
|
-
} from "@tanstack/vue-query";
|
|
6
|
-
import { hasInjectionContext, inject } from "vue";
|
|
2
|
+
import { useQueryClient } from "@tanstack/vue-query";
|
|
7
3
|
import { useORPCMutation } from "../vue-query/mutation.js";
|
|
8
4
|
import { useORPCQuery } from "../vue-query/query.js";
|
|
5
|
+
import { resolveQueryClient } from "../vue-query/query-client.js";
|
|
9
6
|
import { decorateClient } from "./decorate.js";
|
|
10
7
|
export function createORPCNuxtClient(client, options = {}) {
|
|
11
8
|
const utils = createTanstackQueryUtils(client, { prefix: options.prefix });
|
|
12
|
-
let queryClient = options.queryClient ?? (
|
|
13
|
-
function
|
|
9
|
+
let queryClient = options.queryClient ?? resolveQueryClient();
|
|
10
|
+
function getQueryClient() {
|
|
14
11
|
return queryClient ??= useQueryClient();
|
|
15
12
|
}
|
|
16
13
|
function createMethods(target) {
|
|
17
14
|
return {
|
|
18
15
|
useQuery(input, queryOptions) {
|
|
19
|
-
return useORPCQuery(target, input, queryOptions,
|
|
16
|
+
return useORPCQuery(target, input, queryOptions, getQueryClient());
|
|
20
17
|
},
|
|
21
18
|
useMutation(mutationOptions) {
|
|
22
|
-
return useORPCMutation(target, mutationOptions,
|
|
19
|
+
return useORPCMutation(target, mutationOptions, getQueryClient());
|
|
23
20
|
},
|
|
24
21
|
invalidate() {
|
|
25
22
|
const utils2 = target;
|
|
26
|
-
return
|
|
23
|
+
return getQueryClient().invalidateQueries({ queryKey: utils2.key() });
|
|
27
24
|
}
|
|
28
25
|
};
|
|
29
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { useOrpc } from "./nuxt/composables.js";
|
|
1
|
+
export { useOrpc, useOrpcQueryClient } from "./nuxt/composables.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { useOrpc } from "./nuxt/composables.js";
|
|
1
|
+
export { useOrpc, useOrpcQueryClient } from "./nuxt/composables.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { QueryClient } from "@tanstack/vue-query";
|
|
1
2
|
import { type NuxtApp } from "nuxt/app";
|
|
2
3
|
type InjectedORPCClient = NuxtApp extends {
|
|
3
4
|
$orpc: infer TClient;
|
|
@@ -8,4 +9,13 @@ type InjectedORPCClient = NuxtApp extends {
|
|
|
8
9
|
* The router type is inferred from that plugin's return value.
|
|
9
10
|
*/
|
|
10
11
|
export declare function useOrpc(): InjectedORPCClient;
|
|
12
|
+
/**
|
|
13
|
+
* Read the QueryClient installed for this Nuxt app, by the module or by your own plugin.
|
|
14
|
+
* Unlike Vue Query's own accessor, this ignores component-level providers and works outside
|
|
15
|
+
* a component, for example in an event handler or when clearing the cache between tests.
|
|
16
|
+
* The browser keeps the Nuxt context available after startup; during server rendering,
|
|
17
|
+
* call this inside nuxtApp.runWithContext().
|
|
18
|
+
* Throws when no QueryClient is installed, as with queryClient: false and no Vue Query plugin.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useOrpcQueryClient(): QueryClient;
|
|
11
21
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useNuxtApp } from "nuxt/app";
|
|
2
|
+
import { resolveQueryClient } from "../vue-query/query-client.js";
|
|
2
3
|
export function useOrpc() {
|
|
3
4
|
const client = useNuxtApp().$orpc;
|
|
4
5
|
if (!client) {
|
|
@@ -6,3 +7,12 @@ export function useOrpc() {
|
|
|
6
7
|
}
|
|
7
8
|
return client;
|
|
8
9
|
}
|
|
10
|
+
export function useOrpcQueryClient() {
|
|
11
|
+
const queryClient = resolveQueryClient(useNuxtApp().vueApp);
|
|
12
|
+
if (!queryClient) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
"Install Vue Query before calling useOrpcQueryClient(). Enable the module's queryClient option, or install Vue Query from a Nuxt plugin."
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return queryClient;
|
|
18
|
+
}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { createORPCClient } from "@orpc/client";
|
|
2
2
|
import { RPCLink } from "@orpc/client/fetch";
|
|
3
|
-
import { VUE_QUERY_CLIENT } from "@tanstack/vue-query";
|
|
4
3
|
import {
|
|
5
4
|
defineNuxtPlugin as createNuxtPlugin,
|
|
6
5
|
useRequestHeaders,
|
|
7
6
|
useRequestURL
|
|
8
7
|
} from "nuxt/app";
|
|
9
|
-
import { inject } from "vue";
|
|
10
8
|
import { createORPCNuxtClient } from "../client/create.js";
|
|
9
|
+
import { resolveQueryClient } from "../vue-query/query-client.js";
|
|
11
10
|
export function defineNuxtPlugin(setup) {
|
|
12
11
|
return createNuxtPlugin((nuxtApp) => {
|
|
13
12
|
const options = setup(nuxtApp);
|
|
14
|
-
const queryClient = options.queryClient ??
|
|
13
|
+
const queryClient = options.queryClient ?? resolveQueryClient(nuxtApp.vueApp);
|
|
15
14
|
if (!queryClient) {
|
|
16
15
|
throw new Error(
|
|
17
16
|
"orpc-nuxt: install Vue Query before the oRPC plugin. Enable the module's QueryClient, use enforce: 'pre' in your Vue Query plugin, or pass queryClient explicitly."
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type QueryClient } from "@tanstack/vue-query";
|
|
2
|
+
import { type App } from "vue";
|
|
3
|
+
/**
|
|
4
|
+
* Find the QueryClient installed by Vue Query, with or without an active injection context.
|
|
5
|
+
* An application is read directly, so a component providing its own cache cannot shadow the one
|
|
6
|
+
* a plugin captured for the whole application.
|
|
7
|
+
* Without an application, the active injection context is the only source.
|
|
8
|
+
*
|
|
9
|
+
* @param app - The application to read, whatever context the call happens in.
|
|
10
|
+
* @returns The installed QueryClient, or undefined when Vue Query is unavailable.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveQueryClient(app?: App): QueryClient | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { VUE_QUERY_CLIENT } from "@tanstack/vue-query";
|
|
2
|
+
import { hasInjectionContext, inject } from "vue";
|
|
3
|
+
export function resolveQueryClient(app) {
|
|
4
|
+
if (app) return app.runWithContext(injectQueryClient);
|
|
5
|
+
return hasInjectionContext() ? injectQueryClient() : void 0;
|
|
6
|
+
}
|
|
7
|
+
function injectQueryClient() {
|
|
8
|
+
return inject(VUE_QUERY_CLIENT, void 0);
|
|
9
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orpc-nuxt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "oRPC integration for Nuxt with TanStack Vue Query composables.",
|
|
5
5
|
"homepage": "https://github.com/IlyaSemenov/orpc-nuxt#readme",
|
|
6
6
|
"bugs": "https://github.com/IlyaSemenov/orpc-nuxt/issues",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"prepare": "lefthook install",
|
|
47
47
|
"prepublishOnly": "bun run build",
|
|
48
48
|
"test": "bun test src tests/runtime",
|
|
49
|
+
"test:component": "vitest run --root tests/fixtures/nuxt",
|
|
49
50
|
"test:nuxt": "bun tests/nuxt/run.ts",
|
|
50
51
|
"types": "tsc --noEmit && tsc --noEmit -p tests/tsconfig.json && nuxt typecheck tests/fixtures/nuxt"
|
|
51
52
|
},
|
|
@@ -58,22 +59,26 @@
|
|
|
58
59
|
"@changesets/cli": "^2.31.1",
|
|
59
60
|
"@nuxt/cli": "^3.37.0",
|
|
60
61
|
"@nuxt/module-builder": "^1.0.3",
|
|
62
|
+
"@nuxt/test-utils": "^4.3.2",
|
|
61
63
|
"@orpc/server": "2.0.0-beta.35",
|
|
62
64
|
"@playwright/test": "1.58.2",
|
|
63
65
|
"@tsconfig/bun": "^1.0.10",
|
|
64
66
|
"@types/bun": "^1.3.14",
|
|
65
67
|
"@vue/server-renderer": "^3.5.0",
|
|
68
|
+
"@vue/test-utils": "^2.5.0",
|
|
69
|
+
"happy-dom": "^20.14.3",
|
|
66
70
|
"nuxt": "^4.5.2",
|
|
67
71
|
"oxfmt": "^0.67.0",
|
|
68
72
|
"oxlint": "^1.82.0",
|
|
69
73
|
"publint": "^0.3.22",
|
|
70
74
|
"typescript": "^5.9.3",
|
|
75
|
+
"vitest": "^5.0.0",
|
|
71
76
|
"vue-tsc": "^3.3.11",
|
|
72
77
|
"zod": "^4.3.6"
|
|
73
78
|
},
|
|
74
79
|
"peerDependencies": {
|
|
75
|
-
"@orpc/client": "2.0.0-beta.35",
|
|
76
|
-
"@orpc/tanstack-query": "2.0.0-beta.35",
|
|
80
|
+
"@orpc/client": "^2.0.0-beta.35",
|
|
81
|
+
"@orpc/tanstack-query": "^2.0.0-beta.35",
|
|
77
82
|
"@tanstack/vue-query": "^5.102.8",
|
|
78
83
|
"nuxt": "^3.14.1592 || ^4.0.1",
|
|
79
84
|
"vue": "^3.5.0"
|