103 |
104 |
105 |
133 |
--------------------------------------------------------------------------------
/src/composables/useConvexQuery/index.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server'
2 | import type { Ref } from 'vue'
3 | import type { OptionalRestArgsAndOptions } from '../../types.ts'
4 | import type { UseConvexQueryOptions, UseConvexQueryReturn } from './types.ts'
5 | import {
6 | getFunctionName,
7 | } from 'convex/server'
8 | import { computed, onScopeDispose, ref, watch } from 'vue'
9 | import { useConvexClient } from '../useConvexClient'
10 | import { useQueryArgs } from './lib/useQueryArgs.ts'
11 | import { useServerQuery } from './lib/useServerQuery.ts'
12 |
13 | /**
14 | * A composable that provides a Realtime Convex query. It supports reactivity and can be used both on the client and server side.
15 | * @param query The Convex query function.
16 | * @param rest The arguments and options for the query.
17 | * @returns The result of the query.
18 | */
19 | export function useConvexQuery>(query: Query, ...rest: OptionalRestArgsAndOptions): UseConvexQueryReturn {
20 | const { args, options } = useQueryArgs(rest)
21 |
22 | const isServer = typeof window === 'undefined'
23 |
24 | // use http client on server-side
25 | if (isServer) {
26 | return useServerQuery(query, args, options)
27 | }
28 |
29 | const convex = useConvexClient()
30 |
31 | // Initial data
32 | const data: Ref | undefined> = ref | undefined>(convex.client.localQueryResult(getFunctionName(query), args.value))
33 | const error = ref(null)
34 |
35 | const suspense = () => {
36 | if (data.value) {
37 | return Promise.resolve(data.value)
38 | }
39 | if (error.value) {
40 | return Promise.reject(error.value)
41 | }
42 |
43 | return new Promise>((resolve, reject) => {
44 | const stop = watch(
45 | () => [data.value, error.value],
46 | ([newData, newError]) => {
47 | if (newData) {
48 | stop()
49 | resolve(newData)
50 | }
51 | else if (newError) {
52 | stop()
53 | reject(newError)
54 | }
55 | },
56 | { immediate: true },
57 | )
58 | })
59 | }
60 |
61 | const handleError = (err: Error) => {
62 | data.value = null
63 | error.value = err
64 | }
65 |
66 | const handleResult = (result: FunctionReturnType) => {
67 | data.value = result
68 | error.value = null
69 | }
70 |
71 | const refetch = async () => {
72 | try {
73 | const result = await convex.query(query, args.value)
74 | handleResult(result)
75 | return result
76 | }
77 | catch (err) {
78 | const error_ = err instanceof Error ? err : new Error('Unknown error occurred')
79 | handleError(error_)
80 | throw error_
81 | }
82 | }
83 |
84 | const createSubscription = (args: FunctionArgs) => {
85 | return convex.onUpdate(
86 | query,
87 | args,
88 | handleResult,
89 | handleError,
90 | )
91 | }
92 |
93 | // recreate subscription when args change
94 | let cancelSubscription: () => void | undefined
95 | watch(args, (newArgs) => {
96 | cancelSubscription?.()
97 |
98 | cancelSubscription = createSubscription(newArgs)
99 | }, {
100 | immediate: true,
101 | })
102 |
103 | // cleanup subscription when component is unmounted
104 | onScopeDispose(() => cancelSubscription?.())
105 |
106 | return {
107 | data,
108 | error,
109 | isPending: computed(() => data.value === undefined),
110 | suspense,
111 | refetch,
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/playground/convex/_generated/server.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * Generated utilities for implementing server-side Convex query and mutation functions.
4 | *
5 | * THIS CODE IS AUTOMATICALLY GENERATED.
6 | *
7 | * To regenerate, run `npx convex dev`.
8 | * @module
9 | */
10 |
11 | import {
12 | actionGeneric,
13 | httpActionGeneric,
14 | queryGeneric,
15 | mutationGeneric,
16 | internalActionGeneric,
17 | internalMutationGeneric,
18 | internalQueryGeneric,
19 | } from "convex/server";
20 |
21 | /**
22 | * Define a query in this Convex app's public API.
23 | *
24 | * This function will be allowed to read your Convex database and will be accessible from the client.
25 | *
26 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
27 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
28 | */
29 | export const query = queryGeneric;
30 |
31 | /**
32 | * Define a query that is only accessible from other Convex functions (but not from the client).
33 | *
34 | * This function will be allowed to read from your Convex database. It will not be accessible from the client.
35 | *
36 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
37 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
38 | */
39 | export const internalQuery = internalQueryGeneric;
40 |
41 | /**
42 | * Define a mutation in this Convex app's public API.
43 | *
44 | * This function will be allowed to modify your Convex database and will be accessible from the client.
45 | *
46 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
47 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
48 | */
49 | export const mutation = mutationGeneric;
50 |
51 | /**
52 | * Define a mutation that is only accessible from other Convex functions (but not from the client).
53 | *
54 | * This function will be allowed to modify your Convex database. It will not be accessible from the client.
55 | *
56 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
57 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
58 | */
59 | export const internalMutation = internalMutationGeneric;
60 |
61 | /**
62 | * Define an action in this Convex app's public API.
63 | *
64 | * An action is a function which can execute any JavaScript code, including non-deterministic
65 | * code and code with side-effects, like calling third-party services.
66 | * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
67 | * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
68 | *
69 | * @param func - The action. It receives an {@link ActionCtx} as its first argument.
70 | * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
71 | */
72 | export const action = actionGeneric;
73 |
74 | /**
75 | * Define an action that is only accessible from other Convex functions (but not from the client).
76 | *
77 | * @param func - The function. It receives an {@link ActionCtx} as its first argument.
78 | * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
79 | */
80 | export const internalAction = internalActionGeneric;
81 |
82 | /**
83 | * Define a Convex HTTP action.
84 | *
85 | * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
86 | * as its second.
87 | * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
88 | */
89 | export const httpAction = httpActionGeneric;
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |