Skip to content

A standalone Nitro module that integrates GraphQL servers into any Nitro application with automatic type generation, file watching, and seamless framework integration.

License

Notifications You must be signed in to change notification settings

productdevbook/nitro-graphql

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Nitro GraphQL

npm version npm downloads bundle License

The easiest way to add GraphQL to any Nitro application

๐Ÿš€ Auto-discovery โ€ข ๐Ÿ“ Type Generation โ€ข ๐ŸŽฎ Apollo Sandbox โ€ข ๐Ÿ”ง Zero Config

Quick Start โ€ข Examples โ€ข Documentation โ€ข Community


๐ŸŽฅ Watch & Learn

โœจ Why Nitro GraphQL?

  • โšก 5-minute setup - From zero to GraphQL in minutes
  • ๐Ÿ” Auto-discovery - Scans your files, builds your schema
  • ๐Ÿ“ Type-safe - Full TypeScript support with auto-generated types
  • ๐ŸŽฏ Universal - Works with Nuxt, Nitro, and any Nitro-based framework
  • ๐ŸŽฎ Developer-friendly - Built-in Apollo Sandbox for testing
  • ๐Ÿ”ง Zero config - Sensible defaults, customize when needed

๐Ÿš€ Quick Start

1. Install

GraphQL Yoga (recommended):

pnpm add nitro-graphql graphql-yoga graphql

Apollo Server:

pnpm add nitro-graphql @apollo/server @apollo/utils.withrequired @as-integrations/h3 graphql

2. Configure

๐Ÿ”ง Nitro Project
// nitro.config.ts
import { defineNitroConfig } from 'nitropack/config'

export default defineNitroConfig({
  modules: ['nitro-graphql'],
  graphql: {
    framework: 'graphql-yoga', // or 'apollo-server'
  },
})
๐ŸŸข Nuxt Project
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nitro-graphql/nuxt'],
  nitro: {
    graphql: {
      framework: 'graphql-yoga',
    },
  },
})

3. Create Your Schema

# server/graphql/schema.graphql
type Query {
  hello: String!
  greeting(name: String!): String!
}

type Mutation {
  _empty: String
}

4. Add Resolvers

// server/graphql/hello.resolver.ts
export const helloResolver = defineResolver({
  Query: {
    hello: () => 'Hello from GraphQL!',
    greeting: (_, { name }) => `Hello, ${name}!`,
  },
})

5. Start Development

pnpm dev

๐ŸŽ‰ That's it! Your GraphQL server is ready at:

  • Endpoint: http://localhost:3000/api/graphql
  • Playground: http://localhost:3000/api/graphql (browser)
  • Health: http://localhost:3000/api/graphql/health

๐ŸŽฎ Examples

Try these working examples:

Example Description Demo
Nitro Basic Standalone Nitro with GraphQL pnpm playground:nitro
Nuxt Integration Full Nuxt app with client types pnpm playground:nuxt
Apollo Federation Federated GraphQL services pnpm playground:federation

๐Ÿ—๏ธ Building Your First Feature

Let's create a complete user management system:

1. Define 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!
}

2. Create Resolvers

// server/graphql/users/user.resolver.ts
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)
  }
})

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
  }
})

3. Test in Apollo Sandbox

mutation {
  createUser(input: {
    name: "John Doe"
    email: "john@example.com"
  }) {
    id
    name
    email
    createdAt
  }
}

query {
  users {
    id
    name
    email
  }
}

๐Ÿš€ Advanced Features

๐ŸŽญ Custom Directives

Create reusable GraphQL directives:

// server/graphql/directives/auth.directive.ts
export const authDirective = defineDirective({
  name: 'auth',
  locations: ['FIELD_DEFINITION'],
  args: {
    requires: { type: 'String', defaultValue: 'USER' }
  },
  transformer: (schema) => {
    // Add authentication logic
  }
})

Use in schema:

type Query {
  users: [User!]! @auth(requires: "ADMIN")
  profile: User! @auth
}
๐ŸŒ External GraphQL Services

Connect to multiple GraphQL APIs:

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    graphql: {
      framework: 'graphql-yoga',
      externalServices: [
        {
          name: 'github',
          schema: 'https://api.github.com/graphql',
          endpoint: 'https://api.github.com/graphql',
          headers: () => ({
            Authorization: `Bearer ${process.env.GITHUB_TOKEN}`
          })
        }
      ]
    }
  }
})
๐Ÿ”„ Apollo Federation

Build federated GraphQL services:

// nitro.config.ts
export default defineNitroConfig({
  graphql: {
    framework: 'apollo-server',
    federation: {
      enabled: true,
      serviceName: 'users-service'
    }
  }
})

๐Ÿ“– Documentation

Core Utilities

All utilities are auto-imported in resolver files:

Function Purpose Example
defineResolver Complete resolvers defineResolver({ Query: {...}, Mutation: {...} })
defineQuery Query-only resolvers defineQuery({ users: () => [...] })
defineMutation Mutation-only resolvers defineMutation({ createUser: (...) => {...} })
defineType Custom type resolvers defineType({ User: { posts: (parent) => [...] } })
defineDirective Custom directives defineDirective({ name: 'auth', ... })

Type Generation

Automatic TypeScript types are generated:

  • Server types: #graphql/server - Use in resolvers and server code
  • Client types: #graphql/client - Use in frontend components
// Server-side
import type { User, CreateUserInput } from '#graphql/server'

// Client-side  
import type { GetUsersQuery, CreateUserMutation } from '#graphql/client'

Project Structure

server/
โ”œโ”€โ”€ graphql/
โ”‚   โ”œโ”€โ”€ schema.graphql              # Main schema
โ”‚   โ”œโ”€โ”€ hello.resolver.ts           # Basic resolvers
โ”‚   โ”œโ”€โ”€ users/
โ”‚   โ”‚   โ”œโ”€โ”€ user.graphql           # User schema
โ”‚   โ”‚   โ””โ”€โ”€ user.resolver.ts       # User resolvers
โ”‚   โ”œโ”€โ”€ directives/                # Custom directives
โ”‚   โ””โ”€โ”€ config.ts                  # Optional GraphQL config

โš ๏ธ Important: Use named exports for all resolvers:

// โœ… Correct
export const userQueries = defineQuery({...})

// โŒ Deprecated
export default defineQuery({...})

๐Ÿšจ Troubleshooting

Common Issues

GraphQL endpoint returns 404

  • โœ… Check nitro-graphql is in modules
  • โœ… Set graphql.framework option
  • โœ… Create at least one .graphql file

Types not generating

  • โœ… Restart dev server
  • โœ… Check file naming: *.graphql, *.resolver.ts
  • โœ… Verify exports are named exports

Import errors

  • โœ… Use correct path: nitro-graphql/utils/define
  • โœ… Use named exports in resolvers

Vite: "Parse failure: Expected ';', '}' or " on GraphQL files

  • โœ… Add graphql() plugin from nitro-graphql/vite
  • โœ… Ensure graphql() is placed before nitro() in plugins array
  • โœ… Example:
    import { graphql } from 'nitro-graphql/vite'
    
    export default defineConfig({
      plugins: [
        graphql(), // โ† Must be first
        nitro(),
      ]
    })

RollupError: "[exportName]" is not exported by "[file].resolver.ts"

This error occurs when the resolver scanner can't find the expected export in your resolver file. Common causes:

  1. Using default export instead of named export โŒ

    // โŒ WRONG - Will not be detected
    export default defineQuery({
      users: () => [...]
    })
    // โœ… CORRECT - Use named export
    export const userQueries = defineQuery({
      users: () => [...]
    })
  2. Not using a define function โŒ

    // โŒ WRONG - Plain object won't be detected
    export const resolvers = {
      Query: {
        users: () => [...]
      }
    }
    // โœ… CORRECT - Use defineResolver, defineQuery, etc.
    export const userResolver = defineResolver({
      Query: {
        users: () => [...]
      }
    })
  3. File naming doesn't match export โŒ

    // โŒ File: uploadFile.resolver.ts but export is named differently
    export const fileUploader = defineMutation({...})
    // โœ… CORRECT - Export name can be anything, as long as it uses a define function
    export const uploadFile = defineMutation({...})
    export const fileUploader = defineMutation({...}) // Both work!
  4. Syntax errors preventing parsing

    • Check for TypeScript compilation errors in the file
    • Ensure imports are valid
    • Verify no missing brackets or syntax issues

How resolver scanning works:

  • The module uses oxc-parser to scan .resolver.ts files
  • It looks for named exports using these functions:
    • defineResolver - Complete resolver with Query, Mutation, etc.
    • defineQuery - Query-only resolvers
    • defineMutation - Mutation-only resolvers
    • defineType - Custom type resolvers
    • defineSubscription - Subscription resolvers
    • defineDirective - Directive resolvers
  • Only exports using these functions are included in the virtual module

Debugging steps:

  1. Check your resolver file uses named exports: export const name = defineQuery({...})
  2. Verify you're using one of the define functions listed above
  3. Look for TypeScript/syntax errors in the file
  4. Restart the dev server after fixing
  5. If issues persist, simplify the resolver to test (single query)

๐ŸŒŸ Production Usage

This package powers production applications:

  • Nitroping - Self-hosted push notification service

๐Ÿค– Using Claude Code

Speed up development with Claude Code โ€” AI-powered assistance for setting up and building with nitro-graphql.

Quick Setup Prompts

Copy and paste these prompts into Claude Code to scaffold a complete GraphQL API.

๐Ÿ’ก Tip: After pasting, Claude Code will execute step-by-step and validate each action.

๐ŸŸข Nuxt Project
## GOAL
Set up nitro-graphql in this Nuxt project with a User management GraphQL API.

## PREREQUISITES
Check if this is a Nuxt project by looking for nuxt.config.ts in the root.

## STEP 1: INSTALL DEPENDENCIES
Action: Run this command
Command: pnpm add nitro-graphql graphql-yoga graphql
Validation: Check package.json contains these packages

## STEP 2: CONFIGURE NUXT
File: nuxt.config.ts
Action: EDIT (add to existing config, don't replace)
Add these properties:

export default defineNuxtConfig({
  modules: ['nitro-graphql/nuxt'],  // Add this module
  nitro: {
    graphql: {
      framework: 'graphql-yoga',
    },
  },
})

Validation: Check the file has modules array and nitro.graphql config

## STEP 3: CREATE SCHEMA
File: server/graphql/schema.graphql
Action: CREATE NEW FILE (create server/graphql/ directory if needed)
Content:

type User {
  id: ID!
  name: String!
  email: String!
}

type Query {
  users: [User!]!
  user(id: ID!): User
}

type Mutation {
  _empty: String
}

Validation: File should be in server/graphql/ directory

## STEP 4: CREATE CONTEXT (Optional but recommended)
File: server/graphql/context.ts
Action: CREATE NEW FILE (auto-generated on first run, but create manually for clarity)
Content:

// Extend H3 event context with custom properties
declare module 'h3' {
  interface H3EventContext {
    // Add your custom context properties here
    // Example:
    // db?: Database
    // auth?: { userId: string }
  }
}

Note: This file lets you add custom properties to resolver context
Validation: File exists in server/graphql/

## STEP 5: CREATE CONFIG (Optional)
File: server/graphql/config.ts
Action: CREATE NEW FILE (auto-generated, customize if needed)
Content:

// Custom GraphQL Yoga configuration
export default defineGraphQLConfig({
  // Custom context enhancer, plugins, etc.
  // See: https://the-guild.dev/graphql/yoga-server/docs
})

Note: Use this to customize GraphQL Yoga options
Validation: File exists in server/graphql/

## STEP 6: CREATE RESOLVERS
File: server/graphql/users.resolver.ts
Action: CREATE NEW FILE
Content:

// โš ๏ธ CRITICAL: Use NAMED EXPORTS (not default export)
export const userQueries = defineQuery({
  users: async (_, __, context) => {
    // context is H3EventContext - access event, storage, etc.
    return [
      { id: '1', name: 'John Doe', email: 'john@example.com' },
      { id: '2', name: 'Jane Smith', email: 'jane@example.com' }
    ]
  },
  user: async (_, { id }, context) => {
    // Third parameter is context (H3EventContext)
    const users = [
      { id: '1', name: 'John Doe', email: 'john@example.com' },
      { id: '2', name: 'Jane Smith', email: 'jane@example.com' }
    ]
    return users.find(u => u.id === id) || null
  }
})

Validation: File ends with .resolver.ts and uses named export

## STEP 7: START DEV SERVER
Command: pnpm dev
Expected Output: Server starts on http://localhost:3000
Wait for: "Nitro built in X ms" message
Note: context.ts and config.ts will auto-generate if you skipped steps 4-5

## VALIDATION CHECKLIST
- [ ] Navigate to http://localhost:3000/api/graphql - should show GraphQL playground
- [ ] Health check: http://localhost:3000/api/graphql/health - should return OK
- [ ] Run this query in playground:
  ```graphql
  query {
    users {
      id
      name
      email
    }
  }

Expected: Returns 2 users

  • Check .nuxt/types/nitro-graphql-server.d.ts exists (types auto-generated)

FILE STRUCTURE CREATED

server/
  graphql/
    schema.graphql          โ† GraphQL type definitions
    context.ts              โ† H3 event context augmentation (optional)
    config.ts               โ† GraphQL Yoga config (optional)
    users.resolver.ts       โ† Query resolvers
.nuxt/
  types/
    nitro-graphql-server.d.ts  โ† Auto-generated types
graphql.config.ts           โ† Auto-generated (for IDE tooling)

CRITICAL RULES (MUST FOLLOW)

โŒ DO NOT use default exports in resolvers Wrong: export default defineQuery({...}) Right: export const userQueries = defineQuery({...})

โŒ DO NOT name files without .resolver.ts extension Wrong: users.ts or user-resolver.ts Right: users.resolver.ts or user.resolver.ts

โœ… DO use named exports for all resolvers โœ… DO place files in server/graphql/ directory โœ… DO restart dev server if types don't generate

TROUBLESHOOTING

Issue: "GraphQL endpoint returns 404" Fix: Ensure 'nitro-graphql/nuxt' is in modules array (not just 'nitro-graphql')

Issue: "defineQuery is not defined" Fix: Restart dev server - auto-imports need to regenerate

Issue: "Types not generating" Fix: Check .nuxt/types/nitro-graphql-server.d.ts exists, if not restart dev server

Issue: "Module not found: nitro-graphql" Fix: Run pnpm install again, check package.json has the package

NEXT STEPS (After Setup Works)

  1. Add mutations: "Add createUser and deleteUser mutations with H3 storage"
  2. Extend context: "Add database connection to context.ts and use it in resolvers"
  3. Use types: "Import and use TypeScript types from #graphql/server in resolvers"
  4. Add auth: "Add authentication middleware using context in resolvers"
  5. Custom config: "Configure GraphQL Yoga plugins in config.ts"

Now implement this setup step-by-step.


</details>

<details>
<summary>โšก <strong>Nitro Project</strong></summary>

Set up nitro-graphql in this Nitro project following these exact specifications:

INSTALLATION:

  1. Run: pnpm add nitro-graphql graphql-yoga graphql

CONFIGURATION (nitro.config.ts): import { defineNitroConfig } from 'nitro/config'

export default defineNitroConfig({ modules: ['nitro-graphql'], graphql: { framework: 'graphql-yoga', }, })

SCHEMA (server/graphql/schema.graphql): type Product { id: ID! name: String! price: Float! }

input CreateProductInput { name: String! price: Float! }

type Query { products: [Product!]! product(id: ID!): Product }

type Mutation { createProduct(input: CreateProductInput!): Product! }

RESOLVERS (server/graphql/products.resolver.ts): // Use NAMED EXPORTS only export const productQueries = defineQuery({ products: async (, __, context) => { // Access H3 event context const products = await context.storage?.getItem('products') || [] return products }, product: async (, { id }, context) => { const products = await context.storage?.getItem('products') || [] return products.find(p => p.id === id) } })

export const productMutations = defineMutation({ createProduct: async (_, { input }, context) => { const products = await context.storage?.getItem('products') || [] const product = { id: Date.now().toString(), ...input } products.push(product) await context.storage?.setItem('products', products) return product } })

KEY RULES:

  • Files: *.graphql for schemas, *.resolver.ts for resolvers
  • MUST use named exports (not default export)
  • defineQuery and defineMutation are auto-imported
  • Context is the third parameter (access H3 event context)
  • Endpoint: http://localhost:3000/api/graphql

Now implement this setup.


</details>

<details>
<summary>๐ŸŽฎ <strong>Apollo Server Setup</strong></summary>

Set up nitro-graphql with Apollo Server following these exact specifications:

INSTALLATION:

  1. Run: pnpm add nitro-graphql @apollo/server @apollo/utils.withrequired @as-integrations/h3 graphql

CONFIGURATION (nitro.config.ts): import { defineNitroConfig } from 'nitro/config'

export default defineNitroConfig({ modules: ['nitro-graphql'], graphql: { framework: 'apollo-server', }, })

SCHEMA (server/graphql/schema.graphql): type Book { id: ID! title: String! author: String! }

type Query { books: [Book!]! book(id: ID!): Book }

type Mutation { addBook(title: String!, author: String!): Book! }

RESOLVERS (server/graphql/books.resolver.ts): // IMPORTANT: Use NAMED EXPORTS export const bookResolver = defineResolver({ Query: { books: async () => { return [ { id: '1', title: '1984', author: 'George Orwell' } ] }, book: async (, { id }) => { return { id, title: '1984', author: 'George Orwell' } } }, Mutation: { addBook: async (, { title, author }) => { return { id: Date.now().toString(), title, author } } } })

KEY RULES:

  • framework: 'apollo-server' in config
  • defineResolver for complete resolver maps
  • Named exports required (export const name = ...)
  • Apollo Sandbox: http://localhost:3000/api/graphql
  • Supports Apollo Federation with federation: { enabled: true }

Now implement this setup.


</details>

<details>
<summary>๐Ÿ”„ <strong>Add Feature to Existing Setup</strong></summary>

Add a complete blog posts feature to my nitro-graphql API following these specifications:

SCHEMA (server/graphql/posts/post.graphql): type Post { id: ID! title: String! content: String! authorId: ID! createdAt: String! }

input CreatePostInput { title: String! content: String! authorId: ID! }

input UpdatePostInput { title: String content: String }

extend type Query { posts(limit: Int = 10, offset: Int = 0): [Post!]! post(id: ID!): Post }

extend type Mutation { createPost(input: CreatePostInput!): Post! updatePost(id: ID!, input: UpdatePostInput!): Post deletePost(id: ID!): Boolean! }

RESOLVERS (server/graphql/posts/post.resolver.ts): // Use NAMED EXPORTS export const postQueries = defineQuery({ posts: async (, { limit, offset }, context) => { const posts = await context.storage?.getItem('posts') || [] return posts.slice(offset, offset + limit) }, post: async (, { id }, context) => { const posts = await context.storage?.getItem('posts') || [] return posts.find(p => p.id === id) || null } })

export const postMutations = defineMutation({ createPost: async (, { input }, context) => { const posts = await context.storage?.getItem('posts') || [] const post = { id: Date.now().toString(), ...input, createdAt: new Date().toISOString() } posts.push(post) await context.storage?.setItem('posts', posts) return post }, updatePost: async (, { id, input }, context) => { const posts = await context.storage?.getItem('posts') || [] const index = posts.findIndex(p => p.id === id) if (index === -1) return null posts[index] = { ...posts[index], ...input } await context.storage?.setItem('posts', posts) return posts[index] }, deletePost: async (_, { id }, context) => { const posts = await context.storage?.getItem('posts') || [] const filtered = posts.filter(p => p.id !== id) await context.storage?.setItem('posts', filtered) return filtered.length < posts.length } })

TYPE USAGE: After dev server restarts, types are auto-generated in:

  • .nitro/types/nitro-graphql-server.d.ts (server types)
  • .nuxt/types/nitro-graphql-server.d.ts (for Nuxt)

Import types: import type { Post, CreatePostInput } from '#graphql/server'

KEY RULES:

  • Use "extend type" to add to existing Query/Mutation
  • Named exports required
  • Context has H3 event properties
  • Types auto-generate on file changes

Now implement this feature.


</details>

### Working with Your GraphQL API

Once set up, you can ask Claude Code for help with:

"Add authentication to my GraphQL resolvers" "Create a custom @auth directive for field-level permissions" "Set up type generation for client-side queries" "Add pagination to my users query" "Connect to an external GitHub GraphQL API" "Debug: my types aren't generating in .nitro/types/" "Optimize resolver performance using DataLoader"


### Tips for Better Results

- **Start specific**: Include your framework (Nuxt/Nitro), version, and goal
- **Reference docs**: Mention "following nitro-graphql conventions" to align with best practices
- **Show errors**: Paste error messages for faster debugging
- **Test iteratively**: Run `pnpm dev` after each change to verify

## ๐Ÿ› ๏ธ Development

```bash
# Install dependencies
pnpm install

# Build module
pnpm build

# Watch mode
pnpm dev

# Run playgrounds
pnpm playground:nitro
pnpm playground:nuxt
pnpm playground:federation

# Lint
pnpm lint

๐Ÿ’ฌ Community

[!TIP] Want to contribute? We believe you can play a role in the growth of this project!

Ways to Contribute

  • ๐Ÿ’ก Share ideas via GitHub Issues
  • ๐Ÿ› Report bugs with detailed information
  • ๐Ÿ“– Improve docs - README, examples, guides
  • ๐Ÿ”ง Code contributions - Bug fixes and features
  • ๐ŸŒŸ Star the project to show support

Help Wanted

  • Performance benchmarks
  • Video tutorials
  • Database adapter guides
  • VS Code extension

Sponsors

License

MIT License ยฉ 2023 productdevbook

About

A standalone Nitro module that integrates GraphQL servers into any Nitro application with automatic type generation, file watching, and seamless framework integration.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published