Server Plugins
Stability: experimental, may change in semver minor versions.
NOTE: This page relates to changing how the PostGraphile HTTP server and CLI work. If you're instead looking to change the generated GraphQL schema (e.g. to add fields or types), see Schema Plugins.
In addition to the Graphile Engine plugin system which builds the GraphQL schema in PostGraphile, PostGraphile also has a plugin system for the CLI and web layer. This plugin system is less mature, help documenting it is welcome!
First-party open-source plugins
We have some first-party open source plugins that can enhance your PostGraphile experience, or act as a template for writing your own plugins:
@graphile/operation-hooks
- enables you to add callbacks to certain operations, useful for validation, pre-flight checks, authorization, error handling, notifying users of semi-related information, etc.@graphile/pg-pubsub
- provides realtime capabilities to schema plugins powered by PostgreSQL's LISTEN/NOTIFY.
First-party premium plugins
There are also a couple of first-party plugins that may be purchased on the Graphile Store:
- all features now OSS via@graphile/supporter
@graphile/pg-pubsub
plugin@graphile/pro
Pro Spon - includes protections that can be mounted in front of PostGraphile to protect it from malicious actors
To use these premium plugins you will need a GRAPHILE_LICENSE
environmental
variable to be present, as in these examples:
# GNU/Linux and macOS bash:
export GRAPHILE_LICENSE="license_key_from_graphile_store"
postgraphile -c postgres://...
# Heroku
heroku config:set GRAPHILE_LICENSE="license_key_from_graphile_store" -a my_heroku_app
# Windows Console
set GRAPHILE_LICENSE="license_key_from_graphile_store" & postgraphile -c postgres://...
# Windows PowerShell
$env:GRAPHILE_LICENSE="license_key_from_graphile_store"; postgraphile -c postgres://...
These plugins do not "phone home" so you'll need to update your license at least once every 9 months. You can check the expiry date of your current license in the Graphile Store validator and log in to generate a new license code.
For more information, see the FAQ at the bottom of the Go Pro! page.
Installing
You can install plugins with yarn add
or npm install
, e.g.
yarn add @graphile/operation-hooks
Enabling via CLI flag
PostGraphile plugins can be specified with the --plugins
CLI flag; however
this flag must be the first flag passed to PostGraphile as plugins can
register additional CLI flags. Multiple plugins can be specified with comma
separation:
postgraphile --plugins \
@graphile/operation-hooks,@graphile/pg-pubsub,@graphile/pro \
-c postgres:///my_db
Enabling via .postgraphilerc.js
If you're using the CLI version, plugins can also be enabled via
.postgraphilerc.js
file; for example:
module.exports = {
options: {
plugins: [
"@graphile/operation-hooks",
"@graphile/pg-pubsub",
"@graphile/pro",
],
connection: "postgres:///my_db",
schema: ["app_public"],
// ...
},
};
Enabling via middleware options
This will likely get easier in future, but for now enabling via the middleware is a slightly more involved process:
To include the dependencies using CommonJS (Node 8):
const { postgraphile, makePluginHook } = require("postgraphile");
const { default: OperationHooks } = require("@graphile/operation-hooks");
const { default: PgPubsub } = require("@graphile/pg-pubsub");
const { default: GraphilePro } = require("@graphile/pro");
If you're using ES2015 Modules (ESM) then this syntax may be more to your taste:
import { postgraphile, makePluginHook } from "postgraphile";
import OperationHooks from "@graphile/operation-hooks";
import PgPubsub from "@graphile/pg-pubsub";
import GraphilePro from "@graphile/pro";
To enable the plugins, use makePluginHook
to create a pluginHook
function to
pass via the PostGraphile options:
// Only include as many plugins as you need. An empty array is also valid.
const pluginHook = makePluginHook([OperationHooks, PgPubsub, GraphilePro]);
const postGraphileMiddleware = postgraphile(databaseUrl, "app_public", {
pluginHook,
// ...
});
app.use(postGraphileMiddleware);
Writing your own plugins
IMPORTANT: here be dragons. This interface is experimental, and documentation on it is far from complete. To use this interface you are expected to have deep knowledge of the PostGraphile internals, since that is what you will be augmenting/overriding. Note that many things you might think you need a server plugin for are better served with standard HTTP middleware, e.g. by using Express/Koa/Fastify/etc middleware you can implement CORS, sessions, custom authentication flows, rate limiting, logging, routing, liveness/readiness/health endpoints, statistics collection and much more - you probably do not need to write PostGraphile server plugins for this kind of functionality.
The hook methods available can be viewed in pluginHook.ts. Note that these may change in semver minor releases of PostGraphile as this is not an officially stable API yet.
Each hook method is passed two parameters:
- subject: the thing being hooked
- context: an object containing some relevant helpers
The hooks are expected to return either the thing being hooked (subject), or a derivative of it. Multiple plugins may register for the same hooks, in these cases the output of one hook function will be fed as input to the next. Hooks are synchronous.
Your plugin will export a single object which defines the hook methods; e.g.:
const MyPlugin = {
["cli:greeting"](messages, { chalk }) {
return [...messages, `Hello ${chalk.blue("world")}!`];
},
};
module.exports = MyPlugin;
// or, for ES6 modules:
// export default MyPlugin;
An example of an open source PostGraphile server plugin is @graphile/operation-hooks:
- uses
cli:flags:add:schema
to add--operation-messages
and--operation-messages-preflight
CLI options - uses
cli:library:options
to convert these CLI options to library options - uses
postgraphile:options
to a) convert the library options into graphileBuildOptions (Graphile Engine plugin options), and b) load the OperationHooksPlugin
Other examples you may wish to check out include @graphile/persisted-operations and postgraphile-log-consola.
Inline tweaks via pluginHook
If you are using postgraphile as a library (i.e. middleware) and you want to make a quick tweak to something using the server plugin hooks, you can do so in your main server.js file:
/**
* This plugin override changes the branding piece of graphiql.
*/
const graphiqlBrandingTweak = {
["postgraphile:graphiql:html"](html) {
console.log("Applying GraphiQL Branding Tweak...");
return html.replace(
"</head>",
'<style type="text/css">div.topBar > div.title > div { visibility: hidden; display: none !important; } div.topBar > div.title::after { content: "GraphiQL for MyCompany" }</style></head>',
);
},
};
const pluginHook = makePluginHook([graphiqlBrandingTweak]);
const postGraphileMiddleware = postgraphile(databaseUrl, "app_public", {
pluginHook,
// ...
});
Examples
Origin specific CORS
You can enable generous CORS by
adding the -o,--cors
flag to the CLI or by
adding a enableCors: true
option when using PostGraphile as a library.
However, by being generous, you allow any origin to communicate with you
PostGraphile instance. If you want to allow just one specific origin, and using
a cors
middleware before PostGraphile (which is by far the preferred route!)
is not an option, then you can make a server plugin such as this one:
/**
* This server plugin injects CORS headers to allow requests only from a specific origin.
*/
function makeAllowedOriginTweak(origin) {
return {
["postgraphile:http:handler"](req, { res }) {
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Methods", "HEAD, GET, POST");
res.setHeader(
"Access-Control-Allow-Headers",
[
"Origin",
"X-Requested-With",
// Used by `express-graphql` to determine whether to expose the GraphiQL
// interface (`text/html`) or not.
"Accept",
// Used by PostGraphile for auth purposes.
"Authorization",
// Used by GraphQL Playground and other Apollo-enabled servers
"X-Apollo-Tracing",
// The `Content-*` headers are used when making requests with a body,
// like in a POST request.
"Content-Type",
"Content-Length",
// For our 'Explain' feature
"X-PostGraphile-Explain",
].join(", "),
);
res.setHeader(
"Access-Control-Expose-Headers",
["X-GraphQL-Event-Stream"].join(", "),
);
return req;
},
};
}
Using the plugin would look like this:
const pluginHook = makePluginHook([
makeAllowedOriginTweak("https://graphql.rocks"),
]);
const postGraphileMiddleware = postgraphile(databaseUrl, "app_public", {
pluginHook,
// ...
});
If you need help writing your own PostGraphile server plugins, ask in #help-and-support in our Discord chat.