npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@stonecrop/nuxt-grafserv

v0.7.9

Published

Pluggable Grafserv GraphQL server as Nuxt Module

Readme

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 postgraphile

# Using yarn
yarn add @stonecrop/nuxt-grafserv postgraphile

# Using npm
npm install @stonecrop/nuxt-grafserv postgraphile
  1. Create your preset file server/graphile.preset.ts:
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
import { makePgService } from 'postgraphile/adaptors/pg'

const preset = {
  extends: [PostGraphileAmberPreset],
  pgServices: [
    makePgService({
      connectionString: process.env.DATABASE_URL || 'postgresql://localhost/mydb',
      schemas: ['public'],
    }),
  ],
}

export default preset
  1. Configure in nuxt.config.ts:
export default defineNuxtConfig({
  modules: ['@stonecrop/nuxt-grafserv'],
  grafserv: {
    type: 'postgraphile', // Required: specify configuration type
    preset: './server/graphile.preset.ts', // Path to preset file
    url: '/graphql',
    graphiql: true,
  }
})

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 PostGraphile preset file (e.g., './server/graphile.preset.ts') | | url | string | ❌ | GraphQL endpoint URL (default: '/graphql/') | | graphiql | boolean | ❌ | Enable GraphiQL IDE (default: true in dev, false in prod) |

Important: The preset must be a file path, not an inline object. See "Why File-Based Presets?" section below.

Example:

// nuxt.config.ts
export default defineNuxtConfig({
  grafserv: {
    type: 'postgraphile',
    preset: './server/graphile.preset.ts',
    url: '/graphql',
    graphiql: true,
  }
})
// server/graphile.preset.ts
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
import { makePgService } from 'postgraphile/adaptors/pg'

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

export default preset

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 | ❌ | Enable GraphiQL IDE (default: true in dev, false in prod) |

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 { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
import { makePgService } from 'postgraphile/adaptors/pg'

const preset = {
  extends: [PostGraphileAmberPreset],
  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 { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
import { makePgService } from 'postgraphile/adaptors/pg'
import plugins from './plugins'

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

export default preset

Available Middleware Hooks

  • processRequest - Process all incoming requests
  • processGraphQLRequestBody - Process GraphQL request bodies
  • ruruHTML - Customize Ruru IDE HTML generation
  • onSubscribe - Handle GraphQL subscriptions

PostGraphile Integration

PostGraphile v5+ automatically generates your GraphQL schema from PostgreSQL.

With Community Plugins

import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
import { makePgService } from 'postgraphile/adaptors/pg'
import PgSimplifyInflectorPlugin from '@graphile-contrib/pg-simplify-inflector'

const preset = {
  extends: [PostGraphileAmberPreset],
  plugins: [PgSimplifyInflectorPlugin],
  pgServices: [
    makePgService({
      connectionString: process.env.DATABASE_URL,
      schemas: ['public'],
    }),
  ],
  schema: {
    defaultBehavior: 'connection', // Enable Relay-style connections
  },
  grafast: {
    explain: process.env.NODE_ENV === 'development', // Plan diagrams in dev
  },
}

export default preset

Advanced Configuration

import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
import { makePgService } from 'postgraphile/adaptors/pg'

const preset = {
  extends: [PostGraphileAmberPreset],
  pgServices: [
    makePgService({
      connectionString: process.env.DATABASE_URL,
      schemas: ['public', 'app_private'],
      superuserConnectionString: process.env.SUPERUSER_DATABASE_URL, // For watch mode
      pubsub: true, // Enable LISTEN/NOTIFY for subscriptions
    }),
  ],
  gather: {
    // Smart tags for schema customization
    pgJwtTypes: 'app_public.jwt_token',
  },
  schema: {
    // Behavior overrides
    defaultBehavior: '-insert -update -delete', // Read-only by default
    pgJwtSecret: process.env.JWT_SECRET,
  },
  grafast: {
    explain: true,
    context: (requestContext) => ({
      // Custom context for all resolvers
      userId: requestContext.user?.id,
    }),
  },
}

export default preset

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 { PostGraphileAmberPreset } from 'postgraphile/presets/amber'

const preset = {
  extends: [PostGraphileAmberPreset],
  // ...
}

export default preset

Import Requirements:

Relative imports in preset files work with file extensions:

// server/graphile/graphile.preset.ts
import { PostGraphileAmberPreset } from 'postgraphile/presets/amber'
import { makePgService } from 'postgraphile/adaptors/pg'
import { MyPlugin } from './plugins/my-plugin'
import { AnotherPlugin } from './plugins/another'

const preset = {
  extends: [PostGraphileAmberPreset],
  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

# Install dependencies
pnpm install

# Generate type stubs
pnpm run dev:prepare

# Develop with the playground
pnpm run dev

# Build the module
pnpm run build

# Run ESLint
pnpm run lint

# Run tests
pnpm run test
pnpm run test:watch