Typescript implementation of rfc 7807 problem details

Typescript implementation of RFC 7870 inspired from Hellang ProblemDetails

Problem details options

  • typePrefix - Reference
  • contentTypes - response content type, defaults to "application/problem+json"
  • mapStatusCode - a function that creates default problem details in case there's no mapping for the current error or the predicate mapping didn't pass
  • appendCacheHeaders - a function the add no cache response header
  • includeExceptionDetails - a function that determines whether to include exception details or not.
  • exceptionDetailsPropertyName - the property name that will have the error stack trace, defaults to exceptionDetails

Cache headers

By default these headers will be added

"cache-control": "no-cache, no-store, must-revalidate"
"pragma": "no-cache"
"etag": "0"
"expires": "0"

You can modify this behaviour by changing appendCacheHeaders option

// Default function
options.appendCacheHeaders = (setHeader) => {
	setHeader("cache-control", "no-cache, no-store, must-revalidate");
	setHeader("pragma", "no-cache");
	setHeader("etag", "0");
	setHeader("expires", "0");
};

// Do not add any cache header
options.appendCacheHeaders = (setHeader) => {};

// Add custom cache headers
options.appendCacheHeaders = (setHeader) => {
	setHeader("cache-control", "no-cache, no-store, must-revalidate");
	setHeader("pragma", "no-cache");
	setHeader("etag", "0");
	setHeader("expires", "0");
	setHeader("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT"); // This line
};

Framework support

The library support Koa.js and express.js out of the box

  • Express
import express from "express";
import { problemDetailsMiddleware } from "rfc-7807-problem-details";
const app = express();

// Should be added at the last of the middleware chain
app.use(problemDetailsMiddleware.express());

Complete example

  • Koa
import Koa from "koa";
import { problemDetailsMiddleware } from "rfc-7807-problem-details";
const app = new Koa();

// Should be added at the start of the middleware chain
app.use(problemDetailsMiddleware.koa());

Complete example

  • Deno - Oak
import { Application } from "https://deno.land/x/oak/mod.ts";
import {
	ProblemDetailsException,
	ProblemDetailsOptions,
	ProblemDetailsSetup,
} from "https://esm.sh/rfc-7807-problem-details";
const app = new Application();

app.use(async (ctx, next) => {
	// set your options before constructing ProblemDetailsSetup
	const options = new ProblemDetailsOptions();
	options.typePrefix = `https://example.com/probs/out-of-credit`;

	const setup = new ProblemDetailsSetup(options);

	return async (context: any, next: any) => {
		try {
			await next();
		} catch (error) {
			options.appendCacheHeaders((name, value) =>
				ctx.response.headers.set(name, value)
			);

			const problem = setup.prepareProblemDetails(error, context);
			ctx.response.headers.set("content-type", options.contentTypes);
			context.status = problem.status;
			context.body = problem;
		}
	};
});

app.use(async (ctx, next) => {
	switch (ctx.request.url.pathname) {
		case "/example/throw":
			throw new ProblemDetailsException({
				type: "cannot-proceed",
				status: 400,
				title: "You cannot proceed.",
			});
		default:
			ctx.response.body = "Hello World";
			break;
	}
	await next();
});

await app.listen({ port: 8000 });

If you'd like to support custom framework take a look at the source code to see how you can do it.

For more examples check the demo directory.