Hooks API Reference
Complete reference for Nitro hooks provided and used by Nitro GraphQL.
Overview
Nitro GraphQL integrates with Nitro's hook system to provide extension points during the build process and runtime lifecycle. While the module doesn't expose custom GraphQL-specific hooks, it heavily uses Nitro's standard hooks.
Nitro Hooks Used by GraphQL Module
Build Hooks
rollup:before
Called before Rollup bundling starts. Nitro GraphQL uses this to configure bundle behavior for GraphQL files.
Usage in Nitro GraphQL:
// Internal usage
nitro.hooks.hook('rollup:before', (_, rollupConfig) => {
// Configure external dependencies
rollupConfig.external = rollupConfig.external || []
rollupConfig.external.push('oxc-parser', '@oxc-parser')
// Configure chunk handling for GraphQL files
rollupConfig.output.manualChunks = (id, meta) => {
if (id.endsWith('.graphql') || id.endsWith('.gql')) {
return 'schemas'
}
if (id.endsWith('.resolver.ts')) {
return 'resolvers'
}
}
})Custom Hook Usage:
// nitro.config.ts
export default defineNitroConfig({
graphql: { /* ... */ },
hooks: {
'rollup:before': (nitro, config) => {
// Add custom rollup configuration
console.log('Rollup config:', config)
}
}
})types:extend
Called during TypeScript types generation. Nitro GraphQL uses this to add path mappings for virtual imports.
Usage in Nitro GraphQL:
// Internal usage
nitro.hooks.hook('types:extend', (types) => {
// Add path mappings
types.tsConfig.compilerOptions.paths['#graphql/server'] = [
'.nitro/types/nitro-graphql-server.d.ts'
]
types.tsConfig.compilerOptions.paths['#graphql/client'] = [
'.nitro/types/nitro-graphql-client.d.ts'
]
// Add external service paths
for (const service of externalServices) {
types.tsConfig.compilerOptions.paths[`#graphql/client/${service.name}`] = [
`.nitro/types/nitro-graphql-client-${service.name}.d.ts`
]
}
// Include generated types
types.tsConfig.include.push(
'.nitro/types/nitro-graphql-server.d.ts',
'.nitro/types/nitro-graphql-client.d.ts'
)
})Custom Hook Usage:
// nitro.config.ts
export default defineNitroConfig({
graphql: { /* ... */ },
hooks: {
'types:extend': (types) => {
// Add custom type paths
types.tsConfig.compilerOptions.paths['@/types/*'] = [
'./types/*'
]
}
}
})build:before
Called before the build starts. Used to prepare external services for Nuxt.
Usage in Nitro GraphQL:
// Internal usage
nitro.hooks.hook('build:before', () => {
// Pass external services to Nuxt module
if (nitro.options.framework?.name === 'nuxt') {
const nuxtOptions = nitro._nuxt?.options
if (nuxtOptions) {
nuxtOptions.nitroGraphqlExternalServices
= nitro.options.graphql?.externalServices || []
}
}
})Custom Hook Usage:
// nitro.config.ts
export default defineNitroConfig({
graphql: { /* ... */ },
hooks: {
'build:before': () => {
console.log('Build starting...')
// Prepare resources, validate config, etc.
}
}
})Development Hooks
dev:start
Called when development server starts. Nitro GraphQL uses this to scan files and show diagnostics.
Usage in Nitro GraphQL:
// Internal usage
nitro.hooks.hook('dev:start', async () => {
// Scan GraphQL files
const schemas = await scanSchemas(nitro)
const resolvers = await scanResolvers(nitro)
const directives = await scanDirectives(nitro)
const docs = await scanDocs(nitro)
// Show diagnostic box
consola.box({
title: 'Nitro GraphQL',
message: [
`Framework: ${nitro.options.graphql?.framework}`,
`Schemas: ${schemas.length}`,
`Resolvers: ${resolvers.length}`,
`Directives: ${directives.length}`,
`Documents: ${docs.length}`,
'',
'Debug Dashboard: /_nitro/graphql/debug'
].join('\n')
})
})Custom Hook Usage:
// nitro.config.ts
export default defineNitroConfig({
graphql: { /* ... */ },
hooks: {
'dev:start': async () => {
console.log('Dev server started!')
// Initialize dev-only features
}
}
})dev:reload
Called when development server reloads. Can be used to refresh GraphQL schemas.
Custom Hook Usage:
// nitro.config.ts
export default defineNitroConfig({
graphql: { /* ... */ },
hooks: {
'dev:reload': async () => {
console.log('Reloading GraphQL schemas...')
// Refresh cached data, reload external schemas, etc.
}
}
})Runtime Hooks
close
Called when Nitro is closing. Nitro GraphQL uses this for cleanup and final type generation.
Usage in Nitro GraphQL:
// Internal usage
nitro.hooks.hook('close', async () => {
// Final type generation
await serverTypeGeneration(nitro)
await clientTypeGeneration(nitro)
// Close file watcher
watcher.close()
})Custom Hook Usage:
// nitro.config.ts
export default defineNitroConfig({
graphql: { /* ... */ },
hooks: {
close: async () => {
console.log('Shutting down...')
// Cleanup resources, close connections, etc.
}
}
})Custom Hooks in Application Code
While Nitro GraphQL doesn't provide custom hooks, you can use Nitro hooks in your application:
Server Middleware Hook
// server/middleware/graphql.ts
export default defineEventHandler((event) => {
// Runs before GraphQL handler
console.log('GraphQL request:', event.path)
})Request Lifecycle
// server/plugins/graphql-logging.ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('request', (event) => {
if (event.path.includes('/graphql')) {
console.log('GraphQL Request:', {
path: event.path,
method: event.method,
headers: event.headers
})
}
})
nitroApp.hooks.hook('afterResponse', (event) => {
if (event.path.includes('/graphql')) {
console.log('GraphQL Response sent')
}
})
})Hook Patterns
1. Pre-Process Hook Pattern
Execute logic before main operation:
export default defineNitroConfig({
hooks: {
'rollup:before': async (nitro, config) => {
// Validate GraphQL files before bundling
const schemas = await scanSchemas(nitro)
for (const schema of schemas) {
validateSchema(schema)
}
}
}
})2. Post-Process Hook Pattern
Execute logic after main operation:
export default defineNitroConfig({
hooks: {
'types:extend': (types) => {
// Post-process generated types
console.log('Generated paths:', types.tsConfig.compilerOptions.paths)
}
}
})3. Watch and Reload Pattern
React to file changes:
import { watch } from 'chokidar'
export default defineNitroConfig({
hooks: {
'dev:start': () => {
const watcher = watch('server/graphql/**/*.graphql')
watcher.on('change', async (path) => {
console.log('Schema changed:', path)
// Trigger type regeneration
})
}
}
})4. Conditional Hook Pattern
Execute based on conditions:
export default defineNitroConfig({
hooks: {
'build:before': () => {
if (process.env.NODE_ENV === 'production') {
// Production-only logic
console.log('Building for production')
}
}
}
})Nuxt-Specific Hooks
When using Nitro GraphQL with Nuxt, additional hooks are available:
prepare:types
Used by Nuxt module to extend Nuxt types:
// nuxt.config.ts
export default defineNuxtConfig({
hooks: {
'prepare:types': (options) => {
// Add custom type augmentations
options.references.push({
path: 'types/custom.d.ts'
})
}
}
})components:dirs
Register auto-imported components:
// nuxt.config.ts
export default defineNuxtConfig({
hooks: {
'components:dirs': (dirs) => {
// Add GraphQL component directory
dirs.push({
path: '~/components/graphql',
global: true
})
}
}
})Advanced Hook Examples
Schema Validation Hook
// nitro.config.ts
import { buildSchema } from 'graphql'
export default defineNitroConfig({
graphql: {
framework: 'graphql-yoga'
},
hooks: {
'dev:start': async () => {
// Validate all GraphQL schemas
const schemaFiles = await glob('server/graphql/**/*.graphql')
for (const file of schemaFiles) {
try {
const content = await readFile(file, 'utf-8')
buildSchema(content)
console.log(`✓ ${file}`)
}
catch (error) {
console.error(`✗ ${file}:`, error.message)
}
}
}
}
})Performance Monitoring Hook
// server/plugins/graphql-metrics.ts
export default defineNitroPlugin((nitroApp) => {
const metrics = new Map<string, number[]>()
nitroApp.hooks.hook('request', (event) => {
if (event.path.includes('/graphql')) {
event.context._graphqlStart = Date.now()
}
})
nitroApp.hooks.hook('afterResponse', (event) => {
if (event.context._graphqlStart) {
const duration = Date.now() - event.context._graphqlStart
const operation = event.context._graphqlOperation || 'unknown'
if (!metrics.has(operation)) {
metrics.set(operation, [])
}
metrics.get(operation)!.push(duration)
console.log(`GraphQL ${operation}: ${duration}ms`)
}
})
// Expose metrics endpoint
nitroApp.hooks.hook('close', () => {
console.log('GraphQL Metrics:')
for (const [operation, durations] of metrics) {
const avg = durations.reduce((a, b) => a + b, 0) / durations.length
console.log(` ${operation}: ${avg.toFixed(2)}ms avg`)
}
})
})Custom Type Generation Hook
// nitro.config.ts
import { writeFile } from 'node:fs/promises'
export default defineNitroConfig({
graphql: {
framework: 'graphql-yoga'
},
hooks: {
'types:extend': async (types) => {
// Generate custom helper types
const helpers = `
// Auto-generated helper types
import type { Resolvers } from '#graphql/server'
export type QueryKeys = keyof NonNullable<Resolvers['Query']>
export type MutationKeys = keyof NonNullable<Resolvers['Mutation']>
`
await writeFile('.nitro/types/graphql-helpers.d.ts', helpers)
types.tsConfig.include.push(
'.nitro/types/graphql-helpers.d.ts'
)
}
}
})Error Tracking Hook
// server/plugins/graphql-errors.ts
import * as Sentry from '@sentry/node'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('error', (error, { event }) => {
if (event.path.includes('/graphql')) {
Sentry.captureException(error, {
tags: {
type: 'graphql',
operation: event.context._graphqlOperation
},
extra: {
query: event.context._graphqlQuery,
variables: event.context._graphqlVariables
}
})
}
})
})Best Practices
1. Use Async Hooks When Needed
// ✅ Correct - async operation
hooks: {
'dev:start': async () => {
await validateSchemas()
}
}
// ❌ Wrong - missing await
hooks: {
'dev:start': () => {
validateSchemas() // Fire and forget
}
}2. Handle Hook Errors
hooks: {
'build:before': async () => {
try {
await prepareResources()
} catch (error) {
console.error('Failed to prepare resources:', error)
// Decide: throw to stop build, or continue
}
}
}3. Clean Up Resources
let watcher: FSWatcher
hooks: {
'dev:start': () => {
watcher = watch('**/*.graphql')
},
close: () => {
watcher?.close()
}
}4. Conditional Hook Execution
hooks: {
'dev:start': () => {
if (process.env.DEBUG_GRAPHQL) {
// Debug-only logic
enableGraphQLDebugger()
}
}
}5. Document Custom Hooks
/**
* Hook: dev:start
* Purpose: Validate GraphQL schemas on dev server start
* Why: Catch schema errors early in development
*/
hooks: {
'dev:start': async () => {
await validateAllSchemas()
}
}Troubleshooting
Hook Not Called
Problem: Custom hook doesn't execute
Solutions:
- Verify hook name is correct (check Nitro docs)
- Ensure hook is in correct config location
- Check for errors in hook function
- Try logging to confirm hook runs:
console.log('Hook called')
Async Hook Issues
Problem: Async operations not completing
Solutions:
- Use
async/awaitsyntax - Ensure promises are awaited
- Add error handling with try/catch
- Check Nitro logs for hook errors
Hook Order
Problem: Hooks executing in wrong order
Solutions:
- Review Nitro hook lifecycle
- Use appropriate hook for your needs
- Consider dependencies between hooks
- Use sequential hooks if order matters
Type Issues in Hooks
Problem: TypeScript errors in hook functions
Solutions:
- Import correct types from Nitro
- Use type assertions if needed
- Check Nitro version for breaking changes
- Consult Nitro TypeScript documentation