A standalone Nitro module that integrates GraphQL servers into any Nitro application with automatic type generation, file watching, and seamless framework integration.
🚀 Quick Start • 📖 Documentation • 🎮 Playground • 💬 Community
"GraphQL is not just a query language; it's a new way to think about APIs and client-server interaction."
- 🚀 Multi-Framework Support: Works with GraphQL Yoga and Apollo Server
- 🔧 Auto-Discovery: Automatically scans and loads GraphQL schema and resolver files
- 📝 Type Generation: Automatic TypeScript type generation from GraphQL schemas (server & client)
- 🎮 Apollo Sandbox: Built-in GraphQL playground for development
- 🏥 Health Check: Built-in health check endpoint
- 🔌 Universal Compatibility: Works with any Nitro-based application (Nuxt, standalone Nitro, etc.)
- 🎯 Zero Configuration: Sensible defaults with optional customization
- 📂 File-Based Organization: Domain-driven resolver and schema organization
- 🔄 Hot Reload: Development mode with automatic schema and resolver updates
- 📦 Optimized Bundling: Smart chunking and dynamic imports for production
- 🌐 Nuxt Integration: First-class Nuxt.js support with dedicated module
- 🎭 Custom Directives: Create reusable GraphQL directives with automatic schema generation
- 🔗 Multi-Service Support: Connect to multiple external GraphQL APIs alongside your main server
Projects using Nitro GraphQL in production:
- Nitroping - Open-source, self-hosted push notification service
Learn how to use Nitro GraphQL with these video tutorials:
- Nuxt 4 Usage - How to integrate Nitro GraphQL with Nuxt 4
- Nitro Usage - How to use Nitro GraphQL with standalone Nitro
Choose your GraphQL framework and install required dependencies:
For GraphQL Yoga:
# npm
npm install nitro-graphql graphql-yoga graphql
# pnpm (recommended)
pnpm add nitro-graphql graphql-yoga graphql
# yarn
yarn add nitro-graphql graphql-yoga graphql
For Apollo Server:
# npm
npm install nitro-graphql @apollo/server @apollo/utils.withrequired @as-integrations/h3 graphql
# pnpm (recommended)
pnpm add nitro-graphql @apollo/server @apollo/utils.withrequired @as-integrations/h3 graphql
# yarn
yarn add nitro-graphql @apollo/server @apollo/utils.withrequired @as-integrations/h3 graphql
🔧 For Standalone Nitro Projects
- Update your
nitro.config.ts
:
import { defineNitroConfig } from 'nitropack/config'
export default defineNitroConfig({
modules: ['nitro-graphql'],
graphql: {
framework: 'graphql-yoga', // or 'apollo-server'
},
})
- Create the GraphQL directory structure:
mkdir -p server/graphql
🟢 For Nuxt.js Projects
- Update your
nuxt.config.ts
:
export default defineNuxtConfig({
modules: [
'nitro-graphql/nuxt',
],
nitro: {
modules: ['nitro-graphql'],
graphql: {
framework: 'graphql-yoga',
},
},
})
- Create the GraphQL directory structure:
mkdir -p server/graphql
Create your main schema file:
# server/graphql/schema.graphql
scalar DateTime
scalar JSON
type Query {
hello: String!
greeting(name: String!): String!
}
type Mutation {
_empty: String
}
Create a resolver for your queries:
// server/graphql/hello.resolver.ts
import { defineResolver } from 'nitro-graphql/utils/define'
export const helloResolver = defineResolver({
Query: {
hello: () => 'Hello from GraphQL!',
greeting: (_, { name }) => `Hello, ${name}!`,
},
})
// You can also export multiple resolvers from the same file
export const additionalResolver = defineResolver({
Query: {
// Additional query resolvers
},
})
# For Nitro
pnpm dev
# For Nuxt
pnpm dev
🎉 That's it! Your GraphQL server is now running at:
- GraphQL Endpoint:
http://localhost:3000/api/graphql
- Apollo Sandbox:
http://localhost:3000/api/graphql
(in browser) - Health Check:
http://localhost:3000/api/graphql/health
The module uses a domain-driven file structure under server/graphql/
:
server/
├── graphql/
│ ├── schema.graphql # Main schema with scalars and base types
│ ├── hello.resolver.ts # Global resolvers (use named exports)
│ ├── directives/ # Custom GraphQL directives
│ │ ├── auth.directive.ts # Authentication directive
│ │ ├── cache.directive.ts # Caching directive
│ │ └── validate.directive.ts # Validation directive
│ ├── users/
│ │ ├── user.graphql # User schema definitions
│ │ ├── user-queries.resolver.ts # User query resolvers (use named exports)
│ │ └── create-user.resolver.ts # User mutation resolvers (use named exports)
│ ├── posts/
│ │ ├── post.graphql # Post schema definitions
│ │ ├── post-queries.resolver.ts # Post query resolvers (use named exports)
│ │ └── create-post.resolver.ts # Post mutation resolvers (use named exports)
│ └── config.ts # Optional GraphQL configuration
│ └── schema.ts # Changing Special Return types
Tip
New Named Export Pattern: The module now supports named exports for GraphQL resolvers, allowing you to export multiple resolvers from a single file. This provides better organization and flexibility in structuring your resolver code.
Let's create a complete user management feature:
👤 Step 1: Define User Schema
# server/graphql/users/user.graphql
type User {
id: ID!
name: String!
email: String!
createdAt: DateTime!
}
input CreateUserInput {
name: String!
email: String!
}
extend type Query {
users: [User!]!
user(id: ID!): User
}
extend type Mutation {
createUser(input: CreateUserInput!): User!
}
🔍 Step 2: Create Query Resolvers
// server/graphql/users/user-queries.resolver.ts
import { defineQuery } from 'nitro-graphql/utils/define'
export const userQueries = defineQuery({
users: async (_, __, { storage }) => {
return await storage.getItem('users') || []
},
user: async (_, { id }, { storage }) => {
const users = await storage.getItem('users') || []
return users.find(user => user.id === id)
}
})
// You can also split queries into separate named exports
export const additionalUserQueries = defineQuery({
userCount: async (_, __, { storage }) => {
const users = await storage.getItem('users') || []
return users.length
},
})
✏️ Step 3: Create Mutation Resolvers
// server/graphql/users/create-user.resolver.ts
import { defineMutation } from 'nitro-graphql/utils/define'
export const createUserMutation = defineMutation({
createUser: async (_, { input }, { storage }) => {
const users = await storage.getItem('users') || []
const user = {
id: Date.now().toString(),
...input,
createdAt: new Date()
}
users.push(user)
await storage.setItem('users', users)
return user
}
})
// You can also export multiple mutations from the same file
export const updateUserMutation = defineMutation({
updateUser: async (_, { id, input }, { storage }) => {
const users = await storage.getItem('users') || []
const userIndex = users.findIndex(user => user.id === id)
if (userIndex === -1)
throw new Error('User not found')
users[userIndex] = { ...users[userIndex], ...input }
await storage.setItem('users', users)
return users[userIndex]
}
})
🧪 Step 4: Test Your Feature
Open Apollo Sandbox at http://localhost:3000/api/graphql
and try:
# Create a user
mutation {
createUser(input: {
name: "John Doe"
email: "[email protected]"
}) {
id
name
email
createdAt
}
}
# Query users
query {
users {
id
name
email
}
}
The module automatically generates TypeScript types for you:
- Server types:
.nitro/types/nitro-graphql-server.d.ts
- Client types:
.nitro/types/nitro-graphql-client.d.ts
- Auto-imports: Available for
defineResolver
and other utilities
Your IDE will automatically provide type safety and autocomplete!
You can import and use the generated types in your code:
Client-side types (#graphql/client
):
// Import generated types for queries, mutations, and operations
import type { GetUsersQuery, CreateUserInput } from '#graphql/client'
// Use in Vue components
const users = ref<GetUsersQuery['users']>([])
// Use in composables
export function useUsers() {
const createUser = async (input: CreateUserInput) => {
// Type-safe input
}
}
Server-side types (#graphql/server
):
// Import generated types and interfaces
import type { User, Post, CreateUserInput } from '#graphql/server'
// Use types in your server code
function validateUser(user: User): boolean {
return user.email.includes('@')
}
// Use in data layer
async function getUserPosts(user: User): Promise<Post[]> {
// user is fully typed with all fields
return await db.posts.findMany({ where: { authorId: user.id } })
}
// Use input types for validation
function validateCreateUserInput(input: CreateUserInput): void {
if (!input.email || !input.name) {
throw new Error('Email and name are required')
}
}
These imports provide full TypeScript support with autocompletion, type checking, and IntelliSense in your IDE.
Tip
Nitro Auto-Imports: Thanks to Nitro's auto-import feature, you don't need to manually import defineResolver
, defineQuery
, defineMutation
, and other utilities in your resolver files. They're available globally! However, if you prefer explicit imports, you can use:
import { defineResolver } from 'nitro-graphql/utils/define'
⚙️ Runtime Configuration
// nitro.config.ts
export default defineNitroConfig({
modules: ['nitro-graphql'],
graphql: {
framework: 'graphql-yoga', // or 'apollo-server'
},
runtimeConfig: {
graphql: {
endpoint: {
graphql: '/api/graphql', // GraphQL endpoint
healthCheck: '/api/graphql/health' // Health check endpoint
},
playground: true, // Enable Apollo Sandbox
}
}
})
🔧 Advanced Configuration
// server/graphql/config.ts
import { defineGraphQLConfig } from 'nitro-graphql/utils/define'
export default defineGraphQLConfig({
// Custom GraphQL Yoga or Apollo Server configuration
plugins: [
// Add custom plugins
],
context: async ({ request }) => {
// Enhanced context with custom properties
return {
user: await authenticateUser(request),
db: await connectDatabase(),
}
},
})
Try out the examples:
- Standalone Nitro:
playground/
- Nuxt.js Integration:
playground-nuxt/
Both examples include working GraphQL schemas, resolvers, and demonstrate the module's capabilities.
Note
Auto-Import Available: All utilities are automatically imported in your resolver files thanks to Nitro's auto-import feature. You can use them directly without import statements, or use explicit imports if you prefer:
import { defineMutation, defineQuery, defineResolver } from 'nitro-graphql/utils/define'
Important
Named Exports Required: All GraphQL resolvers must now use named exports instead of default exports. This allows you to export multiple resolvers from a single file, providing better organization and flexibility. For example:
// ✅ Correct - Named exports
export const userQueries = defineQuery({ ... })
export const userMutations = defineMutation({ ... })
// ❌ Incorrect - Default exports (deprecated)
export default defineQuery({ ... })
defineResolver - Define complete resolvers
import { defineResolver } from 'nitro-graphql/utils/define'
export const mainResolver = defineResolver({
Query: {
// Query resolvers
},
Mutation: {
// Mutation resolvers
},
// Custom type resolvers
})
// You can also export multiple resolvers from the same file
export const additionalResolver = defineResolver({
Query: {
// Additional query resolvers
},
})
defineQuery - Define only Query resolvers
import { defineQuery } from 'nitro-graphql/utils/define'
export const userQueries = defineQuery({
users: async (_, __, { storage }) => {
return await storage.getItem('users') || []
},
user: async (_, { id }, { storage }) => {
const users = await storage.getItem('users') || []
return users.find(user => user.id === id)
}
})
// You can also split queries into separate named exports
export const userStatsQueries = defineQuery({
userCount: async (_, __, { storage }) => {
const users = await storage.getItem('users') || []
return users.length
},
})
defineMutation - Define only Mutation resolvers
import { defineMutation } from 'nitro-graphql/utils/define'
export const userMutations = defineMutation({
createUser: async (_, { input }, { storage }) => {
const users = await storage.getItem('users') || []
const user = {
id: Date.now().toString(),
...input,
createdAt: new Date()
}
users.push(user)
await storage.setItem('users', users)
return user
}
})
// You can also export multiple mutations from the same file
export const userUpdateMutations = defineMutation({
updateUser: async (_, { id, input }, { storage }) => {
const users = await storage.getItem('users') || []
const userIndex = users.findIndex(user => user.id === id)
if (userIndex === -1)
throw new Error('User not found')
users[userIndex] = { ...users[userIndex], ...input }
await storage.setItem('users', users)
return users[userIndex]
}
})
defineSubscription - Define Subscription resolvers
import { defineSubscription } from 'nitro-graphql/utils/define'
export const userSubscriptions = defineSubscription({
userAdded: {
subscribe: () => pubsub.asyncIterator('USER_ADDED'),
},
postUpdated: {
subscribe: withFilter(
() => pubsub.asyncIterator('POST_UPDATED'),
(payload, variables) => payload.postUpdated.id === variables.postId
),
}
})
// You can also export multiple subscriptions from the same file
export const notificationSubscriptions = defineSubscription({
notificationAdded: {
subscribe: () => pubsub.asyncIterator('NOTIFICATION_ADDED'),
},
})
defineType - Define custom type resolvers
import { defineType } from 'nitro-graphql/utils/define'
export const userTypes = defineType({
User: {
posts: async (parent, _, { storage }) => {
const posts = await storage.getItem('posts') || []
return posts.filter(post => post.authorId === parent.id)
},
fullName: parent => `${parent.firstName} ${parent.lastName}`,
},
})
// You can also export multiple type resolvers from the same file
export const postTypes = defineType({
Post: {
author: async (parent, _, { storage }) => {
const users = await storage.getItem('users') || []
return users.find(user => user.id === parent.authorId)
},
},
})
defineDirective - Create custom GraphQL directives
import { defineDirective } from 'nitro-graphql/utils/define'
import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils'
import { defaultFieldResolver, GraphQLError } from 'graphql'
export const authDirective = defineDirective({
name: 'auth',
locations: ['FIELD_DEFINITION', 'OBJECT'],
args: {
requires: {
type: 'String',
defaultValue: 'USER',
description: 'Required role to access this field',
},
},
description: 'Directive to check authentication and authorization',
transformer: (schema) => {
return mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
const authDirectiveConfig = getDirective(schema, fieldConfig, 'auth')?.[0]
if (authDirectiveConfig) {
const { resolve = defaultFieldResolver } = fieldConfig
fieldConfig.resolve = async function (source, args, context, info) {
if (!context.user) {
throw new GraphQLError('You must be logged in')
}
if (context.user.role !== authDirectiveConfig.requires) {
throw new GraphQLError('Insufficient permissions')
}
return resolve(source, args, context, info)
}
}
return fieldConfig
},
})
},
})
Usage in Schema:
type User {
id: ID!
name: String!
email: String! @auth(requires: "ADMIN")
secretData: String @auth(requires: "SUPER_ADMIN")
}
type Query {
users: [User!]! @auth
adminStats: AdminStats @auth(requires: "ADMIN")
}
Available Argument Types:
- Basic scalars:
String
,Int
,Float
,Boolean
,ID
,JSON
,DateTime
- Non-nullable:
String!
,Int!
,Float!
,Boolean!
,ID!
,JSON!
,DateTime!
- Arrays:
[String]
,[String!]
,[String]!
,[String!]!
(and all combinations for other types) - Custom types: Any string for your custom GraphQL types
Helper Function:
export const validateDirective = defineDirective({
name: 'validate',
locations: ['FIELD_DEFINITION', 'ARGUMENT_DEFINITION'],
args: {
minLength: arg('Int', { description: 'Minimum length' }),
maxLength: arg('Int', { description: 'Maximum length' }),
pattern: arg('String', { description: 'Regex pattern' }),
},
// ... transformer implementation
})
defineSchema - Define custom schema with validation
You can override schema types if needed. StandardSchema supported — Zod, Valibot, anything works:
# server/graphql/schema.ts
import { defineSchema } from 'nitro-graphql/utils/define'
import { z } from 'zod'
export default defineSchema({
Todo: z.object({
id: z.string(),
title: z.string(),
completed: z.boolean(),
createdAt: z.date(),
}),
User: z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
age: z.number().min(0),
}),
})
With Drizzle Schema:
import { defineSchema } from 'nitro-graphql/utils/define'
import { z } from 'zod'
import { userSchema } from './drizzle/user'
export default defineSchema({
Todo: z.object({
id: z.string(),
title: z.string(),
}),
User: userSchema, // Import from Drizzle schema
})
Common Issues
Solution: Make sure you have:
- Added
nitro-graphql
to your modules - Set the
graphql.framework
option - Created at least one schema file
Solution:
- Restart your dev server
- Check that your schema files end with
.graphql
- Verify your resolver files end with
.resolver.ts
Solution:
- Make sure you're in development mode
- Check file naming conventions
- Restart the dev server
Solution:
// ❌ Incorrect import path
import { defineResolver } from 'nitro-graphql'
// ✅ Correct import path
import { defineResolver } from 'nitro-graphql/utils/define'
Solution:
// ❌ Incorrect - Default exports (deprecated)
export default defineResolver({ ... })
// ✅ Correct - Named exports
export const myResolver = defineResolver({ ... })
export const anotherResolver = defineResolver({ ... })
GraphQL Yoga
// nitro.config.ts
export default defineNitroConfig({
graphql: {
framework: 'graphql-yoga',
},
})
Apollo Server
// nitro.config.ts
export default defineNitroConfig({
graphql: {
framework: 'apollo-server',
},
})
Custom Directives
Create reusable GraphQL directives with automatic schema generation:
// server/graphql/directives/auth.directive.ts
import { defineDirective } from 'nitro-graphql/utils/define'
import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils'
export const authDirective = defineDirective({
name: 'auth',
locations: ['FIELD_DEFINITION', 'OBJECT'],
args: {
requires: {
type: 'String',
defaultValue: 'USER',
description: 'Required role to access this field',
},
},
description: 'Authentication and authorization directive',
transformer: (schema) => {
return mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
const authConfig = getDirective(schema, fieldConfig, 'auth')?.[0]
if (authConfig) {
// Transform field resolvers to check authentication
const { resolve = defaultFieldResolver } = fieldConfig
fieldConfig.resolve = async (source, args, context, info) => {
if (!context.user || context.user.role !== authConfig.requires) {
throw new GraphQLError('Access denied')
}
return resolve(source, args, context, info)
}
}
return fieldConfig
},
})
},
})
Common Directive Examples:
@auth(requires: "ADMIN")
- Role-based authentication@cache(ttl: 300, scope: "PUBLIC")
- Field-level caching@rateLimit(limit: 10, window: 60)
- Rate limiting@validate(minLength: 5, maxLength: 100)
- Input validation@transform(upper: true, trim: true)
- Data transformation@permission(roles: ["ADMIN", "MODERATOR"])
- Multi-role permissions
Usage in Schema:
type User {
id: ID!
name: String!
email: String! @auth(requires: "ADMIN")
posts: [Post!]! @cache(ttl: 300)
}
type Query {
users: [User!]! @rateLimit(limit: 100, window: 3600)
sensitiveData: String @auth(requires: "SUPER_ADMIN")
}
The module automatically generates the directive schema definitions and integrates them with both GraphQL Yoga and Apollo Server.
Custom Scalars
// server/graphql/scalars/DateTime.resolver.ts
import { GraphQLScalarType } from 'graphql'
import { Kind } from 'graphql/language'
import { defineResolver } from 'nitro-graphql/utils/define'
export const dateTimeScalar = defineResolver({
DateTime: new GraphQLScalarType({
name: 'DateTime',
serialize: (value: Date) => value.toISOString(),
parseValue: (value: string) => new Date(value),
parseLiteral: (ast) => {
if (ast.kind === Kind.STRING) {
return new Date(ast.value)
}
return null
}
})
})
// You can also export multiple scalars from the same file
export const jsonScalar = defineResolver({
JSON: new GraphQLScalarType({
name: 'JSON',
serialize: value => value,
parseValue: value => value,
parseLiteral: (ast) => {
if (ast.kind === Kind.STRING) {
return JSON.parse(ast.value)
}
return null
}
})
})
Error Handling
// server/graphql/users/user-queries.resolver.ts
import { defineQuery } from 'nitro-graphql/utils/define'
export const userQueries = defineQuery({
user: async (_, { id }, { storage }) => {
try {
const user = await storage.getItem(`user:${id}`)
if (!user) {
throw new Error(`User with id ${id} not found`)
}
return user
}
catch (error) {
console.error('Error fetching user:', error)
throw error
}
}
})
// You can also export additional error handling queries
export const safeUserQueries = defineQuery({
userSafe: async (_, { id }, { storage }) => {
try {
const user = await storage.getItem(`user:${id}`)
return user || null // Return null instead of throwing
}
catch (error) {
console.error('Error fetching user:', error)
return null
}
}
})
Nuxt Integration
For Nuxt.js applications, the module provides enhanced integration:
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'nitro-graphql/nuxt',
],
nitro: {
modules: ['nitro-graphql'],
graphql: {
framework: 'graphql-yoga',
},
},
})
Client-side GraphQL files are automatically detected in the app/graphql/
directory.
The module automatically generates a GraphQL SDK and provides type-safe client access for frontend usage.
📁 GraphQL File Structure
Create your GraphQL queries and mutations in the app/graphql/
directory:
app/
├── graphql/
│ ├── queries.graphql # GraphQL queries
│ ├── mutations.graphql # GraphQL mutations
│ └── subscriptions.graphql # GraphQL subscriptions (optional)
🔥 Creating GraphQL Files
Query File Example:
# app/graphql/queries.graphql
query GetUsers {
users {
id
name
email
createdAt
}
}
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
createdAt
}
}
Mutation File Example:
# app/graphql/mutations.graphql
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
name
email
createdAt
}
}
mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {
updateUser(id: $id, input: $input) {
id
name
email
createdAt
}
}
⚡ Using the Generated SDK
The module automatically generates a type-safe SDK based on your GraphQL files:
// The SDK is automatically generated and available as an import
import { createGraphQLClient } from '#graphql/client'
// Create a client instance
const client = createGraphQLClient({
endpoint: '/api/graphql',
headers: {
Authorization: 'Bearer your-token-here'
}
})
// Use the generated methods with full type safety
const getUsersData = await client.GetUsers()
console.log(getUsersData.users) // Fully typed response
const newUser = await client.CreateUser({
input: {
name: 'John Doe',
email: '[email protected]'
}
})
console.log(newUser.createUser) // Fully typed response
🎯 Basic Usage Examples
Fetching Data:
// Import the generated client
import { createGraphQLClient } from '#graphql/client'
const client = createGraphQLClient()
// Query users
const { users } = await client.GetUsers()
console.log(users) // Array of User objects with full typing
// Query specific user
const { user } = await client.GetUser({ id: '123' })
console.log(user) // User object or null
Creating Data:
// Create a new user
const { createUser } = await client.CreateUser({
input: {
name: 'Jane Doe',
email: '[email protected]'
}
})
console.log(createUser) // Newly created user with full typing
Error Handling:
try {
const { users } = await client.GetUsers()
console.log(users)
}
catch (error) {
console.error('GraphQL Error:', error)
// Handle GraphQL errors appropriately
}
🔧 Client Configuration
import { createGraphQLClient } from '#graphql/client'
// Basic configuration
const client = createGraphQLClient({
endpoint: '/api/graphql',
headers: {
'Authorization': 'Bearer your-token',
'X-Client-Version': '1.0.0'
},
timeout: 10000
})
// Advanced configuration with dynamic headers
const client = createGraphQLClient({
endpoint: '/api/graphql',
headers: async () => {
const token = await getAuthToken()
return {
'Authorization': token ? `Bearer ${token}` : '',
'X-Request-ID': crypto.randomUUID()
}
},
retry: 3,
timeout: 30000
})
pnpm build
- Build the modulepnpm dev
- Watch mode with automatic rebuildingpnpm lint
- ESLint with auto-fixpnpm playground
- Run the Nitro playground examplepnpm release
- Build, version bump, and publish
- Node.js 20.x or later
- pnpm (required package manager)
Tip
Want to contribute? We believe you can play a role in the growth of this project!
- 💡 Share your ideas: Use GitHub Issues for new feature suggestions
- 🐛 Report bugs: Report issues you encounter in detail
- 📖 Improve documentation: Enhance README, examples, and guides
- 🔧 Code contributions: Develop bug fixes and new features
- 🌟 Support the project: Support the project by giving it a star
- GitHub Issues: Feature requests and bug reports
- GitHub Discussions: General discussions and questions
- Pull Requests: Code contributions
- Open an issue: Let's discuss what you want to do first
- Fork & Branch: Fork the project and create a feature branch
- Write code: Develop according to existing code standards
- Test: Test your changes
- Send PR: Create a pull request with detailed description
Important
Please don't forget to read the Contribution Guidelines document before contributing.
Help us improve nitro-graphql! Pick any item and contribute:
- Nitro-compatible framework integrations
- Nuxt + Pinia Colada example
- StackBlitz playground demos
- Performance benchmarks
- Bundle size optimization
- Testing utilities
- Error handling patterns
- Video tutorials
- Migration guides
- Best practices guide
- VS Code extension
- CLI tools
- Debug utilities
- Database adapters (Prisma, Drizzle)
- Cache strategies
- Deployment guides
Note
Have other ideas? Open an issue to discuss!
Connect to multiple external GraphQL APIs alongside your main GraphQL server. Perfect for integrating with services like GitHub API, Shopify API, or any GraphQL endpoint.
// nuxt.config.ts (for Nuxt projects)
export default defineNuxtConfig({
nitro: {
graphql: {
framework: 'graphql-yoga',
externalServices: [
{
name: 'countries',
schema: 'https://countries.trevorblades.com',
endpoint: 'https://countries.trevorblades.com',
documents: ['app/graphql/external/countries/**/*.graphql'],
headers: {
// Optional: Add custom headers
'Authorization': 'Bearer your-token'
}
},
{
name: 'github',
schema: 'https://api.github.com/graphql',
endpoint: 'https://api.github.com/graphql',
documents: ['app/graphql/external/github/**/*.graphql'],
headers: () => ({
// Dynamic headers with function
'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`
})
}
]
}
}
})
For better performance and offline development, you can download and cache external schemas locally:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
graphql: {
framework: 'graphql-yoga',
externalServices: [
{
name: 'github',
schema: 'https://docs.github.com/public/schema.docs.graphql',
endpoint: 'https://api.github.com/graphql',
downloadSchema: 'once', // Download mode (see options below)
downloadPath: './schemas/github.graphql', // Optional: custom download path
headers: () => ({
'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`
})
}
]
}
}
})
Download Modes:
Mode | Behavior | Use Case |
---|---|---|
true or 'once' |
Download only if file doesn't exist | Offline-friendly development |
'always' |
Check for updates on every build | Always stay up-to-date |
'manual' |
Never download automatically | Full manual control |
false |
Disable schema downloading | Always use remote |
Benefits:
- Offline Development: Work without internet connection after initial download
- Faster Builds: No remote fetching on each build when using 'once' mode
- Version Control: Commit downloaded schemas to track API changes
- Network Reliability: Fallback to cached schema if remote is unavailable
How it works:
- 'once' mode (recommended): Downloads schema only if file doesn't exist, then uses cached version
- 'always' mode: Checks for schema changes on every build using hash comparison
- 'manual' mode: User manages schema files manually, no automatic downloading
File locations:
- Default:
.nitro/graphql/schemas/[serviceName].graphql
- Custom: Use
downloadPath
option to specify your preferred location
<!-- app/graphql/external/countries/countries.graphql -->
query GetCountries {
countries {
code
name
emoji
continent {
name
}
}
}
query GetCountry($code: ID!) {
country(code: $code) {
code
name
capital
currency
}
}
// Import from centralized index
import { $sdk, $countriesSdk, $githubSdk } from '~/app/graphql'
// Or import directly from service folders
import { $countriesSdk } from '~/app/graphql/countries/ofetch'
// Use in components
const countries = await $countriesSdk.GetCountries()
const country = await $countriesSdk.GetCountry({ code: 'US' })
// Your main service still works
const users = await $sdk.GetUsers()
After configuration, your project structure becomes:
app/graphql/
├── index.ts # Centralized exports (auto-generated)
├── default/ # Your main GraphQL service
│ ├── ofetch.ts # Main service client
│ └── sdk.ts # Main service SDK
├── countries/ # External countries service
│ ├── ofetch.ts # Countries service client
│ └── sdk.ts # Countries service SDK
├── github/ # External GitHub service
│ ├── ofetch.ts # GitHub service client
│ └── sdk.ts # GitHub service SDK
└── external/ # Your external service queries
├── countries/
│ └── countries.graphql
└── github/
└── repositories.graphql
Each service gets its own type definitions:
// Types are automatically generated and available
import type { GetCountriesQuery } from '#graphql/client/countries'
import type { GetUsersQuery } from '#graphql/client'
const handleCountries = (countries: GetCountriesQuery) => {
// Fully typed countries data
}
Option | Type | Required | Description |
---|---|---|---|
name |
string |
✅ | Unique service name (used for folder/file names) |
schema |
string | string[] |
✅ | GraphQL schema URL or file path |
endpoint |
string |
✅ | GraphQL endpoint URL for queries |
documents |
string[] |
❌ | Glob patterns for GraphQL query files |
headers |
Record<string, string> | () => Record<string, string> |
❌ | Custom headers for schema introspection and queries |
codegen.client |
CodegenClientConfig |
❌ | Custom codegen configuration for client types |
codegen.clientSDK |
GenericSdkConfig |
❌ | Custom codegen configuration for SDK generation |
To enable GraphQL language features in your IDE (autocompletion, validation, go-to definition), create a graphql.config.ts
file in your project root:
// graphql.config.ts
import type { IGraphQLConfig } from 'graphql-config'
export default <IGraphQLConfig> {
schema: ['./.nuxt/graphql/schema.graphql'],
documents: ['./app/graphql/**/*.{graphql,js,ts,jsx,tsx}'],
exclude: ['./app/graphql/external/**/*'] // Exclude external service documents
}
// graphql.config.ts
import type { IGraphQLConfig } from 'graphql-config'
export default <IGraphQLConfig> {
projects: {
// Main GraphQL server
default: {
schema: ['./.nuxt/graphql/schema.graphql'],
documents: ['./app/graphql/default/**/*.{graphql,js,ts,jsx,tsx}']
},
// External services
github: {
schema: [
// Use downloaded schema if available, otherwise use remote
'./.nuxt/graphql/schemas/github.graphql',
// Fallback to remote if local doesn't exist
'https://docs.github.com/public/schema.docs.graphql'
],
documents: ['./app/graphql/external/github/**/*.graphql']
},
countries: {
schema: ['./.nuxt/graphql/schemas/countries.graphql'],
documents: ['./app/graphql/external/countries/**/*.graphql']
}
}
}
- Downloaded schemas:
./.nuxt/graphql/schemas/[serviceName].graphql
- Custom download path: Use your
downloadPath
configuration - Remote fallback: Include remote URL as second option
This configuration enables:
- 🎯 Service-specific validation: Each GraphQL service gets its own validation rules
- 🚀 IDE autocompletion: Full IntelliSense for queries and mutations
- ✅ Real-time validation: Catch GraphQL errors while typing
- 🔍 Go-to definition: Navigate to type definitions across services
For the best development experience with GraphQL, install these recommended VS Code extensions:
- GraphQL: Language Feature Support - Provides GraphQL language features like autocompletion, go-to definition, and schema validation
- GraphQL: Syntax Highlighting - Adds syntax highlighting for GraphQL queries, mutations, subscriptions, and schema files
These extensions will enable:
- 🎨 Syntax highlighting for
.graphql
files - 📝 IntelliSense and autocompletion based on your schema
- ✅ Real-time validation of GraphQL queries
- 🔍 Go-to definition for types and fields
- 💡 Hover information for GraphQL elements
Thank you for using and developing this project. Every contribution makes the GraphQL ecosystem stronger!
MIT License © 2023 productdevbook