add useTRPCAsyncData composable

This commit is contained in:
Robert Soriano
2022-05-18 21:40:33 -07:00
parent b9d3a4942c
commit d28895cf5e
9 changed files with 129 additions and 52 deletions

View File

@@ -21,7 +21,7 @@ export default defineNuxtConfig({
trpcURL: '/api/trpc', // defaults to /api/trpc
},
typescript: {
strict: true // set this to true to infer input/output types
strict: true // set this to true to make input/output types work
}
})
```
@@ -64,6 +64,22 @@ console.log(farewell); // => 👈 goodbye
</script>
```
## `useTRPCAsyncData`
A composable that wraps Nuxt's [`useAsyncData`](https://v3.nuxtjs.org/api/composables/use-async-data/) with some modifications to have better error handlings.
```ts
const path = 'hello'
const client = useClient()
const {
data,
pending,
error,
refresh
} = await useTRPCAsyncData(path, () => client.query(path))
```
## Recipes
- [Validation](/recipes/validation.md)

View File

@@ -46,7 +46,9 @@
"bumpp": "^7.1.1",
"eslint": "^8.14.0",
"nuxt": "^3.0.0-rc.3",
"ohash": "^0.1.0",
"pnpm": "^7.1.0",
"superjson": "^1.9.1",
"trpc-nuxt": "workspace:*",
"zod": "^3.16.0"
},

View File

@@ -1,28 +1,20 @@
<script setup lang="ts">
const client = useClient()
const { data, refresh } = await useAsyncData('getUser', () => client.query('getUsers'), {
server: true,
})
const addUser = async (username: string) => {
try {
await client.mutation('createUser', {
username,
})
refresh()
console.log('user added')
}
catch (error) {
console.log(error)
}
}
const key = 'getUser'
const { data, pending, error } = await useTRPCAsyncData(key, () => client.query(key, {
username: 'jcena',
}))
</script>
<template>
<div>
{{ data }}
<div v-if="data">
{{ JSON.stringify(data, null, 2) }}
</div>
<div v-else-if="error">
asdx {{ JSON.stringify(error.data, null, 2) }}
</div>
</div>
<button @click="addUser('marksx')">
add
</button>
</template>

View File

@@ -1,17 +0,0 @@
import { defineNuxtPlugin } from '#app'
export default defineNuxtPlugin((nuxtApp) => {
// if (process.server) {
// nuxtApp.hooks.hook('app:rendered', () => {
// nuxtApp.ssrContext['']
// })
// }
// if (process.client) {
// nuxtApp.hooks.hook('app:created', () => {
// console.log('app:created')
// })
// }
if (nuxtApp.ssrContext)
console.log('hello', nuxtApp.ssrContext)
})

View File

@@ -1,7 +1,10 @@
// ~/server/trpc/index.ts
import { z } from 'zod'
import { ZodError, z } from 'zod'
import * as trpc from '@trpc/server'
import type { inferAsyncReturnType } from '@trpc/server'
import type { CompatibilityEvent } from 'h3'
import type { ResponseMetaFnPayload } from 'trpc-nuxt/api'
// import superjson from 'superjson'
const fakeUsers = [
{ id: 1, username: 'jcena' },
@@ -37,9 +40,30 @@ export const router = trpc
},
})
export const createContext = () => {
// ...
return {
/** context data */
}
export const createContext = (event: CompatibilityEvent) => {
event.res.setHeader('x-ssr', 1)
return {}
}
export const responseMeta = (opts: ResponseMetaFnPayload<any>) => {
// const nuxtApp = useNuxtApp()
// const client = useClient()
// console.log(opts)
// if (nuxtApp.nuxtState) {
// nuxtApp.nuxtState.trpc = client.runtime.transformer.serialize({
// ctx: opts.ctx,
// errors: opts.errors,
// })
// }
// else {
// nuxtApp.nuxtState = {
// trpc: client.runtime.transformer.serialize({
// ctx: opts.ctx,
// errors: opts.errors,
// }),
// }
// }
return {}
}

23
pnpm-lock.yaml generated
View File

@@ -16,8 +16,10 @@ importers:
fs-extra: ^10.1.0
h3: ^0.7.8
nuxt: ^3.0.0-rc.3
ohash: ^0.1.0
pathe: ^0.3.0
pnpm: ^7.1.0
superjson: ^1.9.1
trpc-nuxt: workspace:*
ufo: ^0.8.4
zod: ^3.16.0
@@ -37,7 +39,9 @@ importers:
bumpp: 7.1.1
eslint: 8.15.0
nuxt: 3.0.0-rc.3
ohash: 0.1.0
pnpm: 7.1.1
superjson: 1.9.1
trpc-nuxt: 'link:'
zod: 3.16.0
@@ -1834,6 +1838,13 @@ packages:
/cookie-es/0.5.0:
resolution: {integrity: sha512-RyZrFi6PNpBFbIaQjXDlFIhFVqV42QeKSZX1yQIl6ihImq6vcHNGMtqQ/QzY3RMPuYSkvsRwtnt5M9NeYxKt0g==}
/copy-anything/3.0.2:
resolution: {integrity: sha512-CzATjGXzUQ0EvuvgOCI6A4BGOo2bcVx8B+eC2nF862iv9fopnPQwlrbACakNCHRIJbCSBj+J/9JeDf60k64MkA==}
engines: {node: '>=12.13'}
dependencies:
is-what: 4.1.7
dev: true
/core-util-is/1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
@@ -3818,6 +3829,11 @@ packages:
call-bind: 1.0.2
dev: true
/is-what/4.1.7:
resolution: {integrity: sha512-DBVOQNiPKnGMxRMLIYSwERAS5MVY1B7xYiGnpgctsOFvVDz9f9PFXXxMcTOHuoqYp4NK9qFYQaIC1NRRxLMpBQ==}
engines: {node: '>=12.13'}
dev: true
/is-wsl/2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
@@ -5935,6 +5951,13 @@ packages:
postcss-selector-parser: 6.0.10
dev: true
/superjson/1.9.1:
resolution: {integrity: sha512-oT3HA2nPKlU1+5taFgz/HDy+GEaY+CWEbLzaRJVD4gZ7zMVVC4GDNFdgvAZt6/VuIk6D2R7RtPAiCHwmdzlMmg==}
engines: {node: '>=10'}
dependencies:
copy-anything: 3.0.2
dev: true
/supports-color/5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}

View File

@@ -1,3 +1,4 @@
import { fileURLToPath } from 'url'
import { dirname, join } from 'pathe'
import { addServerHandler, defineNuxtModule } from '@nuxt/kit'
@@ -18,6 +19,9 @@ export default defineNuxtModule<ModuleOptions>({
trpcURL: '/api/trpc',
},
async setup(options, nuxt) {
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
nuxt.options.build.transpile.push(runtimeDir)
const clientPath = join(nuxt.options.buildDir, 'trpc-client.ts')
const handlerPath = join(nuxt.options.buildDir, 'trpc-handler.ts')
@@ -29,6 +33,7 @@ export default defineNuxtModule<ModuleOptions>({
nuxt.hook('autoImports:extend', (imports) => {
imports.push(
{ name: 'useClient', from: clientPath },
{ name: 'useTRPCAsyncData', from: join(runtimeDir, 'composables') },
)
})
@@ -42,21 +47,14 @@ export default defineNuxtModule<ModuleOptions>({
url: '${options.baseURL}${options.trpcURL}',
})
const useClient = () => client
export {
useClient
}
export const useClient = () => client
`)
await fs.writeFile(handlerPath, `
import { createTRPCHandler } from 'trpc-nuxt/api'
import * as functions from '~/server/trpc'
export default createTRPCHandler({
router: functions.router,
...functions
})
export default createTRPCHandler(functions)
`)
},
})

View File

@@ -58,6 +58,8 @@ export function createTRPCHandler<Router extends AnyRouter>({
const $url = createURL(req.url)
event.context.hello = 'world'
const httpResponse = await resolveHTTPResponse({
router,
req: {

View File

@@ -0,0 +1,37 @@
import type {
AsyncData,
KeyOfRes,
PickFrom,
_Transform,
} from 'nuxt/dist/app/composables/asyncData'
import type { AsyncDataOptions, NuxtApp } from '#app'
// @ts-expect-error: Resolved by Nuxt
import { useAsyncData, useState } from '#imports'
export async function useTRPCAsyncData<
DataT,
DataE = Error,
Transform extends _Transform<DataT> = _Transform<DataT, DataT>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>,
>(
key: string,
handler: (ctx?: NuxtApp) => Promise<DataT>,
options: AsyncDataOptions<DataT, Transform, PickKeys> = {},
): Promise<AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE | null | true>> {
const serverError = useState<DataE | true | null>(`error-${key}`, () => null)
const { error, data, ...rest } = await useAsyncData(key, handler, options)
// Only set the value on server and if serverError is empty
if (process.server && error.value && !serverError.value)
serverError.value = error.value as DataE | true | null
// Clear error if data is available
if (data.value)
serverError.value = null
return {
...rest,
data,
error: serverError,
}
}