website-astro/src/content/fragments/en/nuxt-graphql-static.md

140 lines
3.2 KiB
Markdown
Raw Normal View History

2022-12-28 10:36:15 +01:00
---
title: Static website and GraphQL queries with Nuxt.js
subtitle: Graphql client is king.
lang: en
slug: "nuxt-graphql-static"
createdAt: "2022-06-08T14:24:06.000Z"
updatedAt: "2022-09-08T13:43:33.000Z"
excerpt: When the most used gql module doesn't work…
tags: ["nuxt.js"]
---
## The problem
I encountered a nasty bug while using static generation with Nuxt and [nuxt apollo](https://github.com/nuxt-community/apollo-module "Dépôt github du module nuxt apollo (new tab)") client.
I found [the issue](https://github.com/nuxt-community/apollo-module/issues/339 "Github issue on nuxt/apollo repository (new tab)") already reported on github.
It seems the module doesn't handle static generation correctly with `nuxt generate`.
I could find request to my local API url after the static generation. Moreover, it also seemed like `<nuxt-link>` navigation was broken.
## The solution 🙌
Fortunately, there is another Nuxt module that handles GraphQL requests!
[Nuxt graphql request to the rescue!](https://github.com/gomah/nuxt-graphql-request)
### The conf
```javascript
// nuxt.config.js
buildModules: [
'nuxt-graphql-request',
],
graphql: {
clients: {
default: {
endpoint: 'http://API_URL/graphql',
options: {
headers: {
authorization: 'Bearer API_TOKEN',
},
},
},
},
},
```
### The request
The best approach so far is to use `asyncData` in pages and `fetch` in components. Using `fetch` in pages does not work well at all with `nuxt generate`.
I also install the `graphql-tag` package (only in `devDependencies`) to be able to import directly `.gql` files.
Query example:
```graphql
# homepage.gql
query {
homepage {
title
subtitle
hero {
id
alt
}
}
}
```
#### Inside a page
```javascript
// index.vue
<script>
import homepageQuery from '~/graphql/queries/singles/homepage'
export default {
async asyncData({ $graphql }) {
const data = await $graphql.default.request(homepageQuery)
return { data }
},
}
</script>
```
#### Inside a component
It is safer to wait until `fetch` has received a response before displaying anything. You can use `$fetchState` to be sure ([documentation](https://nuxtjs.org/docs/2.x/components-glossary/pages-fetch "Documentation on the fetch hook (new tab)")).
```javascript
// Header.vue
<template>
<header v-if="!$fetchState.pending">
</header>
</template>
<script>
import headerQuery from '~/graphql/queries/singles/header'
export default {
data() {
return {
data: {},
}
},
async fetch() {
try {
const data = await this.$graphql.default.request(headerQuery)
this.data = data
} catch (error) {
console.error(JSON.stringify(error, undefined, 2))
}
},
}
</script>
```
### Options
To pass options to the request, for example for a multilingual version with [nuxt/i18n](https://i18n.nuxtjs.org/) and/or a url parameter in a dynamic page:
```javascript
// _slug.vue
<script>
import articleQuery from '~/graphql/queries/articles'
export default {
async asyncData({ $graphql, app, params }) {
const locale = app.i18n.localeProperties.iso
const data = await $graphql.default.request(articleQuery, {
code: locale,
slug: params.slug,
})
return { data }
},
}
</script>
```