Skip to main content
Version: Draft

Overview

What follows are some of the more commonly used options to serve as a quick reference, but this list can quickly become out of date (feel free to send a PR!). You can use TypeScript or the sponsors-only Spon graphile config options command mentioned above to see what options are available to you — different presets and plugins make different options available.

inflection options

(TypeScript type: GraphileBuild.InflectionOptions)

None at this time.

pgServices

(TypeScript type: ReadonlyArray<GraphileConfig.PgServiceConfiguration>)

Details the PostgreSQL database(s) for PostGraphile to connect to; this is a separate option because it’s used in both the gather phase (for introspection) and at runtime.

Generally it’s best to construct this by using the makePgService helper from the adaptor(s) you are using (see below), but if you want to know the nitty-gritty: each entry in the list is an object with the following keys (only name and adaptor are required):

  • name: string - an arbitrary unique name for this config; please keep it alphanumeric! Don’t set this unless you have more than one pgService; see warning below.
  • adaptor: PgAdaptor - the module to use as the postgres adaptor; e.g. the result of import("@dataplan/pg/adaptors/pg") for the pg module
  • adaptorSettings - internal configuration object specific to the adaptor (automatically created by makePgService(), see adaptorSettings below)
  • schemas: string[] - an array of PostgreSQL schema names to use
  • pgSettings: (requestContext: Grafast.RequestContext) => Record<string, string> | null - a callback function that will be called by the server to determine the pgSettings to use for a particular request
  • pgSettingsForIntrospection: Record<string, string> | null - the pgSettings to use when introspecting the database (for example if you want to change roles)
  • pgSubscriber: PgSubscriber - a PgSubscriber instance that allows code to subscribe to LISTEN/NOTIFY events in the database - useful for GraphQL subscriptions, and also for schema watch mode.
  • withPgClientKey: string - the key on the context object to store the withPgClient method the schema uses for communicating with the database. Defaults to withPgClient if name is "main" (the default), or ${name}_withPgClient otherwise.
  • pgSettingsKey: string - the key on the context object to store the pgSettings configuration to use when communicating with the database. Defaults to pgSettings if name is "main" (the default), or ${name}_pgSettings otherwise.
  • pgSubscriberKey: string - the key on the context object to store the pgSubscriber instance to, for use during GraphQL subscriptions. Defaults to pgSubscriber if name is "main" (the default), or ${name}_pgSubscriber otherwise."
Example manual configuration (advanced, usually not needed)
import * as pg from "pg";
import * as adaptor from "@dataplan/pg/adaptors/pg";

const pgServices = [
{
name: "main",
schemas: ["app_public"],
pgSettingsKey: "pgSettings",
withPgClientKey: "withPgClient",
adaptor,
adaptorSettings: {
pool: new pg.Pool({ connectionString: process.env.DATABASE_URL }),
superuserPool: new pg.Pool({
connectionString: process.env.SUPERUSER_DATABASE_URL,
}),
},
},
];
info

The manual configuration approach above is advanced and usually not needed. Most users should use makePgService() as shown in the next example, which is simpler and handles the configuration for you.

Don’t set name unless you need to!

We recommend that you don’t set a name unless you have more than one pgService. If you do, the different services may need different properties on context to detail how to connect, the settings to use, and how to subscribe to events. The withPgClientKey, pgSettingsKey and pgSubscriberKey options dictate under which keys these properties are stored on the context. By default, if the pgService’s name is left at its default ("main") then it uses "withPgClient", "pgSettings" and "pgSubscriber" respectively for these keys; otherwise it prefixes the keys with the name of the service and an underscore, for example a pgService with name: "otherdb" would use the context keys otherdb_withPgClient, otherdb_pgSettings and otherdb_pgSubscriber.

makePgService

When PostGraphile (or, strictly, @dataplan/pg) wishes to communicate with PostgreSQL, it does so using an “adaptor”. The adaptor must expose a common set of functionality, but on top of that it can add adaptor-specific features, for example you might write an adaptor to add support for using your ORM of choice, so that in your custom fields/plan resolvers you can use that ORM to execute queries against the database.

Your choice of adaptor is quite important if you want to write custom JS logic in your schema, but otherwise it likely comes down to performance, convenience, and maybe avoiding additional dependencies. By default, PostGraphile uses the postgraphile/adaptors/pg adaptor which wraps the pg module.

Every adaptor should expose a makePgService helper function that takes a common set of optional configuration parameters:

  • connectionString
  • schemas
  • superuserConnectionString
  • pubsub (create a pgSubscriber entry; should default to true)
  • pass-through options (a subset of those in pgServices above):
    • name (default: "main")
    • pgSettings
    • pgSettingsKey (default with default name: pgSettings, otherwise: ${name}_pgSettings)
    • pgSettingsForIntrospection
    • withPgClientKey (default with default name: withPgClient, otherwise: ${name}_withPgClient)
    • pgSubscriber
    • pgSubscriberKey (default with default name: pgSubscriber, otherwise: ${name}_pgSubscriber)

The postgraphile/adaptors/pg adaptor additionally accepts these options:

  • pool - pass your own pre-built pg.Pool instance to use, in which case connectionString and poolConfig will be ignored. You are responsible for releasing this pool!
  • poolConfig - additional configuration options (options other than connectionString) to pass through to pg.Pool; see the pg.Pool options which inherit the pg.Client options.
  • superuserPool - as pool, but for superuser connections (only used to install the watch fixtures in watch mode)
  • superuserPoolConfig - as poolConfig, but for superuser connections (only used to install the watch fixtures in watch mode)
info

These common options are those that the postgraphile CLI might pass, which is why every adaptor should support them.

name must be unique!

The name option must be unique across all your pgServices; therefore if you have more than one entry in pgServices you must give each additional entry an explicit and unique name.

Each adaptor may additionally accept any other options it likes (but care should be taken to not conflict with options of other adaptors, or options that we might want to add to core in future).

makePgService will return a fully resolved configuration object, suitable for inclusion into the pgServices array in your graphile.config.mjs (or similar) file.

Example configuration via makePgService (recommended)
import { makePgService } from "postgraphile/adaptors/pg";

/** @type {GraphileConfig.Preset} */
const preset = {
// ...
pgServices: [
makePgService({
// Database connection string:
connectionString: process.env.DATABASE_URL,

// List of database schemas:
schemas: ["app_public"],

// Enable LISTEN/NOTIFY:
pubsub: true,

// Optional: additional pg.Pool configuration
poolConfig: {
max: 10,
idleTimeoutMillis: 30000,
},

// Optional, only needed for `--watch` mode:
superuserConnectionString: process.env.SUPERUSER_DATABASE_URL,

// Optional: additional superuser pg.Pool configuration
superuserPoolConfig: {
max: 2,
},
}),
],
};

adaptorSettings

The adaptorSettings property is an internal configuration object that is created automatically when you use makePgService(). In general, you should not configure this directly. Instead, pass the configuration options directly to makePgService() as shown in the examples above.

If for some advanced reason you need to manually configure pgServices (rather than using makePgService()), then you would need to provide adaptorSettings with whatever the adaptor requires.

postgraphile/adaptors/pg

(Or, equivalently, postgraphile/@dataplan/pg/adaptors/pg or @dataplan/pg/adaptors/pg)

This adaptor uses the pg module under the hood and uses the pg.Pool API primarily, it accepts the following options:

  • pool - pass your own pre-built pg.Pool instance to use, in which case all other (non-superuser) options will be ignored. You are responsible for releasing this pool!
  • connectionString - the database connection string to use, we’ll create a pool for you automatically (and handle releasing it) using this connection string
  • poolConfig - additional configuration options (options other than connectionString) to pass through to pg.Pool; see the pg.Pool options which inherit the pg.Client options.
  • pubsub (default: true) - enable LISTEN/NOTIFY via creation of a pgSubscriber
  • superuserPool - as pool, but for superuser connections (only used to install the watch fixtures in watch mode)
  • superuserConnectionString - as connectionString, but for superuser connections (only used to install the watch fixtures in watch mode)
  • superuserPoolConfig - as poolConfig, but for superuser connections (only used to install the watch fixtures in watch mode)

Note if pool is provided then connectionString and poolConfig should not be specified. Similarly if superuserPool is provided then superuserConnectionString and superuserPoolConfig should not be specified.

gather options

(TypeScript type: GraphileBuild.GatherOptions)

  • pgStrictFunctions: boolean - if true, we’ll treat all PostgreSQL function arguments that don’t have defaults as being required (non-nullable)
  • pgJwtTypes: string | string[] - an array of (or comma separated list of) the names (including schemas) for the types in the database to convert into a JWT (equivalent to giving these type the behavior "-table +jwt"); example: pgJwtTypes: "app_public.jwt_token"
  • installWatchFixtures: boolean - if not false and schema is in watch mode then we will attempt to install the “event triggers” into the database so that PostGraphile can be notified when your database changes

Deprecated options:

  • pgV4UseTableNameForNodeIdentifier: boolean - if true, uses the table name instead of the type name in the Node identifier (highly discouraged because it significantly increases the risk of NodeID conflicts)

schema options

(TypeScript type: GraphileBuild.SchemaOptions)

tip

Plugins may add additional options, please refer to your plugins’ documentation to determine the options that they offer.

  • defaultBehavior: string | undefined - if set, applies a default behavior to all entities; for example to prefer lists over connections: +list -connection.
  • dontSwallowErrors: boolean - if true, errors during the schema build process will throw rather than the system trying to recover from them. Recommended, but not enabled by default as it can be a barrier to entry to new users.
  • exportSchemaSDLPath: string | undefined - when set, writes the latest GraphQL schema definition (SDL) to the given path every time the schema is rebuilt.
  • exportSchemaIntrospectionResultPath: string | undefined - when set, writes the GraphQL introspection JSON to the given path alongside SDL exports.
  • jsonScalarAsString: boolean - if true, JSON values will be stringified rather than returned as “dynamic” objects.
  • pgForbidSetofFunctionsToReturnNull: boolean - if true, setof functions cannot return null, so our list and connection types can be non-nullable in more places.
  • pgJwtSecret
  • pgJwtSignOptions
  • pgOrderByNullsLast: boolean | undefined - if true, orders such that nulls are always last; if false, orders such that nulls are always first; otherwise uses the default ordering
  • pgUseCustomNetworkScalars: boolean - if not false, adds the CidrAddress, MacAddress and similar types for PostgreSQL network scalars.
  • sortExport: boolean - if true (default: false), sorts exported SDL and introspection output for stable diffs.

grafast options

(TypeScript type: GraphileConfig.GrafastOptions)

  • explain - a list of ‘explain’ types that should be exposed to clients via extensions.explain (plan for the operation plan, sql for the SQL), or true to expose everything.
  • context - an object (or function that returns an object, or promise to an object) to be merged into the GraphQL context, accessible from plan resolvers. If a function, it will receive two parameters, first is the request context (which may contain details such as the incoming HTTP request, depends on what server/etc you are using) and the second is the current context object that your results will be merged into (overwriting pre-existing keys).

grafserv options

(TypeScript type: GraphileConfig.GrafservOptions)

  • port: number - Port number to listen on (default: 5678)
  • host: string - Host to listen on (default: ‘127.0.0.1’; consider setting to ‘0.0.0.0’ in Docker and similar environments)
  • graphqlPath: string - The path at which GraphQL will be available; usually /graphql
  • graphiqlPath: string - The path at which GraphiQL will be available; usually /
  • eventStreamPath: string - The path at which the GraphQL event stream would be made available; usually /graphql/stream
  • graphqlOverGET: boolean - If true, we’ll support GraphQL queries over the GET method (beware of the security implications, for example cross-site timing attacks)
  • graphiql: boolean
  • graphiqlOnGraphQLGET: boolean - If true, then we will render GraphiQL on GET requests to the /graphql endpoint
  • watch: boolean - Set true to enable watch mode
  • maxRequestLength: number - The length, in bytes, for the largest request body that the server will accept, only used if the framework of choice doesn’t already handle input parsing

pgSettings

Connections from PostGraphile to the PostgreSQL database may need to carry with them custom settings to be set within the PostgreSQL transaction. These can be used to indicate simple PostgreSQL settings such as statement_timeout=5000, or can be used to indicate details of the currently active user (e.g. jwt.claims.user_id = 42).

Every pgService can indicate its own pgSettings callback to dictate which settings that specific service should use with its associated database (see pgServices above), and these settings are added to the GraphQL context using the key set in that services’ pgSettingsKey.

Presets may also add values to pgSettings, for example postgraphile/presets/lazy-jwt parses the Authorization header and adds the claims from the JWT to pgSettings.

lazy-jwt is intentionally minimal

postgraphile/presets/lazy-jwt is a stopgap for teams that are still wiring up their own authentication. It only supports Bearer tokens, expects a shared secret unless you override the verification options, and cannot help with common production concerns such as refresh tokens, revocation, or key rotation. Plan to replace it with middleware in your application framework that verifies credentials and then share that information with PostgreSQL through pgSettings.

For most PostGraphile users there’s only one pgService, and the default pgSettingsKey is "pgSettings", so rather than configuring pgSettings via the adaptor you might opt to configure it via including a pgSettings key in the object you return from the Grafast context callback mentioned in the “Grafast options” section above. The value for this key should be a POJO (plain old JavaScript object) with string keys and string values, and you should be careful to copy across settings from the pgService adaptor and any presets/plugins that may have added to it. For example:

graphile.config.mjs
export default {
// ...

grafast: {
context(requestContext, args) {
return {
pgSettings: {
// If any pgSettings were already set, mix them in
...args.contextValue?.pgSettings,
// Add our own settings
statement_timeout: "10000",
},
};
},
},
};
Naming conventions

You can use pgSettings to define variables that your Postgres functions/policies depend on, or to tweak internal Postgres settings.

When adding variables for your own usage, the keys must contain either one or two period (.) characters, and the prefix (the bit before the first period) must not be used by any Postgres extension. We recommend using a prefix such as jwt. or myapp.. Examples: jwt.claims.userid, myapp.is_admin

Variables without periods will be interpreted as internal Postgres settings, such as role, and will be applied by Postgres.

Exposing HTTP request data to PostgreSQL

Using the pgSettings functionality mentioned above you can extend the data made available within PostgreSQL through current_setting(...). Remember: the grafast.context entry can be a callback (even an asynchronous callback if you need) which can extract details from the HTTP request.

When using PostGraphile in library mode, you will likely have a middleware that handles user authentication (whether this be via sessions, cookies, or JWTs). You can use the requestContext to extract details from the request that came through your Grafserv adaptor and then expose this data to PostgreSQL; for example in an express app you might do something like:

graphile.config.js
export default {
// ...

grafast: {
async context(requestContext, args) {
// Extract request details from the requestContext:
const req = requestContext.expressv4?.req;

return {
pgSettings: {
...args.contextValue?.pgSettings,
// Expose a specific header (if present) to PostgreSQL
"myapp.headers.x_something": req?.getHeader("x-something"),
// Expose the user id from the request, if present
"myapp.user_id": req?.user?.id,
},
};
},
},
};

If you’re using the PostGraphile CLI then you won’t have middleware to do the heavy lifting for you, but you can still process incoming request headers - this more complicated example extracts the user’s ID from a JWT and sends that and the value of a specific HTTP header to PostgreSQL:

graphile.config.js
import jwt from "jsonwebtoken";

const JWT_SECRET = process.env.JWT_SECRET;

export default {
// ...

grafast: {
async context(requestContext, args) {
// Extract request details from the requestContext:
const req = requestContext.node?.req;
// Or: const req = requestContext.expressv4?.req;
// Or: const ctx = requestContext.koav2?.ctx;
// Or: const req = requestContext.fastifyv4?.request;

const context = {
// Base settings for all requests:
pgSettings: {
...args.contextValue?.pgSettings,
// Expose a specific header (if present) to PostgreSQL
"myapp.headers.x_something": req?.getHeader("x-something"),
},
};

// Process the authorization header, if present
const auth = req?.headers["authorization"];
if (typeof auth === "string" && typeof JWT_SECRET === "string") {
const parts = auth.split(" ");
if (parts.length === 2 && parts[0].toLowerCase() === "bearer") {
const token = parts[1];
const claims = jwt.verify(token, JWT_SECRET, {
algorithms: ["HS256", "HS384"],
audience: "postgraphile",
complete: false,
});
// Expose the user id from the request, if present
context.pgSettings["myapp.user_id"] = claims.uid;
}
}

return context;
},
},
};
Use abstractions

GraphQL itself is transport agnostic, as is grafast, so depending on how you choose to use your PostGraphile schema you may or may not have access to an HTTP request. Your context callback should be written to support all the different ways that your schema may be used: directly, over HTTP, using websockets, etc.

Do not expose the request, response or requestContext objects via the GraphQL context, always use abstractions.

Accessing pgSettings data in PostgreSQL

With either of the above examples, you could write an SQL function get_x_something() to get the myapp.headers.x_something setting:

create function get_x_something() returns text as $$
select nullif(current_setting('myapp.headers.x_something', true), '')::text;
$$ language sql stable;

By default, everything in pgSettings is applied to the current transaction with set_config($key, $value, true); note that set_config only supports string values so it is best to only feed pgSettings string values (we’ll convert other values using the String() function, which may not have the effect you intend). All settings are automatically reset when the transaction completes.

Here’s an example of switching the PostgreSQL client into the ‘visitor’ role, and applying the application setting jwt.claims.user_id using the req.user object from an Express server:

graphile.config.js
export default {
grafast: {
context(requestContext, args) {
// Base context used for all GraphQL requests
const context = {
pgSettings: {
...args.contextValue?.pgSettings,
role: "visitor",
},
};

// Extract the current user from the Express request:
const user = requestContext.expressv4?.req.user;

// If there's a user, pass additional data to Postgres:
if (user) {
context.pgSettings["jwt.claims.user_id"] = String(user.id);
}

return context;
},
},
};