@stonecrop/nuxt-grafserv

Pluggable Grafserv GraphQL server as Nuxt Module
18,178 13
Install
npm install @stonecrop/nuxt-grafserv
Latest Version:0.55.0
Vue:3
License:MIT
Last Updated:Oct 22, 2026
Links: GitHub  ·  npm
Maintainer: knuckledown

Nuxt Grafserv

npm version npm downloads License

Pluggable Grafserv GraphQL server as a Nuxt Module. Uses the Grafast execution engine for high-performance GraphQL.

Features

  • 🚀  Grafserv Server Integration
  • ⚡️  Grafast Execution Engine (faster than graphql-js)
  • 🔄  Schema Stitching Support
  • 🔍  Graphile Preset System for Advanced Configuration
  • 📝  TypeScript Support
  • 🔍  GraphiQL/Ruru Interface
  • ⚡️  Hot Module Reloading
  • 🎯  Separate Route Handlers for GraphQL/UI and Static Assets

Architecture

This module uses modern Grafserv patterns with three key components:

  1. Preset-Based Configuration: Leverages Graphile's preset system for extensibility and plugin support
  2. Separate Route Handlers: Two dedicated handlers for GraphQL operations/UI and static assets
  3. Objects Structure: Uses Grafast's modern objects/interfaces/enums schema building pattern for better type safety

The module automatically registers these handlers:

  • {url} - Unified GraphQL operations and Ruru UI endpoint
  • /ruru-static/** - Static assets for the Ruru IDE

Quick Setup

PostGraphile Integration (Recommended)

For PostGraphile users, this is the recommended configuration approach:

  1. Add dependencies:
# Using pnpm
pnpm add @stonecrop/nuxt-grafserv

# Using yarn
yarn add @stonecrop/nuxt-grafserv

# Using npm
npm install @stonecrop/nuxt-grafserv
  1. Set DATABASE_URL in your environment (.env or shell):
DATABASE_URL=postgresql://localhost/mydb
  1. Configure in nuxt.config.ts:
export default defineNuxtConfig({
  modules: ['@stonecrop/nuxt-grafserv'],
  grafserv: {
    type: 'postgraphile', // Required: specify configuration type
    url: '/graphql',
    graphiql: true,
    // fieldCasing: 'pascal',        // optional — defaults to 'camel'
    // schemas: ['public', 'auth'],  // optional — defaults to ['public']
    // explain: true,                // optional — enables Ruru Explain tab (dev only)
  }
})

For custom plugins, a non-standard connection, or advanced configuration, create a preset file:

// server/graphile.preset.ts
import { createStonecropPreset, makePgService, createStonecropPlugin } from '@stonecrop/graphql-middleware'

export default {
  extends: [createStonecropPreset()],
  pgServices: [
    makePgService({
      connectionString: process.env.DATABASE_URL,
      schemas: ['public'],
    }),
  ],
  plugins: [createStonecropPlugin(), MyCustomPlugin],
}

Then reference it in nuxt.config.ts:

grafserv: {
  type: 'postgraphile',
  preset: './server/graphile.preset.ts',
}

Custom Schema Configuration

For custom GraphQL schemas with your own resolvers:

  1. Add dependency:
pnpm add @stonecrop/nuxt-grafserv
  1. Configure in nuxt.config.ts:
export default defineNuxtConfig({
  modules: ['@stonecrop/nuxt-grafserv'],
  grafserv: {
    type: 'schema', // Required: specify configuration type
    schema: 'server/**/*.graphql',
    resolvers: 'server/resolvers.ts',
    url: '/graphql',
    graphiql: true,
  }
})

Configuration

The module supports two configuration types using a discriminated union pattern:

PostGraphile Configuration

Use type: 'postgraphile' for PostGraphile-based GraphQL APIs:

Option Type Required Description
type 'postgraphile' ✅ Configuration type discriminator
preset string ❌ Path to preset file. When omitted, a default preset is synthesized from DATABASE_URL.
fieldCasing 'camel' | 'pascal' ❌ Column → field name casing (default: 'camel'). Ignored when preset is set.
schemas string[] ❌ PostgreSQL schemas to expose (default: ['public']). Ignored when preset is set.
explain boolean ❌ Enable Ruru Explain tab. Never use in production. Ignored when preset is set.
url string ❌ GraphQL endpoint URL (default: /graphql/)
graphiql boolean ❌ Serve the Ruru GraphiQL IDE and its static assets. Defaults to true in development and false in production.

Minimal example (no preset file):

// nuxt.config.ts — DATABASE_URL is read from process.env automatically
export default defineNuxtConfig({
  grafserv: {
    type: 'postgraphile',
    url: '/graphql/',
    graphiql: true,
  }
})

Explicit preset file (for custom plugins and advanced configuration):

// server/graphile.preset.ts
import { createStonecropPreset, makePgService, createStonecropPlugin } from '@stonecrop/graphql-middleware'

export default {
  extends: [createStonecropPreset()],
  pgServices: [
    makePgService({
      connectionString: process.env.DATABASE_URL,
      schemas: ['public'],
    }),
  ],
  plugins: [createStonecropPlugin(), MyCustomPlugin],
}

Schema Configuration

Use type: 'schema' for custom GraphQL schemas with Grafast resolvers:

Option Type Required Description
type 'schema' ✅ Configuration type discriminator
schema string | string[] | SchemaProvider ✅ Path(s) to .graphql files or schema provider function
resolvers string ❌ Path to resolvers file (required for .graphql files)
url string ❌ GraphQL endpoint URL (default: /graphql/)
graphiql boolean ❌ Serve the Ruru GraphiQL IDE and its static assets. Defaults to true in development and false in production.

Example with files:

export default defineNuxtConfig({
  grafserv: {
    type: 'schema',
    schema: 'server/**/*.graphql',
    resolvers: 'server/resolvers.ts',
    url: '/graphql/',
  }
})

Example with schema provider function:

export default defineNuxtConfig({
  grafserv: {
    type: 'schema',
    schema: async () => {
      // Return a GraphQLSchema instance
      return myCustomSchema
    },
  }
})

Configuration Comparison

Feature PostGraphile Config Schema Config
Type discriminator type: 'postgraphile' type: 'schema'
Schema source Generated from preset Files or function
Resolvers Auto-generated by PostGraphile Must provide via resolvers file
Primary use case PostgreSQL-backed APIs Custom GraphQL APIs
Setup complexity Minimal (DB connection only) Moderate (schema + resolvers)
Plugin system PostGraphile plugins Grafast standard steps

Basic Usage

Schema Configuration Example

For custom schemas with resolvers:

  1. Create your GraphQL schema (server/schema.graphql):
type Query {
  hello: String!
  ping: Boolean!
}

type Mutation {
  echo(message: String!): String!
}
  1. Create your resolvers (server/resolvers.ts):
import { constant, lambda, type GrafastSchemaConfig } from 'grafast'

const resolvers: GrafastSchemaConfig['objects'] = {
  Query: {
    plans: {
      hello: () => constant('world'),
      ping: () => constant(true)
    }
  },
  Mutation: {
    plans: {
      echo: (_source, fieldArgs) => {
        const { $message } = fieldArgs
        return $message
      }
    }
  }
}

export default resolvers
  1. Configure Nuxt:
export default defineNuxtConfig({
  modules: ['@stonecrop/nuxt-grafserv'],
  grafserv: {
    type: 'schema',
    schema: 'server/schema.graphql',
    resolvers: 'server/resolvers.ts',
  }
})

PostGraphile Configuration Example

For database-backed GraphQL APIs:

  1. Create your preset file server/graphile.preset.ts:
import { createStonecropPreset, makePgService } from '@stonecrop/graphql-middleware'

const preset = {
  extends: [createStonecropPreset()],
  pgServices: [
    makePgService({
      connectionString: process.env.DATABASE_URL,
      schemas: ['public'],
    }),
  ],
}

export default preset
  1. Configure PostGraphile in nuxt.config.ts:
export default defineNuxtConfig({
  modules: ['@stonecrop/nuxt-grafserv'],
  grafserv: {
    type: 'postgraphile',
    preset: './server/graphile.preset.ts',
  }
})
  1. That's it! PostGraphile automatically generates your GraphQL schema from your PostgreSQL database.

Advanced Usage

Custom Grafserv Plugins

Add custom plugins for authentication, logging, or rate limiting through your preset file:

// server/graphile/plugins.ts
import type { GraphileConfig } from 'graphile-config'

const loggingPlugin: GraphileConfig.Plugin = {
  name: 'request-logging',
  version: '1.0.0',
  grafserv: {
    middleware: {
      processGraphQLRequestBody: async (next, event) => {
        console.log('Processing:', event.request.url)
        return next()
      }
    }
  }
}

const authPlugin: GraphileConfig.Plugin = {
  name: 'authentication',
  version: '1.0.0',
  grafserv: {
    middleware: {
      processGraphQLRequestBody: async (next, event) => {
        const token = event.request.headers.get('authorization')
        // Validate token and add user to context
        if (!token) {
          throw new Error('Unauthorized')
        }
        return next()
      }
    }
  }
}

export default [loggingPlugin, authPlugin]

Import the plugins in your preset:

// server/graphile/graphile.preset.ts
import { createStonecropPreset, makePgService } from '@stonecrop/graphql-middleware'
import plugins from './plugins'

const preset = {
  extends: [createStonecropPreset()],
  pgServices: [makePgService({ /* ... */ })],
  plugins
}

export default preset
Available Middleware Hooks

These are hooks provided by the grafserv package itself via the Graphile preset system — they are not specific to @stonecrop/nuxt-grafserv. The hook used in the example above (processGraphQLRequestBody) is the most common entry point. For the full list of available hooks, refer to the Grafserv documentation.

PostGraphile Integration

PostGraphile v5+ automatically generates your GraphQL schema from PostgreSQL. When using a custom preset file (preset option), you have access to all PostGraphile preset keys — these are PostGraphile features, not Stonecrop-specific ones.

With Community Plugins

Third-party PostGraphile plugins can be added via the plugins array in a custom preset file:

// server/graphile.preset.ts
import { createStonecropPreset, makePgService, createStonecropPlugin } from '@stonecrop/graphql-middleware'
import PgSimplifyInflectorPlugin from '@graphile-contrib/pg-simplify-inflector'

export default {
  extends: [createStonecropPreset()],
  plugins: [createStonecropPlugin(), PgSimplifyInflectorPlugin],
  pgServices: [
    makePgService({
      connectionString: process.env.DATABASE_URL,
      schemas: ['public'],
    }),
  ],
}

For further PostGraphile preset configuration (behaviours, JWT, subscriptions, smart tags, etc.), refer to the PostGraphile v5 documentation.

Why File-Based Presets?

PostGraphile presets must be in separate files, not inline in nuxt.config.ts.

The module uses PostGraphile's instance-based approach:

  1. At build time, the preset file is imported and a PostGraphile instance is created
  2. The instance is exposed via Nitro's virtual module system
  3. At runtime, handlers call pgl.getSchema() to get the schema

This approach avoids GraphQL module duplication, follows PostGraphile's recommended pattern, supports watch mode, and properly handles database connections.

Your preset file needs a default export:

import { createStonecropPreset } from '@stonecrop/graphql-middleware'

const preset = {
  extends: [createStonecropPreset()],
  // ...
}

export default preset

Import Requirements:

Relative imports in preset files work with file extensions:

// server/graphile/graphile.preset.ts
import { createStonecropPreset, makePgService } from '@stonecrop/graphql-middleware'
import { MyPlugin } from './plugins/my-plugin'
import { AnotherPlugin } from './plugins/another'

const preset = {
  extends: [createStonecropPreset()],
  pgServices: [makePgService({/*...*/})],
  plugins: [MyPlugin, AnotherPlugin],
}

export default preset

Schema Building with Grafast

Grafast uses a step-based execution model. Common patterns:

import { constant, lambda, access, object, filter, type GrafastSchemaConfig } from 'grafast'

const resolvers: GrafastSchemaConfig['objects'] = {
  Query: {
    plans: {
      // Static values use constant()
      hello: () => constant('world'),

      // Arguments accessed via fieldArgs with $ prefix
      user: (_source, fieldArgs) => {
        const { $id } = fieldArgs
        return lambda($id, (id) => getUserById(id))
      },

      // Use filter() for list filtering
      userOrders: (_source, fieldArgs) => {
        const { $userId } = fieldArgs
        const $allOrders = constant(getAllOrders())
        return filter($allOrders, $order =>
          lambda([access($order, 'userId'), $userId],
            ([orderUserId, userId]) => orderUserId === userId
          )
        )
      }
    }
  },

  Mutation: {
    plans: {
      // Use object() to compose objects from steps
      createUser: (_source, fieldArgs) => {
        const { $name, $email } = fieldArgs
        const $id = constant(generateId())
        const $now = constant(new Date().toISOString())

        const $user = object({
          id: $id,
          name: $name,
          email: $email,
          role: constant('user'),
          createdAt: $now,
          updatedAt: $now
        })

        return lambda($user, user => {
          saveUser(user)
          return user
        })
      }
    }
  },

  // Field resolvers for types
  User: {
    plans: {
      fullName: ($user) => {
        return lambda($user, (user) => {
          const typed = user as { firstName: string; lastName: string }
          return `${typed.firstName} ${typed.lastName}`
        })
      }
    }
  },

  // Related type resolvers
  Order: {
    plans: {
      // Use access() to extract properties before lookups
      user: ($order) => {
        const $userId = access($order, 'userId')
        return lambda($userId, userId => getUserById(userId as string) ?? null)
      }
    }
  }
}

export default resolvers

Key Concepts:

  • Resolvers return steps (constant, lambda, access, object, filter), not plain values
  • Arguments are accessed via fieldArgs.$argumentName
  • constant() - static values
  • lambda() - transform step values at execution time
  • access() - extract object properties efficiently
  • object() - compose objects from steps
  • filter() - filter list steps

See Grafast Standard Steps for the complete reference.

Development

This package lives inside the Stonecrop pnpm workspace, so its scripts run with pnpm run from this directory.

# Install all monorepo dependencies (run from the repo root)
pnpm install

# Generate type stubs
pnpm run dev:prepare

# Develop with the playground (from nuxt_grafserv/).
# Starts + migrates the Postgres container (Docker required), then runs Nuxt.
pnpm run dev

# Build the module
pnpm run build:module

# Run ESLint
pnpm run lint

# Run tests
pnpm run test
pnpm run test:watch

The playground runs against a PostgreSQL container managed by Docker Compose. pnpm run dev starts the container and applies migrations automatically (via playground/scripts/bootstrap.mjs) before launching Nuxt, so Docker must be running; both steps are idempotent, so this is a fast no-op once the container exists (it persists via a named volume). To manage the database on its own, run pnpm db:up / pnpm db:down (or node scripts/bootstrap.mjs to recreate and migrate) from playground/.

Related Packages

@stonecrop/nuxt

Nuxt module for Stonecrop

21,261 13
nuxt-graphql-middleware

Module to perform GraphQL requests as a server middleware.

62,192 70
@privyid/nuxt-csrf

Module CSRF for NUXT

26,088 9
mixpanel-nuxt

Mixpanel module for Nuxt

19,992 7
nuxt-radash

radash for Nuxt

10,601 20

Version History

Version Released License
0.55.0 MIT
0.54.0 MIT
0.53.0 MIT
0.52.0 MIT
0.51.0 MIT