nuxt-gql-pulse

⚡ Nuxt GQL Pulse
A Nuxt 3/4 module for making GraphQL requests with ease, leveraging the power of Nuxt's composables for doing amazing things.
✨ Features
- 🔌 Multiple GraphQL client support
- ⚡ Composables built on Nuxt's
useAsyncData - 🎯 Simple API, minimal boilerplate
- 🧩 Flexible request options per-client and per-request
- 🔄 SSR-friendly with Nuxt payload caching
- 🦾 Type Strong: type-safe client names (via global
TGqlPulseClientKeytype) - 🗂️ Caching options:
- sessionStorage (SPA-only) for persistent client cache
- Nuxt payload (SSR-friendly) for server-side hydration
- 🛠️ Integrates with
graphql-request(graffle-compatible) - 📦 Works with Nuxt 3 and Nuxt 4
- 🧰 Composables:
useGqlPulseClient(clientName)— access raw clientuseGqlPulseRequest(...)— simple requestuseGqlPulseRawRequest(...)— low-level raw responseuseGqlPulseRequestWithCache(...)— sessionStorage cache (SPA)useAsyncGqlPulse(...)— SSR-friendly async datauseAsyncGqlPulseWithCache(...)— SPA async with sessionStorageuseGqlPulseBatchRequests(...)— batch multiple queriesuseAsyncGqlPulseBatch(...)— async batch with optional payload cache
🚀 Quick Setup
Install the module and peer deps:
npm install nuxt-gql-pulse graphql graphql-request
Add it to your nuxt.config.ts:
export default defineNuxtConfig({
modules: ['nuxt-gql-pulse'],
gqlPulse: {
clients: {
rickandmortyapi: {
endpoint: 'https://rickandmortyapi.com/graphql',
},
},
},
})
That’s it! You can now use Nuxt GQL Pulse in your Nuxt app ✨
Example: Query characters from Rick & Morty API
<script setup lang="ts">
const query = /* GraphQL */ `
query {
characters(page: 1) {
results {
id
name
status
species
}
}
}
`
const { data } = await useAsyncGqlPulse<{
characters: {
results: { id: string; name: string; status: string; species: string }[]
}
}>({
client: 'rickandmortyapi',
document: query,
})
</script>
<template>
<div>
<h2>Rick & Morty Characters</h2>
<ul>
<li v-for="character in data.characters.results" :key="character.id">
{{ character.name }} — {{ character.status }} ({{ character.species }})
</li>
</ul>
</div>
</template>
🔄 Comparison — Why choose Nuxt GQL Pulse?
Below is a practical comparison with relevant Nuxt GraphQL modules (research summary). This shows where features overlap and where nuxt-gql-pulse stands out.
| Capability / Module | nuxt-gql-pulse | nuxt-graphql-request | nuxt-graphql-client | nuxt-graphql-middleware | @nuxtjs/apollo / URQL |
|---|---|---|---|---|---|
| Multiple named clients | ✅ (built-in) | ✅ | ✅ | ⚠️ (server-focused) | ✅ |
Composables on useAsyncData | ✅ (ready) | ✅ (manual use) | ✅ (codegen) | ✅ (server wrappers) | ✅ |
| sessionStorage caching (client persistent) | ✅ unique | ❌ | ❌ | ❌ (in-memory/payload only) | ❌ (uses normalized cache) |
| Nuxt payload / SSR hydration cache | ✅ | ❌ | ⚠️ | ✅ | ✅ |
Integration with graphql-request / graffle | ✅ | ✅ | ⚠️ (may use different clients / codegen) | ⚠️ (server fetch) | ❌ (Apollo/URQL) |
Raw response (rawRequest) support | ✅ | ✅ | ⚠️ | ⚠️ | ✅ (client-specific) |
| Batch requests helper | ✅ | ⚠️ (manual) | ⚠️ | ⚠️ | ✅ (depends on client) |
| Nuxt 3 / 4 support | ✅ | ✅ | ✅ | ✅ | ✅ |
| Lightweight / minimal runtime footprint | ✅ | ✅ | ⚠️ (may add codegen) | ⚠️ | ❌ (Apollo larger) |
📄 Documentation
⚙️ Configuration (nuxt.config.ts)
export default defineNuxtConfig({
modules: ['nuxt-gql-pulse'],
gqlPulse: {
/**
* Define your GraphQL clients
*/
clients: {
// 'rickandmortyapi' was used in examples above instead of 'default'
default: {
/**
* The client endpoint URL
*/
endpoint: 'https://rickandmortyapi.com/graphql',
/**
* Per-client request configuration
* Docs: https://github.com/graffle-js/graffle/blob/graphql-request/examples/configuration-fetch-options.ts
*/
options: {
method?: 'GET' | 'POST';
headers?: Headers | string[][] | Record<string, string> | (() => Headers | string[][] | Record<string, string>);
cache?: 'default' | 'force-cache' | 'no-cache' | 'no-store' | 'only-if-cached' | 'reload';
credentials?: 'include' | 'omit' | 'same-origin';
integrity?: string;
keepalive?: boolean;
mode?: 'same-origin' | 'cors' | 'navigate' | 'no-cors';
priority?: 'auto' | 'high' | 'low';
redirect?: 'error' | 'follow' | 'manual';
referrer?: string;
referrerPolicy?: '' | 'same-origin' | 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
errorPolicy?: 'none' | 'ignore' | 'all';
}
},
secondClient: {
// ... another client config
},
},
/**
* Global options applied to all clients
*/
options: {
// same structure as per-client `options`
},
/**
* Optional
* By default, all composables are auto-imported.
*
* You can exclude some if you only use one or two.
*
* Available auto-import composables:
* [
* 'useGqlPulseRequest',
* 'useGqlPulseRequestWithCache',
* 'useGqlPulseBatchRequests',
* 'useGqlPulseRawRequest',
* 'useAsyncGqlPulse',
* 'useAsyncGqlPulseWithCache',
* 'useAsyncGqlPulseBatch',
* ]
*
* ⚠️ `useGqlPulseClient` is **always** imported by default
* and cannot be excluded.
*/
excludeComposables: [],
},
})
🔑 Type Declarations (index.d.ts)
if you want to have type-safe client names in your project,
you can declare the TGqlPulseClientKey type globally in your project.
normally this is auto-generated in .nuxt/types/gql-pulse.d.ts during build,
but you can also declare it manually in your project for better DX.
declare global {
type TGqlPulseClientKey = 'rickandmortyapi'
}
export {}
🧩 Composables Overview
🔧 API quick reference (signatures)
- useGqlPulseClient(clientName: string | "default"): GraphQLClient — get raw client
- useGqlPulseRequest
(opts): Promise opts = { document, client?, variables? } - useGqlPulseRawRequest
(opts): Promise<{ status, headers, data, errors?, extensions? }> - useGqlPulseRequestWithCache
(opts): Promise<RemovableRef > — sessionStorage cache (SPA) - useAsyncGqlPulse(opts): AsyncDataReturn — SSR-friendly useAsyncData wrapper
- useAsyncGqlPulseWithCache(opts): AsyncDataReturn — SPA sessionStorage cache variant
- useGqlPulseBatchRequests(opts): Promise
— batch many queries at once - useAsyncGqlPulseBatch(opts): AsyncDataReturn — batched useAsyncData with optional payload cache
🔁 Caching: SSR vs SPA
- withPayloadCache / useAsyncGqlPulse → SSR-friendly, data is stored in Nuxt payload and reused during client hydration (no double-fetch).
- *WithCache variants & useGqlPulseRequestWithCache → SPA only (use sessionStorage via @vueuse/core). Use these for client-persistent caching across reloads; not suitable for SSR-only pages.
🔧 API details
useGqlPulseRequest
// Make standard GraphQL requests.
const data = await useGqlPulseRequest({
document, // string | DocumentNode
client: string, // defaults to first client
variables: TVariables,
})
useGqlPulseRawRequest
// Low-level request returning headers, status, and errors.
const res = await useGqlPulseRawRequest({
document: string,
client: string,
variables: TVariables,
}) // { status, headers, data, extensions, errors? }
useGqlPulseClient
// Access the underlying GraphQLClient instance.
// Always auto-imported and cannot be excluded.
const client = useGqlPulseClient('default')
useAsyncGqlPulse
// Nuxt-friendly async data fetching with optional payload cache. (SSR Friendly)
const { data, pending, error } = await useAsyncGqlPulse({
key: 'characters',
document,
variables,
client: 'default',
withPayloadCache: true,
})
useAsyncGqlPulseWithCache
// SPA-only sessionStorage caching.
const data = await useAsyncGqlPulseWithCache({
key: 'characters',
document,
variables,
})
useGqlPulseBatchRequests
// Batch multiple queries into one request.
const result = await useGqlPulseBatchRequests({
documents: [
{ document: query1, variables: { id: 1 } },
{ document: query2, variables: { id: 2 } },
],
client: 'default',
})
useAsyncGqlPulseBatch
// Nuxt-friendly async data fetching for batched requests with optional payload cache. (SSR Friendly)
const { data, pending, error } = await useAsyncGqlPulseBatch({
key: 'batch-1',
documents: [
{ document: query1, variables: { id: 1 } },
{ document: query2, variables: { id: 2 } },
],
client: 'default',
withPayloadCache: true,
})
useGqlPulseRequestWithCache
// SPA-only sessionStorage caching for single requests.
const result = await useGqlPulseRequestWithCache({
key: 'character-1',
document,
variables,
})
🔌 Clients custom configuration (plugins/gqlPulse.ts)
You can also provide custom configuration to clients via a Nuxt plugin:
export default defineNuxtPlugin(() => {
const client = useGqlPulseClient('rickandmortyapi')
const secondClient = useGqlPulseClient('secondClient')
/**
* Request middleware
* Runs before every GraphQL request.
* You can modify headers, body, or any request config here.
*/
const requestMiddleware = async (
request: RequestExtendedInit<Variables>
) => ({
...request,
headers: {
...request.headers,
Authorization: `Bearer token`,
},
})
/**
* Response middleware
* Runs after every GraphQL response.
* Perfect for custom logging or error handling.
*/
const responseMiddleware = async (
response: GraphQLClientResponse<unknown> | Error
) => {
if (response instanceof Error) {
console.error('❌ GraphQL error:', response.message)
} else {
console.log('✅ Response received:', response)
}
}
// Apply middlewares to both clients
for (const c of [client, secondClient]) {
c.requestConfig.requestMiddleware = requestMiddleware
c.requestConfig.responseMiddleware = responseMiddleware
}
/**
* Optional: override other requestConfig options
*/
// client.requestConfig.jsonSerializer = JSON
// client.requestConfig.body = JSON.stringify({ query: "{ characters { name } }" })
// client.requestConfig.signal = AbortSignal.timeout(5000)
// client.requestConfig.window = null
})
👮🏽♂️ Authentication
You can also provide Authentication headers in different ways:
- Static headers in
nuxt.config.ts:
export default defineNuxtConfig({
gqlPulse: {
clients: {
default: {
endpoint: 'https://default.com/graphql',
options: {
headers: {
authorization: 'Bearer TOKEN_HERE',
},
},
},
},
},
})
- Dynamic headers using
requestMiddlewareinplugins/gqlPulse.ts:
const client = useGqlPulseClient('default')
const requestMiddleware = async (request: RequestExtendedInit<Variables>) => {
const token = await getAuthTokenSomehow()
return {
...request,
headers: {
...request.headers,
authorization: `Bearer ${token}`,
},
}
}
client.requestConfig.requestMiddleware = requestMiddleware
- Per-request headers using
useGqlPulseRequestoptions:
const data = await useGqlPulseRequest({
document,
client: 'default',
variables,
requestOptions: {
headers: {
authorization: `Bearer ${token}`,
},
},
})
- using setHeaders() or setHeader() from
useGqlPulseClient():
const client = useGqlPulseClient('rickandmortyapi')
// Replace all existing headers
client.setHeaders({
Authorization: 'Bearer my-secret-token',
'Content-Type': 'application/json',
})
// Set one header without touching the rest
client.setHeader('x-custom-header', 'my-value')
// Point the client to a different GraphQL endpoint
client.setEndpoint('https://custom-api.example.com/graphql')
🧑💻 Usage Examples
See the examples directory for more usage examples:
- Basic Fetch (useGqlPulseRequest)
- Async Data Fetching (useAsyncGqlPulse)
- Using with Pinia (useGqlPulseRequest in Store)
⚙️ Configuration details
gqlPulse.clientsclientName.options mirrors the request options available in graffle/graphql-request (headers, method, requestMiddleware, responseMiddleware, etc.). See graffle examples for advanced options and middleware.
See the graphql-request / graffle. for advanced request configuration and middleware examples.
📑 License & Acknowledgements
This project builds on and integrates the excellent work of the GraphQL community:
- graffle / graphql-request – lightweight GraphQL client used internally.
Please see their repository for license details (MIT-compatible).
This package itself is released under the MIT License.