Skip to content

Commit

Permalink
feat: catch RPC errors
Browse files Browse the repository at this point in the history
  • Loading branch information
sandros94 committed May 31, 2024
1 parent e577733 commit 358d41c
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 36 deletions.
17 changes: 15 additions & 2 deletions playground/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<pre v-if="data && !error">
{{ data }}
</pre>
<div v-else-if="error">
<div v-else-if="error" style="color: crimson;">
{{ error }}
</div>
<div v-else>
Expand Down Expand Up @@ -35,6 +35,15 @@
<button @click="console.log(removeProduct); executeRemove(); execute()">
Remove
</button>
<hr>
<div v-if="forcedError">
This is a forced error, cought by <b>
useSurrealRPC:
</b>
<div style="color: crimson;">
{{ forcedError }}
</div>
</div>
</div>
</template>

Expand All @@ -47,7 +56,7 @@ interface Product {
currency: string
}
const { create, remove, select } = useSurrealDB({
const { create, remove, select, sql } = useSurrealDB({
database: 'staging',
})
Expand All @@ -64,4 +73,8 @@ const { data: dataCreate, execute: executeCreate } = await create<Product>('prod
const removeProduct = ref('')
const { execute: executeRemove } = await remove(removeProduct)
const { error: forcedError } = await sql<Product[]>('SELECT * ROM products;', {
server: false,
})
</script>
29 changes: 15 additions & 14 deletions src/runtime/composables/surreal-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type {
RpcMethods,
RpcParams,
RpcRequest,
RpcResponse,
RpcResponseOk,
RpcResponseError,
SurrealRpcOptions,
} from '../types'

Expand Down Expand Up @@ -35,7 +36,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function authenticate(
token: MROGParam<any, 'authenticate', 0>,
options?: SurrealRpcOptions<any>,
): Promise<AsyncData<RpcResponse<any> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<any> | null, RpcResponseError | FetchError<any> | null>> {
const { database, key, immediate, token: tokenOvr, watch, ...opts } = options || {}

const params = computed<RpcRequest<any, 'authenticate'>['params']>(() => ([toValue(token)]))
Expand All @@ -62,7 +63,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function create<T = any>(
thing: MROGParam<T, 'create', 0>,
options?: SurrealRpcOptions<T> & { data?: MROGParam<T, 'create', 1> },
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { data, database, immediate, key, token, watch, ...opts } = options || {}

const params = computed<RpcRequest<T, 'create'>['params']>(() => ([toValue(thing), toValue(data)]))
Expand All @@ -86,7 +87,7 @@ export function useSurrealDB(overrides?: Overrides) {
}
async function info<T = any>(
options?: SurrealRpcOptions<T>,
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { database, key, token, ...opts } = options || {}

const _key = key ?? 'Sur_' + hash(['surreal', 'info'])
Expand All @@ -110,7 +111,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function insert<T = any>(
thing: MROGParam<T, 'insert', 0>,
options?: SurrealRpcOptions<T> & { data?: MROGParam<T, 'insert', 1> },
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { data, database, immediate, key, token, watch, ...opts } = options || {}

const params = computed<RpcRequest<T, 'insert'>['params']>(() => ([toValue(thing), toValue(data)]))
Expand All @@ -134,7 +135,7 @@ export function useSurrealDB(overrides?: Overrides) {
}
async function invalidate(
options?: SurrealRpcOptions<any>,
): Promise<AsyncData<RpcResponse<any> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<any> | null, RpcResponseError | FetchError<any> | null>> {
const { database, immediate, key, token, watch, ...opts } = options || {}

const _key = key ?? 'Sur_' + hash(['surreal', 'invalidate'])
Expand All @@ -160,7 +161,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function merge<T = any>(
thing: MROGParam<T, 'merge', 0>,
options: SurrealRpcOptions<T> & { data: MROGParam<T, 'merge', 1> },
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { data, database, immediate, key, token, watch, ...opts } = options

const params = computed<RpcRequest<T, 'merge'>['params']>(() => ([toValue(thing), toValue(data)]))
Expand All @@ -187,7 +188,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function patch<T = any>(
thing: MROGParam<T, 'patch', 0>,
options: SurrealRpcOptions<T> & { patches: MROGParam<T, 'patch', 1>, diff?: MROGParam<T, 'patch', 2> },
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { database, diff, immediate, key, patches, token, watch, ...opts } = options

const params = computed<RpcRequest<T, 'patch'>['params']>(() => ([toValue(thing), toValue(patches), toValue(diff)]))
Expand All @@ -214,7 +215,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function query<T = any>(
sql: MROGParam<T, 'query', 0>,
options?: SurrealRpcOptions<T> & { vars?: MROGParam<T, 'query', 1> },
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { database, key, vars, token, watch, ...opts } = options || {}

const params = computed<RpcRequest<T, 'query'>['params']>(() => ([toValue(sql), toValue(vars)]))
Expand All @@ -239,7 +240,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function remove(
thing: MROGParam<any, 'delete', 0>,
options?: SurrealRpcOptions<any>,
): Promise<AsyncData<RpcResponse<any> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<any> | null, RpcResponseError | FetchError<any> | null>> {
const { database, key, immediate, token, watch, ...opts } = options || {}

const params = computed<RpcRequest<any, 'delete'>['params']>(() => ([toValue(thing)]))
Expand All @@ -265,7 +266,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function select<T = any>(
thing: MROGParam<T, 'select', 0>,
options?: SurrealRpcOptions<T>,
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { database, key, token, watch, ...opts } = options || {}

const params = computed<RpcRequest<T, 'select'>['params']>(() => ([toValue(thing)]))
Expand All @@ -290,7 +291,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function signin<T = any>(
auth: MROGParam<T, 'signin', 0>,
options?: SurrealRpcOptions<T>,
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { database, immediate, key, token, watch, ...opts } = options || {}

const params = computed<RpcRequest<T, 'signin'>['params']>(() => ([toValue(auth)]))
Expand All @@ -316,7 +317,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function signup<T = any>(
auth: MROGParam<T, 'signup', 0>,
options?: SurrealRpcOptions<T>,
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { database, immediate, key, token, watch, ...opts } = options || {}

const params = computed<RpcRequest<T, 'signup'>['params']>(() => ([toValue(auth)]))
Expand All @@ -343,7 +344,7 @@ export function useSurrealDB(overrides?: Overrides) {
async function update<T = any>(
thing: MROGParam<T, 'update', 0>,
options?: SurrealRpcOptions<T> & { data?: MROGParam<T, 'update', 1> },
): Promise<AsyncData<RpcResponse<T> | null, FetchError<any> | null>> {
): Promise<AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null>> {
const { data, database, immediate, key, token, watch, ...opts } = options || {}

const params = computed<RpcRequest<T, 'update'>['params']>(() => ([toValue(thing), toValue(data)]))
Expand Down
25 changes: 17 additions & 8 deletions src/runtime/composables/surreal-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ import {
import type {
KeysOf,
PickFrom,
SurrealFetchOptions,
SurrealUseFetchOptions,
SurrealRpcOptions,
RpcRequest,
RpcResponse,
RpcResponseOk,
RpcResponseError,
} from '../types'
import type {
ComputedRef,
MaybeRefOrGetter,
} from '#imports'
import { ref, toValue } from '#imports'
import { createError, ref, toValue } from '#imports'

export function useSurrealFetch<T = any>(
export function useSurrealFetch<DataT = any, ErrorT = any>(
endpoint: MaybeRefOrGetter<string>,
options: SurrealFetchOptions<T> = {},
): AsyncData<PickFrom<T, KeysOf<T>> | null, FetchError<any> | null> {
options: SurrealUseFetchOptions<DataT> = {},
): AsyncData<PickFrom<DataT, KeysOf<DataT>> | null, ErrorT | FetchError<any> | null> {
const {
database,
token,
Expand All @@ -50,14 +51,22 @@ export function useSurrealRPC<T = any>(
params?: MaybeRefOrGetter<RpcRequest<T>['params']> | ComputedRef<RpcRequest<T>['params']>
},
options?: SurrealRpcOptions<T>,
): AsyncData<RpcResponse<T> | null, FetchError<any> | null> {
): AsyncData<RpcResponseOk<T> | null, RpcResponseError | FetchError<any> | null> {
const id = ref(0)
const { key, ...opts } = options || {}

const _key = key ?? 'Sur_' + hash(['surreal', 'rpc', toValue(req.method), toValue(req.params)])

return useSurrealFetch<RpcResponse<T>>('rpc', {
return useSurrealFetch<RpcResponseOk<T>, RpcResponseError>('rpc', {
...opts,
onResponse({ response }) {
if (response.status === 200 && response._data.error) {
throw createError({
statusCode: response._data.error.code,
statusMessage: response._data.error.message,
})
}
},
method: 'POST',
body: {
id: id.value++,
Expand Down
14 changes: 11 additions & 3 deletions src/runtime/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type {} from 'nitropack'
import type { FetchOptions, ResponseType } from 'ofetch'
import { textToBase64 } from 'undio'

import type { DatabasePreset, Overrides, RpcRequest, RpcResponse } from './types'
import { defineNuxtPlugin, useCookie } from '#app'
import type { DatabasePreset, Overrides, RpcRequest, RpcResponse, SurrealFetchOptions } from './types'
import { createError, defineNuxtPlugin, useCookie } from '#imports'

export default defineNuxtPlugin(({ $config }) => {
const { databases, tokenCookieName } = $config.public.surrealdb
Expand Down Expand Up @@ -120,8 +120,16 @@ export default defineNuxtPlugin(({ $config }) => {
function surrealRPC<T = any>(req: RpcRequest<T>, ovr?: Overrides) {
let id = 0

return surrealFetch<RpcResponse<T>>('rpc', {
return surrealFetch<RpcResponse<T>, string, SurrealFetchOptions>('rpc', {
...surrealFetchOptionsOverride(ovr),
onResponse({ response }) {
if (response.status === 200 && response._data.error) {
throw createError({
statusCode: response._data.error.code,
statusMessage: response._data.error.message,
})
}
},
method: 'POST',
body: {
id: id++,
Expand Down
10 changes: 3 additions & 7 deletions src/runtime/server/utils/use-surreal.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import type { FetchOptions, ResponseType } from 'ofetch'
import type { NitroFetchRequest } from 'nitropack'
import { textToBase64 } from 'undio'
import type { H3Event } from 'h3'
import { getCookie } from 'h3'

import type { DatabasePreset, Overrides, RpcRequest, RpcResponse } from '../../types'
import type { DatabasePreset, Overrides, RpcRequest, RpcResponse, SurrealFetchOptions } from '../../types'
import { useRuntimeConfig } from '#imports'

type Methods = 'get' | 'post' | 'put' | 'patch' | 'delete'

function authTokenFn(dbAuth: DatabasePreset['auth']) {
if (!dbAuth) return undefined
if (typeof dbAuth === 'string') {
Expand All @@ -29,12 +26,11 @@ function authTokenFn(dbAuth: DatabasePreset['auth']) {

export function surrealFetch<
T = any,
R extends NitroFetchRequest = NitroFetchRequest,
O extends ResponseType = ResponseType,
R extends string = string,
>(
event: H3Event,
req: R,
options: Omit<FetchOptions<O>, 'method'> & { method: Uppercase<Methods> | Methods },
options: SurrealFetchOptions,
) {
const { databases, tokenCookieName } = useRuntimeConfig(event).public.surrealdb
const authToken = authTokenFn(databases.default.auth)
Expand Down
12 changes: 10 additions & 2 deletions src/runtime/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { PublicRuntimeConfig } from '@nuxt/schema'
import type { AsyncDataOptions, UseFetchOptions } from 'nuxt/app'
import type { FetchOptions, ResponseType } from 'ofetch'

/* Database Overrides */

Expand All @@ -23,8 +24,13 @@ export interface DatabasePreset {
export type SurrealAsyncDataOptions<T> = AsyncDataOptions<T> & Overrides & {
key?: string
}
export type SurrealFetchOptions<T> = UseFetchOptions<T> & Overrides
export type SurrealRpcOptions<T> = Omit<SurrealFetchOptions<RpcResponse<T>>, 'method' | 'body'>
export type SurrealFetchOptions<
T extends ResponseType = ResponseType,
> = Omit<FetchOptions<T>, 'method'> & {
method?: Uppercase<SurrealMethods> | SurrealMethods
}
export type SurrealUseFetchOptions<T> = UseFetchOptions<T> & Overrides
export type SurrealRpcOptions<T> = Omit<SurrealUseFetchOptions<RpcResponseOk<T>>, 'method' | 'body'>

/* Utils */

Expand Down Expand Up @@ -67,6 +73,8 @@ export type JSONPatch<T> = {
value?: JSONPatchValue<T, JSONPatchPath<T>>
}

export type SurrealMethods = 'get' | 'post' | 'put' | 'patch' | 'delete'

/* SurrealDB RPC Methods and Params types */

type CreateParams<T> = [
Expand Down

0 comments on commit 358d41c

Please sign in to comment.