@stonecrop/nuxt-grafserv
| Install | |
|---|---|
npm install @stonecrop/nuxt-grafserv |
|
| Latest Version: | 0.55.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 22, 2026 |
| Links: | GitHub · npm |
Nuxt Grafserv
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:
- Preset-Based Configuration: Leverages Graphile's preset system for extensibility and plugin support
- Separate Route Handlers: Two dedicated handlers for GraphQL operations/UI and static assets
- Objects Structure: Uses Grafast's modern
objects/interfaces/enumsschema 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:
- Add dependencies:
# Using pnpm
pnpm add @stonecrop/nuxt-grafserv
# Using yarn
yarn add @stonecrop/nuxt-grafserv
# Using npm
npm install @stonecrop/nuxt-grafserv
- Set
DATABASE_URLin your environment (.envor shell):
DATABASE_URL=postgresql://localhost/mydb
- 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:
- Add dependency:
pnpm add @stonecrop/nuxt-grafserv
- 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:
- Create your GraphQL schema (
server/schema.graphql):
type Query {
hello: String!
ping: Boolean!
}
type Mutation {
echo(message: String!): String!
}
- 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
- 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:
- 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
- Configure PostGraphile in
nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@stonecrop/nuxt-grafserv'],
grafserv: {
type: 'postgraphile',
preset: './server/graphile.preset.ts',
}
})
- 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:
- At build time, the preset file is imported and a PostGraphile instance is created
- The instance is exposed via Nitro's virtual module system
- 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 valueslambda()- transform step values at execution timeaccess()- extract object properties efficientlyobject()- compose objects from stepsfilter()- 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
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 | |
| 0.50.1 | MIT | |
| 0.50.0 | MIT | |
| 0.49.0 | MIT | |
| 0.48.1 | MIT | |
| 0.48.0 | MIT | |
| 0.47.0 | MIT | |
| 0.46.1 | MIT | |
| 0.46.0 | MIT | |
| 0.45.1 | MIT | |
| 0.45.0 | MIT | |
| 0.44.0 | MIT | |
| 0.43.0 | MIT | |
| 0.42.0 | MIT | |
| 0.41.1 | MIT | |
| 0.41.0 | MIT | |
| 0.40.0 | MIT | |
| 0.39.0 | MIT | |
| 0.38.1 | MIT | |
| 0.38.0 | MIT | |
| 0.36.0 | MIT | |
| 0.35.0 | MIT | |
| 0.34.0 | MIT | |
| 0.33.0 | MIT | |
| 0.32.0 | MIT | |
| 0.31.0 | MIT |
Showing the latest 30 of 130. See every release on npm