Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: useAsyncGql data fetching #153

Merged
merged 2 commits into from Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 8 additions & 2 deletions docs/content/1.getting-started/1.quick-start.md
Expand Up @@ -83,7 +83,7 @@ description: 'Learn how to use nuxt-graphql-client module in your Nuxt 3 applica

6. **You're ready to go! ⚡️**

Below is an example using the `launches` query written earlier.
Below is an example using the `launches` query written earlier in step 4.

```vue [app.vue]
<template>
Expand All @@ -99,7 +99,13 @@ description: 'Learn how to use nuxt-graphql-client module in your Nuxt 3 applica
</template>

<script lang="ts" setup>
const { data } = await useAsyncData('starlink', () => GqlLaunches({ limit: 10 }));
const { data } = await useAsyncGql({
operation: 'launches',
variables: { limit: 5 }
});

// Or: based on your preference
// const { data } = await useAsyncGql('launches', { limit: 5 });
</script>
```

Expand Down
18 changes: 18 additions & 0 deletions docs/content/1.getting-started/2.composables.md
Expand Up @@ -3,6 +3,24 @@ title: Composables
description: 'Use composables provided by nuxt-graphql-client'
---

## useAsyncGql

Asynchronously query data that is required to load a page or component. This method takes an `operation` parameter which is the Operation name of the GraphQL query to be executed.

A unique key is automatically generated based on the operation name, it's pertinent client name and the query parameters.

```ts
const { data } = await useAsyncGql('launches', { limit: 5 });

const { data } = await useAsyncGql({
operation: 'launches',
variables: { limit: 5 }
});

// The examples above are equivalent to:
const { data } = await useAsyncData('<data-key>', () => GqlLaunches({ limit: 5 }))
```

## useGqlCors

Add CORS headers to subsequent requests
Expand Down
12 changes: 5 additions & 7 deletions module/package.json
Expand Up @@ -36,20 +36,18 @@
"prepack": "nuxt-module-build"
},
"dependencies": {
"@graphql-codegen/cli": "^2.11.3",
"@graphql-codegen/typescript": "^2.7.2",
"@graphql-codegen/typescript-graphql-request": "^4.5.2",
"@graphql-codegen/typescript-operations": "^2.5.2",
"@graphql-codegen/cli": "^2.11.5",
"@graphql-codegen/typescript": "^2.7.3",
"@graphql-codegen/typescript-graphql-request": "^4.5.3",
"@graphql-codegen/typescript-operations": "^2.5.3",
"@nuxt/kit": "^3.0.0-rc.6",
"defu": "^6.0.0",
"graphql": "^16.5.0",
"graphql-request": "^4.3.0",
"ohash": "^0.1.5",
"scule": "^0.3.2"
},
"devDependencies": {
"nuxt": "^3.0.0-rc.6"
},
"resolutions": {
"cosmiconfig-typescript-loader": "^3.1.0"
}
}
5 changes: 3 additions & 2 deletions module/src/context.ts
Expand Up @@ -33,10 +33,11 @@ export function prepareContext (ctx: GqlContext, prefix: string) {
ctx.generateImports = () => [
'import { useGql } from \'#imports\'',
'const ctx = { instance: null }',
'const GqlInstance = () => {',
'export const GqlInstance = () => {',
' if (!ctx?.instance) {ctx.instance = useGql()}',
' return ctx.instance',
'}',
`export const GqlOperations = ${JSON.stringify(ctx.clientOps)}`,
...ctx.fns.map(f => fnExp(f))
].join('\n')

Expand Down Expand Up @@ -76,7 +77,7 @@ export async function prepareOperations (ctx: GqlContext, path: string[]) {
})

for (const op of operations) {
clientToUse = new RegExp(`^(${ctx.clients.join('|')}[^_]*)`).exec(op)?.[0] || clientToUse
clientToUse = new RegExp(`^(${ctx.clients.join('|')})(?=\\_)`).exec(op)?.[0] || clientToUse

if (!clientToUse || !ctx.clientOps?.[clientToUse]) {
clientToUse = clientToUse || ctx.clients.find(c => c === 'default') || ctx.clients[0]
Expand Down
3 changes: 2 additions & 1 deletion module/src/module.ts
Expand Up @@ -49,6 +49,7 @@ export default defineNuxtModule<GqlConfig>({

if (!host) { throw new Error('GQL_HOST is not set in public runtimeConfig') }

ctx.clients = ['default']
config.clients = !clientHost ? { default: host } : { default: { host, clientHost } }
}

Expand Down Expand Up @@ -147,10 +148,10 @@ export default defineNuxtModule<GqlConfig>({
})

if (Object.keys(config.clients).length > 1 || !config.clients?.default) {
await prepareOperations(ctx, documents)
prepareTemplate(ctx)
}

await prepareOperations(ctx, documents)
prepareContext(ctx, config.functionPrefix)
}

Expand Down
44 changes: 42 additions & 2 deletions module/src/runtime/composables/index.ts
@@ -1,7 +1,11 @@
import { hash } from 'ohash'
import type { Ref } from 'vue'
import type { GqlClients } from '#build/gql'
// @ts-ignore
import { GqlOperations, GqlInstance } from '#build/gql'
import type { GqlClients, GqlFunc } from '#build/gql'
import { getSdk as gqlSdk } from '#build/gql-sdk'
import { useState, useNuxtApp, useRuntimeConfig } from '#imports'
import { useState, useNuxtApp, useAsyncData, useRuntimeConfig } from '#imports'
import type { AsyncData } from 'nuxt/dist/app/composables'
import type { GqlState, GqlConfig, GqlError, OnGqlError } from '../../types'
import { deepmerge } from '../utils'

Expand Down Expand Up @@ -252,3 +256,39 @@ export const useGqlError = (onError: OnGqlError) => {
}

const useGqlErrorState = () => useState<GqlError>('_gqlErrors', () => null)

/**
* Asynchronously query data that is required to load a page or component.
*
* @param {Object} options
* @param {string} options.operation Name of the query to be executed.
* @param {string} options.variables Variables to be passed to the query.
* @param {Object} options.options AsyncData options.
*/
export function useAsyncGql<
T extends keyof GqlFunc,
P extends Parameters<GqlFunc[T]>['0'],
R extends AsyncData<Awaited<ReturnType<GqlFunc[T]>>, GqlError>,
O extends Parameters<typeof useAsyncData>['2']> (options: { operation: T, variables?: P, options?: O }): Promise<R>

/**
* Asynchronously query data that is required to load a page or component.
*
* @param {string} operation Name of the query to be executed.
* @param {string} variables Variables to be passed to the query.
* @param {Object} options AsyncData options.
*/
export function useAsyncGql<
T extends keyof GqlFunc,
P extends Parameters<GqlFunc[T]>['0'],
R extends AsyncData<Awaited<ReturnType<GqlFunc[T]>>, GqlError>,
O extends Parameters<typeof useAsyncData>['2']> (operation: T, variables?: P, options?: O): Promise<R>

export function useAsyncGql (...args: any[]) {
const operation = (typeof args?.[0] !== 'string' && 'operation' in args?.[0] ? args[0].operation : args[0]) ?? undefined
const variables = (typeof args?.[0] !== 'string' && 'variables' in args?.[0] ? args[0].variables : args[1]) ?? undefined
const options = (typeof args?.[0] !== 'string' && 'options' in args?.[0] ? args[0].options : args[2]) ?? undefined
const client = Object.keys(GqlOperations).find(k => GqlOperations[k].includes(operation)) ?? 'default'
const key = hash({ operation, client, variables })
return useAsyncData(key, () => GqlInstance().handle(client as GqlClients)[operation](variables), options)
}
7 changes: 3 additions & 4 deletions package.json
Expand Up @@ -22,13 +22,12 @@
"@nuxt/module-builder": "latest",
"@nuxt/test-utils": "latest",
"@nuxtjs/eslint-config-typescript": "10.0.0",
"eslint": "8.20.0",
"eslint": "8.21.0",
"nuxt": "^3.0.0-rc.6",
"vitest": "0.20.2"
"vitest": "0.21.0"
},
"resolutions": {
"nuxt-graphql-client": "link:./module",
"cosmiconfig-typescript-loader": "^3.1.0"
"nuxt-graphql-client": "link:./module"
},
"version": "0.0.34"
}
5 changes: 4 additions & 1 deletion playground/app.vue
Expand Up @@ -38,7 +38,10 @@
</template>

<script lang="ts" setup>
const { data, error } = await useAsyncData('starlink', () => GqlLaunches())
const { data, error } = await useAsyncGql({
operation: 'launches',
variables: { limit: 1 }
})

if (error.value) {
// eslint-disable-next-line no-console
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/basic/app.vue
Expand Up @@ -3,7 +3,7 @@
</template>

<script lang="ts" setup>
const { data, error } = await useAsyncData('launches', () => GqlLaunches())
const { data, error } = await useAsyncGql('launches')

if (error.value) {
// eslint-disable-next-line no-console
Expand Down