Skip to content

🚨 Houston, we have an application/problem+json!

License

Notifications You must be signed in to change notification settings

lukecarr/houston

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

84 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Houston

🚨 We have an application/problem+json!

npm npm bundle size npm install size maintainability code coverage vulnerabilities dependencies

πŸš€ Quick Start

Install

# npm
npm i @moducate/houston

# or yarn
yarn add @moducate/houston

Import

// ESM / TypeScript
import { withError } from "@moducate/houston";

// or CommonJS
const { withError } = require("@moducate/houston");

πŸ“„ Templates

You can create error templates using the exported withTemplate function:

const { withTemplate } = require("@moducate/houston");
const app = require("express")();

const [withUserNotFound, rawUserNotFound] = withTemplate(({ userId }) => ({
  type: "https://example.com/user-not-found",
  status: 404,
  instance: `/users/${userId}`,
}));

app.get("/not-found", (_, res) => {
  return withUserNotFound(res, { userId: 1 });
});

const res = rawUserNotFound({ userId: 1 });
// => The second function returned by withTemplate transforms and returns an object (decoupled from http.ServerResponse)

console.log(JSON.stringify(res));
// => { "type": "https://example.com/user-not-found", "status": 404, "instance": "/users/1" }

If you are not needing to use both functions, you can use this handy shorthand to obtain one of them:

const { withTemplate } = require("@moducate/houston");

const [withNotFound] = withTemplate(() => ({ type: "https://example.com/not-found", status: 404 }));
// => Returns the function that transforms a http.ServerResponse

const [, withNotFound] = withTemplate(() => ({ type: "https://example.com/not-found", status: 404 }));
// => Returns the raw function for transforming an object

πŸ’ͺ TypeScript

withTemplate supports TypeScript generics so you can have type definitions for your template's parameters:

const [withUserNotFound] = withTemplate<{ userId: number }>(({ userId }) => ({
  type: "https://example.com/user-not-found",
  status: 404,
  instance: `/users/${userId}`,
}));
// => withUserNotFound's second parameter (`params`) now has the type `{ userId: number }`

🏷 MIME Type

The exported mime constant can be used to obtain the media/MIME type for RFC 7807 JSON errors.

const { mime } = require("@moducate/houston");

console.log(mime);
// => 'application/problem+json'

This is what the Content-Type header of the response supplied to withError is set to.

πŸ›  Options

You can supply options as an additional parameter to withError and withTemplate (and the subsequent withError function returned by the template) to configure Houston's behaviour:

...

withError(
  res,
  { type: "https://example.com/not-found", status: 404 },
  { /* options */ },
);

...

options.stringify

You can supply a custom function used to stringify the error response's JSON body.

By default, JSON.stringify is used.

πŸ’‘ Examples

πŸ“ Full source code for these examples can be found in the examples directory.

Express

const { withError } = require("@moducate/houston");
const app = require("express")();

app.get("/not-found", (_, res) => {
  return withError(res, { type: "https://example.com/not-found", status: 404 });
});

Next.js API Routes

import { withError } from "@moducate/houston/nextjs";

export default function handler(req, res) {
  return withError(req, res, { type: "https://example.com/not-found", status: 404 });
}

Cloudflare Workers

import { withError } from "@moducate/houston/cf-workers";

addEventListener("fetch", event => {
  return withError(event, { type: "https://example.com/not-found", status: 404 });
});

Netlify Functions

import { withError } from "@moducate/houston/netlify";
import type { Handler } from "@netlify/functions";

const handler: Handler = async (event, context) => {
  return withError(event, { type: "https://example.com/not-found", status: 404 });
};

export { handler };

Fastify

⚠ Fastify uses a custom Reply class, rather than http.ServerResponse.

This means you need to supply reply.raw (instead of reply) to withError().

const { withError } = require("@moducate/houston");
const app = require("fastify")();

app.get("/not-found", (_, reply) => {
  return withError(reply.raw, { type: "https://example.com/not-found", status: 404 });
});

fast-json-stringify

See the examples/fast-json-stringify directory for an example project using Houston with Fastify and fast-json-stringify.

Templates

See the examples/templates directory for an example project using Houston's templating functionality with Express.

βš– License

Houston is licensed under the MIT License.