DEV Community

Cover image for Intro to PostGraphile V5 (Part 4): Making the Schema Yours
Benjie for Graphile

Posted on • Edited on

Intro to PostGraphile V5 (Part 4): Making the Schema Yours

Benjie is the community–funded maintainer of the Graphile suite — a collection of MIT–licensed Node.js developer tooling for GraphQL and/or PostgreSQL. A key project in this suite is PostGraphile, a high performance, customizable and extensible GraphQL schema generator for APIs backed primarily (but not exclusively!) by PostgreSQL.

This is part four in the "Intro to PostGraphile V5" series, which started with Part 1: Replacing the Foundations. This week, Benjie takes a look at the new behaviour system coming to PostGraphile Version 5, available in pre–release now for all Graphile sponsors, find out more at postgraphile.org

In PostGraphile Version 4, despite the automatic nature of the GraphQL schema generation, we wanted you to be able to exert significant influence over the shape of the final schema. One main way of achieving this was by using the pre–existing permissions in the database — things that were not GRANT–ed in the database would not be exposed by GraphQL. For finer grained control we added the @omit smart tag which you could attach to database resources (tables, views, columns, constraints, functions, etc) in order to have them be omitted from certain scenarios (listing all rows, ordering, filtering, etc).

The @omit system handled a lot of customization needs, but there were still parts of the auto–generation which couldn’t be affected this way. For example, we didn't have a way to just omit root–level single record accessor fields (e.g. Query.userByUsername), so you would have to write a plugin to remove the field after it was added while navigating around the caveats this entailed.

By its very nature, the @omit system was a block–list rather than an allow–list; this was suitable for most who only wanted to make small changes, but some users would rather start with a clean slate and build up. Sadly, we couldn't add an allow–list equivalent because it would cause breaking changes as we added more @omit–able things: things would suddenly disappear from your schema. Not tenable.

It was clear that we would need a better system in Version 5 — it needed to be able to do everything the V4 system could do, and more. It needed to be both an allow–list and a block–list. It needed to be configurable globally, with local overrides, and perhaps even–more–local–still overrides… like globally disabling updates by default, then re–enabling updates on a given table, but then disabling it on a particular unique constraint (e.g. to turn off Mutation.updateUserByUsername).

Now where had I seen something like this before? 🤔

Feature flags!

I created the concept of @behavior strings, inspired by feature flags, giving the user granular control over entity behaviour. You could add + or - in front of a feature string to determine if that feature should be enabled or disabled respectively, features can be scoped to very granular locations, and by having presets specify default behaviours we could evolve the system over time without breaking changes.

Diagram depicting a behavior string constructed from '-create -update -delete' (don't add create, update or delete mutations), '-list +connection' (Don't use lists, do use connections) and '-query:resource:connection' (Don't add a connection field at the query root)

A user can build their behaviour string in an incremental way, using optional + or - to indicate inclusion or omission. The rightmost value is the most significant, the system scans backwards looking for the first match, then uses the + or - modifier to determine if the behaviour is enabled or not.

These behaviour strings are built up by simple concatenation as we navigate through from plugin default behaviours, to the global default behaviour configured in your preset, to the table's behaviour, then to the specific column/constraint's behaviour. When taking an action (e.g. adding an "update" mutation, creating an "order by" enum, etc.) the system scans back through the resulting behaviour string to see if that action is enabled (+) or not (-); it’s that simple!

At long last, with this system, you can exert per–field, per–type, per–argument control over the generated GraphQL schema without having to drop down into the plugin layer to manually delete things. It even replaces things like simpleCollections because you now choose whether you want a list, connection, both or neither via the behaviour.

Sponsors can read more about the new behaviour system here: https://postgraphile.org/postgraphile/next/behavior

Speaking of having greater control over your GraphQL schema, there has been something which has been requested numerous times over the years, but V4 could not support it… It begins with a "P", and if you've been paying close attention to this series you might already know what it is…

Check back next week to find out what the next new feature is You've guessed it — it was polymorphism 🎉 Check it out in Intro to PostGraphile V5 (Part 5): Polymorphism!; and remember: to get your hands on a pre–release version of PostGraphile V5, all you need to do is sponsor Graphile at any tier and then drop us an email or DM! Find out more at graphile.org/sponsor

Top comments (0)