https://github.com/ezzabuzaid/rfc-7807-problem-details
Typescript implementation of RFC 7807
https://github.com/ezzabuzaid/rfc-7807-problem-details
deno nodejs problem-details rfc-7807 typescript
Last synced: 11 months ago
JSON representation
Typescript implementation of RFC 7807
- Host: GitHub
- URL: https://github.com/ezzabuzaid/rfc-7807-problem-details
- Owner: ezzabuzaid
- License: mit
- Created: 2022-05-10T12:16:00.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2022-12-13T20:05:44.000Z (over 3 years ago)
- Last Synced: 2024-11-26T23:34:41.077Z (over 1 year ago)
- Topics: deno, nodejs, problem-details, rfc-7807, typescript
- Language: TypeScript
- Homepage: https://docs.page/ezzabuzaid/rfc-7807-problem-details
- Size: 1.51 MB
- Stars: 4
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
Typescript implementation of [RFC 7807](https://datatracker.ietf.org/doc/html/rfc7807)
inspired from [Hellang ProblemDetails](https://www.nuget.org/packages/Hellang.Middleware.ProblemDetails)
## Install
```bash
npm i rfc-7807-problem-details
# Using Yarn
yarn add rfc-7807-problem-details
```
## Problem details options
- typePrefix - [Reference](https://datatracker.ietf.org/doc/html/rfc7807#section-3.1)
- 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
```txt
"cache-control": "no-cache, no-store, must-revalidate"
"pragma": "no-cache"
"etag": "0"
"expires": "0"
```
You can modify this behaviour by changing appendCacheHeaders option
```typescript
// 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
```typescript
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](https://docs.page/ezzabuzaid/rfc-7807-problem-details/expressjs)
- Koa
```typescript
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](https://docs.page/ezzabuzaid/rfc-7807-problem-details/koa)
- Deno - Oak
```typescript
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.