The Talent500 Blog
Using Cookies with Postgraphile 1

Using Cookies with Postgraphile

Cookies are an essential part of modern web development and are used by a variety of web applications for various purposes. Postgraphile is a powerful and popular open-source GraphQL server that enables efficient data retrieval and manipulation. PostGraphile is used as a library within several Node server frameworks. The PostGraphile library mode may be used inside a server framework in two ways: as middleware or using the more verbose individual route handlers introduced in PostGraphile v4.10.0.

Cookies may be a relatively safe means of client-side storing. They can be configured as follows:

  • HTTP only: cannot be accessible through client-side JavaScript, preventing it from being accessed by 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: Ensure that the cookie is only delivered if the site matches your domain/subdomain.

For cookie processing, we will utilise Postgraphile with Express, although any equivalent library may be used. This blog describes how cookies are used in a Postgraphile-based application. 

Prerequisites

  • Postgraphile – Creates a GraphQL API from a Postgres database in real time.
  • Express – Minimalistic backend framework for NodeJS

Setup

We’ll begin with a basic Express configuration produced using express-generator.

1const createError = require(“http-errors”);
2const express = require(“express”);
3const path = require(“path”);
4const cookieParser = require(“cookie-parser”);
5const logger = require(“morgan”);
6
7const app = express();
8
9require(“dotenv”).config();
10
11app.use(logger(“dev”));
12app.use(express.json());
13app.use(express.urlencoded({ extended: false }));
14app.use(express.static(path.join(__dirname, “public”)));
15
16// Use secret key to sign the cookies on creation and parsing
17app.use(cookieParser(process.env.SECRET_KEY));
18
19// Catch 404 and forward to error handler
20app.use(function (req, res, next) {
21  next(createError(404));22});
23
24// Error handler
25app.use(function (err, req, res) {
26  // Set locals, only providing error in development
27  res.locals.message = err.message;
28  res.locals.error = req.app.get(“env”) === “development” ? err : {};
29
30  // Render the error page
31  res.status(err.status || 500);
32  res.render(“error”);
33});
34
35module.exports = app;

For adding Postgraphile to an express app, go to the use library page:

1app.use(
2  postgraphile(
3    process.env.DATABASE_URL || “postgres://user:pass@host:5432/dbname”,
4    “public”,
5    {
6      watchPg: true,
7      graphiql: true,
8      enhanceGraphiql: true,
9    }
10  )
11);

Now it’s time to arrange the table. We’ll need a private user accounts database and an authenticate user function that returns a JWT token of the form:

1{

2  token: ‘jwt_token_here’,

3  username: ”,

4  …anyOtherDetails

5}


We will not go into depth regarding table construction or authentication because there are several approaches. 

Adding the Plugin library

To add a cookie to the request, we’ll utilise the open-source @graphile/operation-hooks package from Github.
1npm install @graphile/operation-hooks
2# OR
3yarn add @graphile/operation-hooks

To include the library into the app, follow these steps:

1const { postgraphile, makePluginHook } = require(“postgraphile”);

2

3const pluginHook = makePluginHook([

4  require(“@graphile/operation-hooks”).default,

5  // Any more PostGraphile server plugins here

6]);

7

8app.use(

9  postgraphile(

10    process.env.DATABASE_URL || “postgres://user:pass@host:5432/dbname”,

11    “public”,

12    {

13      watchPg: true,

14      graphiql: true,

15      enhanceGraphiql: true,

16      pluginHook,

17      appendPlugins: [

18        // You will be adding the hooks here

19      ],

20    }

21  )
22);

Adding the Plugin

The plugin allows for two different types of hooks:

  1. SQL Hooks
  2. JavaScript Hooks

Because accessing cookies is a JavaScript action, we will focus on the second type.

We may use the addOperationHook function to integrate the plugin into the build system.

1module.exports = function OperationHookPlugin(builder) {

2  builder.hook(“init”, (_, build) => {

3    // Register our operation hook (passing it the build object):

4    // setAuthCookie is a function we will define later.

5    build.addOperationHook(useAuthCredentials(build));

6

7    // Graphile Engine hooks must always return their input or a derivative of

8    // it.

9    return _;

10  });

11};

If this is in a file called set-auth-cookie.js, it may be added to the append plugins array as follows:

1{

2  appendPlugins: [

3    require(‘./set-auth-cookie.js’),

4  ],
5}

4. Designing the hook

The function is called with two arguments: the build process and the current fieldContext.

The fieldContext contains fields that may be used to limit down the mutation or query that we want to target; for example, if we want the hook to only execute on mutations, we can use the fieldContext isRootMutation field.

1const useAuthCredentials = build => fieldContext => {

const { isRootMutation } = fieldContext;

if (!isRootMutation) {

4    // No hook added here

5    return null;

6  }

7};

To instruct the system on how to use the plugin, we must return an object having before, after, and error properties. These keywords can be used as follows:

1return {

2  // An optional list of callbacks to call before the operation

3  before: [

4    // 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.

5    {

6      // Priority is a number between 0 and 1000. If you’re not sure where to put it, then 500 is a great starting point.

7      priority: 500,

8      // 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.

9      callback: logAttempt,

10    },

11  ],

12

13  // 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.

14  after: [],

15

16  // 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.

17  error: [],
18};

We’ll add it to the after array since we want our action to happen after we obtain the outcome of the mutation.

1const useAuthCredentials = build => fieldContext => {

const { isRootMutation, pgFieldIntrospection } = fieldContext;

if (!isRootMutation) {

4    // No hook added here

5    return null;

6  }
7
if (

9    !pgFieldIntrospection ||

10    // Name of the mutation is authenticateUser

11    pgFieldIntrospection.name !== “authenticateUser”

12  ) {

13    // narrowing the scope down to the mutation we want

14    return null;

15  }

16

17  return {

18    before: [],

19    after: [

20      {
21        priority: 1000,

22        callback: (result, args, context) => {

23          // The result is here, so we can access accessToken and username.

24          console.log(result);

25        },

26      },

27    ],

28    error: [],

29  };

30};

Because the functionality is contained within the plugin hook, we do not have the express result to set the cookie.

However there is an escape route with the third argument: context. Postgraphile allows us to send functions or values into the context variable from the postgraphile instance.

1app.use(

2  postgraphile(process.env.DATABASE_URL, “public”, {

3    async additionalGraphQLContextFromRequest(req, res) {

4      return {

5        // Function to set the cookie passed into the context object

6        setAuthCookie: function (authCreds) {

7          res.cookie(“app_creds”, authCreds, {

8            signed: true,

9            httpOnly: true,

10            secure: true,

11            // Check if you want to include SameSite cookies here, depending on your hosting.

12          });

13        },

14      };
15    },

16  })
17);


Inside the plugin hook, we can now set the cookie.

1{

2  priority: 1000,

3  callback: (result, args, context) => {

4    // This function is passed from additionalGraphQLContextFromRequest as detailed in the snippet above

5    context.setAuthCookie(result);

6  }

7}

5. Reading from the Cookie

Express will interpret the cookies for us because we have previously added the cookieParser with SECRET KEY.

Yet, we most likely want them to be available via Postgraphile SQL functions. That’s how we know if the user is logged in and what their rights are. Postgraphile offers a pgSettings object for this purpose.
1app.use(
2  postgraphile(process.env.DATABASE_URL, “public”, {
3    pgSettings: async req => ({
4      user: req.signedCookies[“app_creds”],
5    }),
6  })
7);

The variables supplied from settings can be retrieved within a SQL function as follows:

1current_setting(‘user’)

We may save any information in cookies, get it on the Express end, and use it to authenticate or authorise Postgres operations.


Conclusion

Using cookies with Postgraphile can be an effective way to create a secure web application. It can help you keep user data and information safe, as well as protect your website from malicious attacks. With the help of Postgraphile, you can easily manage your cookies and provide a secure experience for your users. With the right cookie settings and the appropriate setup, your web application will be both secure and user-friendly.

 

0
Afreen Khalfe

Afreen Khalfe

A professional writer and graphic design expert. She loves writing about technology trends, web development, coding, and much more. A strong lady who loves to sit around nature and hear nature’s sound.

Add comment