Chasten - Prithee Conceptual Introduction

This is a conceptual introduction to prithee and uses a lot of words on explanations. The alternative is the much terser API reference, which focuses on prithee's public contract and might be more useful after you've learned the concepts.

Prithee lets you write purely functional handlers and middlewares even when they require impure input or produce impure output. It achieves this by having you implement impure code somewhere else, and letting your handlers and middleware deal with pure data.

Your handlers and middlewares will be given the data they depend on (their coeffects) in a specific property under the request object, and they can make side effects happen by putting descriptions of these effects into a specific property under the response object.

In a more concrete example, you could write a handler like the following, which has both coeffects and effects:

const { COEFFECTS, EFFECTS } = require('@chasten/prithee');

async function handler(request) {
  const {
    [COEFFECTS]: {
      user,
    } = request;

    return user ?
      {
        [EFFECTS]: {
          log: `Looked up user ${user.id}. Call your nearest GDPR Data Officer.`,
        },
        body: JSON.stringify(user),
        headers: {
          "Content-Type": "application/json",
        },
        status: 200,
      } :
      { status: 404 };
  }
}

module.exports = handler;

In another file in your code, you'll specify how to provide the coeffects, and how to carry out the effects:

const { COEFFECTS, EFFECTS } = require('@chasten/prithee');

const pritheeInstance = {
  [COEFFECTS]: {
    async user(request) {
      return database.get(request.query.userId);
    }
  },
  [EFFECTS]: {
    log: console.log,
  },
};

module.exports = pritheeInstance;

In yet a third file in your code, you'll bind the two together, by wrapping your handler in a prithee middleware:

const { wrap } = require('@chasten/chasten');
const { fn } = require('@chasten/prithee');
const handler = require('./handler');
const pritheeInstance = require('./prithee-instance');

const prithee = fn(pritheeInstance);

module.exports = wrap(handler, [prithee]);

What's the point, then, if your running application code ends up impure, anyway? There are actually a number of benefits.