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/graphql-middleware

v0.13.0

Published

GraphQL middleware for PostGraphile and Stonecrop schema

Downloads

2,753

Readme

@stonecrop/graphql-middleware

GraphQL backend for the Stonecrop framework. Reads doctype schemas and exposes them as PostGraphile plan-step resolvers that handle record fetching, fetch-strategy dispatch, and action execution.

What it does

  • Record fetching — Resolves stonecropRecord and stonecropRecords via direct parameterised SQL, batched via loadOneWithPgClient from @dataplan/pg
  • Fetch-strategy dispatch — For stonecropRecord, iterates meta.links and applies sync / lazy / custom strategies; sync links issue additional SQL and merge results into the record; lazy links are absent from the response
  • Action dispatch — Routes doctype actions to registered handlers via sideEffectWithPgClient
  • PresetcreateStonecropPreset() wraps PostGraphileAmberPreset so user apps never import from PostGraphile directly

Setup

import {
  createStonecropPreset,
  createStonecropPlugin,
  makePgService,
  loadDoctypes,
  registerBuiltinHandlers,
} from '@stonecrop/graphql-middleware'

loadDoctypes('./doctypes')
registerBuiltinHandlers()

const preset: GraphileConfig.Preset = {
  extends: [createStonecropPreset()],
  plugins: [createStonecropPlugin()],
  pgServices: [makePgService({ connectionString: process.env.DATABASE_URL })],
}

createStonecropPlugin() discovers the PgExecutor automatically from pgServices — no executor argument is needed. An optional StonecropPluginOptions object accepts:

| Option | Default | Description | |--------|---------|-------------| | pkField | 'id' | Primary key column name for record lookups |

Debugging

Nuxt module shortcut

When using @stonecrop/nuxt-grafserv, set debug: true in nuxt.config.ts to enable all development aids at once:

export default defineNuxtConfig({
  grafserv: {
    type: 'postgraphile',
    debug: true,
  },
})

debug: true automatically:

  • Enables the Ruru Explain tab (grafast.explain) to inspect Grafast plan steps and generated SQL
  • Injects createDebugPlugin() to log Stonecrop resolver plan construction
  • Configures grafserv.maskError using PostGraphile's recommended pattern: logs every error server-side, returns GraphQLError and safe errors directly, and masks unknown errors with a SHA-1 hash

Never enable debug in production — it exposes query internals and detailed error messages to clients.

Custom preset

If you manage your own preset file, import the debug plugin directly:

import { createStonecropPlugin, createDebugPlugin } from '@stonecrop/graphql-middleware'

export default {
  plugins: [createStonecropPlugin(), createDebugPlugin()],
  grafast: { explain: true },
}

Environment variables

PostGraphile's native DEBUG variables still work alongside Stonecrop's debug plugin:

| Variable | What it shows | |----------|---------------| | DEBUG="@dataplan/pg:PgExecutor" | SQL queries executed by plan steps | | DEBUG="@dataplan/pg:PgExecutor:explain" | SQL plus EXPLAIN output | | DEBUG="graphile-build:warn" | Warnings during schema construction | | DEBUG="graphile-build:SchemaBuilder" | Hook execution order during schema build |

DEBUG="@dataplan/pg:PgExecutor:explain,graphile-build:warn" node server.js

Doctype Schemas

Each doctype JSON file defines structure, relationships, and workflow:

{
  "name": "SalesOrder",
  "tableName": "sales_orders",
  "fields": [
    { "fieldname": "id", "fieldtype": "Data" },
    { "fieldname": "status", "fieldtype": "Select" }
  ],
  "links": {
    "items": {
      "target": "sales-order-item",
      "cardinality": "noneOrMany",
      "backlink": "sales_order_id",
      "fetch": { "method": "sync", "limit": 100 }
    }
  },
  "workflow": {
    "states": ["Draft", "Submitted"],
    "actions": {
      "submit": { "label": "Submit", "handler": "submitOrder" }
    }
  }
}

Fetch strategies

Each entry in links can declare a fetch strategy:

| Strategy | Behaviour | |----------|-----------| | { "method": "sync", "limit": N } | Linked records are fetched in the same resolver call and merged into data | | { "method": "lazy" } | Link is absent from the response; client retrieves it via stonecropRecords with filters | | { "method": "custom", "handler": "myHandler" } | Calls a registered fetch handler (see below) |

When fetch is omitted, noneOrMany/atLeastOne links default to sync (limit 50) and atMostOne/one links default to lazy.

Display-only fields

Fields with fieldtype: "Display" have no backing database column and are excluded from all SQL queries:

{ "fieldname": "planner", "fieldtype": "Display", "component": "Planner", "label": "Resource Planner" }

Actions

Register action handlers with registerHandler. Handlers receive the action arguments and an ActionContext that includes the active PgClient:

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

registerHandler('submitOrder', async (args, ctx) => {
  const [orderId] = args as [string]
  // ctx.pgClient is available for additional database work
  return { submitted: true }
})

Custom fetch handlers

Register custom fetch handlers for links that use { "method": "custom" }:

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

registerFetchHandler('loadLineItems', async (pgClient, parentRecord, link) => {
  // Returns a single record or an array depending on link.cardinality
  const { rows } = await pgClient.query(...)
  return rows
})

API

| Operation | Description | |-----------|-------------| | stonecropRecord(doctype, id, options?) | Fetch a single record; options.includeNested controls which links are resolved | | stonecropRecords(doctype, filters?, orderBy?, limit?, offset?) | Fetch a list with optional filtering and ordering | | stonecropMeta(doctype) | Fetch doctype metadata | | stonecropAllMeta | Fetch all registered doctype metadata | | stonecropAction(doctype, action, args?) | Execute a doctype action |

stonecropRecords accepts orderBy as FIELD_ASC or FIELD_DESC (e.g. "status_ASC"). The field name is validated against the doctype's known fields before interpolation.

For full type signatures see API Reference.