---
title: "Using Cookies with Postgraphile"
description: "Tutorial on reading and setting cookies on Postgraphile"
canonical_url: "https://www.bigbinary.com/blog/cookies-with-postgraphile"
markdown_url: "https://www.bigbinary.com/blog/cookies-with-postgraphile.md"
---

# Using Cookies with Postgraphile

Tutorial on reading and setting cookies on Postgraphile

- Author: Agney Menon
- Published: June 1, 2021
- Categories: Misc

This blog details usage of cookies on a Postgraphile-based application. We will
be using Postgraphile with Express for processing the cookies, but any similar
library can be used.

Cookies can be a very safe method for storage on the client side. They can be
set as:

- HTTP only: cannot be accessed through client-side JavaScript, saving it from
  any third party client-side scripts or web extensions.
- Secure: The web browser ensures that the cookies are set only on a _secure_
  channel.
- Signed: We can sign the content to make sure it isn't changed on the client
  side.
- Same Site: Make sure that the cookie is sent only if the site matches your
  domain/subdomain ([details](https://web.dev/samesite-cookies-explained/))

## Prerequisites

- Postgraphile - Generates an instant GraphQL API from a Postgres database
- Express - Minimalistic backend framework for NodeJS

## Setup

We will start off with a base Express setup generated with
[express-generator](https://expressjs.com/en/starter/generator.html).

```javascript
const createError = require("http-errors");
const express = require("express");
const path = require("path");
const cookieParser = require("cookie-parser");
const logger = require("morgan");

const app = express();

require("dotenv").config();

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, "public")));

// Use secret key to sign the cookies on creation and parsing
app.use(cookieParser(process.env.SECRET_KEY));

// Catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// Error handler
app.use(function (err, req, res) {
  // Set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // Render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;
```

From
[Postgraphile's usage library page](https://www.graphile.org/postgraphile/usage-library/)
for adding Postgraphile to an express app:

```javascript
app.use(
  postgraphile(
    process.env.DATABASE_URL || "postgres://user:pass@host:5432/dbname",
    "public",
    {
      watchPg: true,
      graphiql: true,
      enhanceGraphiql: true,
    }
  )
);
```

Now for the table setup. We need a private `user_accounts` table and a method
named `authenticate_user` that will return a JWT token of the form:

```
{
  token: 'jwt_token_here',
  username: '',
  ...anyOtherDetails
}
```

We will not be detailing table creation or authentication as there are many ways
to go about it. But if you need help,
[Postgraphile security](https://www.graphile.org/postgraphile/security/) is the
page to rely on.

## Adding the Plugin library

To attach a cookie to the request, we will use the `@graphile/operation-hooks`
library which is open-sourced
[on Github](https://github.com/graphile/operation-hooks).

```bash
npm install @graphile/operation-hooks
# OR
yarn add @graphile/operation-hooks
```

To add the library to the app:

```javascript
const { postgraphile, makePluginHook } = require("postgraphile");

const pluginHook = makePluginHook([
  require("@graphile/operation-hooks").default,
  // Any more PostGraphile server plugins here
]);

app.use(
  postgraphile(
    process.env.DATABASE_URL || "postgres://user:pass@host:5432/dbname",
    "public",
    {
      watchPg: true,
      graphiql: true,
      enhanceGraphiql: true,
      pluginHook,
      appendPlugins: [
        // You will be adding the hooks here
      ],
    }
  )
);
```

## Adding the Plugin

The plugin allows for two different types of hooks:

1. [SQL Hooks](https://github.com/graphile/operation-hooks#sql-hooks)
2. [JavaScript Hooks](https://github.com/graphile/operation-hooks#implementing-operation-hooks-in-javascript)

Since accessing cookies is a JavaScript operation, we will be concentrating on
the second type.

To hook the plugin into the build system, we can use the `addOperationHook`
method.

```javascript
module.exports = function OperationHookPlugin(builder) {
  builder.hook("init", (_, build) => {
    // Register our operation hook (passing it the build object):
    // setAuthCookie is a function we will define later.
    build.addOperationHook(useAuthCredentials(build));

    // Graphile Engine hooks must always return their input or a derivative of
    // it.
    return _;
  });
};
```

If this is contained in a file named `set-auth-cookie.js`, then the plugin can
be added to the append plugins array as follows:

```javascript
{
  appendPlugins: [
    require('./set-auth-cookie.js'),
  ],
}
```

## Designing the hook

The function to be executed receives two arguments: `build` process and the
current `fieldContext`.

The `fieldContext` consists of fields that can be used to narrow down the
mutation or query that we want to target; e.g. if the hook is to run only on
mutations, we can use the `fieldContext.isRootMutation` field.

```javascript
const useAuthCredentials = build => fieldContext => {
  const { isRootMutation } = fieldContext;
  if (!isRootMutation) {
    // No hook added here
    return null;
  }
};
```

To direct the system on usage of the plugin, we have to return an object with
`before`, `after` or `error` fields. Here is how these keywords can be used:

(comments are from
[the example repository](https://github.com/graphile/operation-hooks-example/blob/master/hooks/logger.js))

```javascript
return {
  // An optional list of callbacks to call before the operation
  before: [
    // You may register more than one callback if you wish. They will be mixed in with the callbacks registered from other plugins and called in the order specified by their priority value.
    {
      // Priority is a number between 0 and 1000. If you're not sure where to put it, then 500 is a great starting point.
      priority: 500,
      // This function (which can be asynchronous) will be called before the operation. It will be passed a value that it must return verbatim. The only other valid return is `null` in which case an error will be thrown.
      callback: logAttempt,
    },
  ],

  // As `before`, except the callback is called after the operation and will be passed the result of the operation; you may return a derivative of the result.
  after: [],

  // As `before`; except the callback is called if an error occurs; it will be passed the error and must return either the error or a derivative of it.
  error: [],
};
```

Since we want our action to happen after we get result from the mutation, we
will add it to the `after` array.

```javascript
const useAuthCredentials = build => fieldContext => {
  const { isRootMutation, pgFieldIntrospection } = fieldContext;
  if (!isRootMutation) {
    // No hook added here
    return null;
  }

  if (
    !pgFieldIntrospection ||
    // Name of the mutation is authenticateUser
    pgFieldIntrospection.name !== "authenticateUser"
  ) {
    // narrowing the scope down to the mutation we want
    return null;
  }

  return {
    before: [],
    after: [
      {
        priority: 1000,
        callback: (result, args, context) => {
          // The result is here, so we can access accessToken and username.
          console.log(result);
        },
      },
    ],
    error: [],
  };
};
```

Since the functionality is inside the plugin hook, we do not have the express
result to set the cookie 😞.

But we do have an escape hatch with the third argument: `context`. Postgraphile
allows us to pass functions or values into the context variable from the
postgraphile instance.

```javascript
app.use(
  postgraphile(process.env.DATABASE_URL, "public", {
    async additionalGraphQLContextFromRequest(req, res) {
      return {
        // Function to set the cookie passed into the context object
        setAuthCookie: function (authCreds) {
          res.cookie("app_creds", authCreds, {
            signed: true,
            httpOnly: true,
            secure: true,
            // Check if you want to include SameSite cookies here, depending on your hosting.
          });
        },
      };
    },
  })
);
```

We can now set the cookie inside the plugin hook.

```javascript
{
  priority: 1000,
  callback: (result, args, context) => {
    // This function is passed from additionalGraphQLContextFromRequest as detailed in the snippet above
    context.setAuthCookie(result);
  }
}
```

## Reading from the Cookie 🍪

We have already added the `cookieParser` with `SECRET_KEY`, so express will
parse the cookies for us.

But we probably want them to be accessible inside SQL functions for
Postgraphile. That is how we can determine if the user is signed in or what
their permissions are. To do that, Postgraphile provides a `pgSettings` object.

```javascript
app.use(
  postgraphile(process.env.DATABASE_URL, "public", {
    pgSettings: async req => ({
      user: req.signedCookies["app_creds"],
    }),
  })
);
```

Inside an SQL function, the variables passed from settings can be accessed like
this:

```sql
current_setting('user')
```

---

That's all 🎉. We can store any details in cookies, retrieve them on the Express
end and use them inside Postgres functions for authentication or authorization.

Check out [operation-hooks](https://github.com/graphile/operation-hooks) plugin
for more details.

## Links

- [Human page](https://www.bigbinary.com/blog/cookies-with-postgraphile)
