Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 50 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,68 @@
# feh [![feh npm version](https://img.shields.io/npm/v/%40zhaoworks%2Ffeh)](https://www.npmjs.com/package/@zhaoworks/feh) [![last commit](https://img.shields.io/github/last-commit/zhaoworks/feh)](https://github.com/zhaoworks/feh/commits/main/)
###### — @zhaoworks/feh

> *feh* is a Fastify Error Responder, it's a plugin to facilitate and standardize error management in your Fastify application.
Fastify reply helper for standardized status and error responses.

## Usage
#### Features

### Installation
- ✅ `reply.error(status, { message })`
- ✅ `reply.status(status, payload)` for string or JSON payloads
- ✅ Unified formatter with `kind` context (`error` or `status`)

```sh
bun add @zhaoworks/feh
```

<details>
<summary>npm</summary>

> ```sh
> npm install @zhaoworks/feh
> ```

</details>

<details>
<summary>yarn</summary>

> ```sh
> yarn add @zhaoworks/feh
> ```
#### Installation

</details>
```apache
λ bun add @zhaoworks/feh
```

<details>
<summary>pnpm</summary>
#### Usage

> ```sh
> pnpm add @zhaoworks/feh
> ```
```ts
import fastify from 'fastify';
import feh from '@zhaoworks/feh';

</details>
const app = fastify();

app.register(feh, {
format: (input) => {
if (input.kind === 'error') {
return {
ok: false,
status: input.status,
message: input.error.message,
};
}

return {
ok: true,
status: input.status,
data: input.payload,
};
},
});

### Example
app.get('/ok', (_, reply) => reply.status(200, { message: 'success' }));
app.get('/accepted', (_, reply) => reply.status(202, 'accepted'));
app.get('/error', (_, reply) => reply.error(500, { message: 'something went wrong' }));
```

After adding [Fastify](https://fastify.dev/) and **feh** in your project, try this
#### API

```ts
import fastify from 'fastify';
import feh from '@zhaoworks/feh';
- `reply.error(status, { message })`
- `reply.status(status, payload)`

const server = fastify();
`format(input)` receives:

server.register(feh);
- `{ kind: 'error', status, error }`
- `{ kind: 'status', status, payload }`

server.get('/', (_, reply) => {
return reply.error(500, {
message: 'something went completely wrong >:(',
});
});
Default behavior when `format` is not provided:

server
.listen({ port: 4000 })
.then(() => console.log('Listening on http://localhost:4000/'));
```
- `reply.error(status, { message })` → `{ error: { message } }`
- `reply.status(status, payload)` → returns `payload` as-is
- `reply.status(204, payload)` → sends no body (`204 No Content`)

You can also add a custom format to your errors by registering a new error formatter.
Use `format(input)` for all custom response formatting.

```ts
server.register(feh, {
format: (status, error) => ({ error: error.message, our_fault: status === 500 })
});
### License

server.get('/', (_, reply) => {
return reply.error(500, { message: 'a cat must have bitten the wires' }); // -> { error: 'a cat must have bitten the wires', our_fault: true }
});
```
[MIT](/LICENSE)
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zhaoworks/feh",
"description": "fastify response errors.",
"version": "1.0.4",
"description": "Fastify response helper for status and error replies.",
"version": "2.0.0",
"license": "MIT",
"author": "Kauê Fraga Rodrigues <[email protected]>",
"repository": "https://github.com/zhaoworks/feh",
Expand Down
71 changes: 59 additions & 12 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,84 @@
import type { FastifyInstance } from 'fastify';
import type { FastifyInstance, FastifyReply } from 'fastify';
import fp from 'fastify-plugin';

interface ErrorOptions {
message: string;
}

type FormatInput =
| { kind: 'error'; status: number; error: ErrorOptions }
| { kind: 'status'; status: number; payload: unknown };

interface PluginOptions {
format?: (status: number, error: ErrorOptions) => unknown;
/**
* Unified formatter for both `reply.error(...)` and `reply.status(code, payload)`.
*/
format?: (input: FormatInput) => unknown;
}

type StatusReplyMethod = FastifyReply['status'];

/*
* Obs.: this declaration is necessary for including the method `error`
* as a valid method of FastifyReply.
* Obs.: this declaration is necessary for including the methods
* as valid methods of FastifyReply.
*/
declare module 'fastify' {
interface FastifyReply {
error: (status: number, options: ErrorOptions) => FastifyReply;
status(code: number): FastifyReply;
status(code: number, payload: unknown): FastifyReply;
}
}

const DEFAULT_ERROR_FORMATTER = (_: number, error: ErrorOptions) => ({ error });
const DEFAULT_STATUS_FORMATTER = (_: number, payload: unknown) => payload;

function formatErrorPayload(status: number, error: ErrorOptions, options: PluginOptions) {
if (typeof options.format !== 'undefined') {
return options.format({ kind: 'error', status, error });
}

return DEFAULT_ERROR_FORMATTER(status, error);
}

function formatStatusPayload(status: number, payload: unknown, options: PluginOptions) {
if (typeof options.format !== 'undefined') {
return options.format({ kind: 'status', status, payload });
}

return DEFAULT_STATUS_FORMATTER(status, payload);
}

function patchStatusReplyMethod(reply: FastifyReply, options: PluginOptions) {
const originalStatus = reply.status.bind(reply) as StatusReplyMethod;

reply.status = function patchedStatus(code: number, payload?: unknown) {
const response = originalStatus(code);

if (arguments.length >= 2) {
if (code === 204) {
return response.send();
}

return response.send(formatStatusPayload(code, payload, options));
}

return response;
} as StatusReplyMethod;
}

async function plugin(fastify: FastifyInstance, options: PluginOptions) {
fastify.addHook('onRequest', (_, reply, done) => {
patchStatusReplyMethod(reply, options);
done();
});

async function plugin(fastify: FastifyInstance, plugin: PluginOptions) {
/*
* Obs.: the anonymous function must not be an arrow function,
* probably because of the way the Fastify use `this` context.
* probably because of the way the Fastify uses `this` context.
*/
fastify.decorateReply('error', function (this, status, options) {
return this.status(status).send(
typeof plugin.format === 'undefined'
? DEFAULT_ERROR_FORMATTER(status, options)
: plugin.format(status, options),
);
fastify.decorateReply('error', function (this: FastifyReply, status: number, error: ErrorOptions) {
return this.status(status).send(formatErrorPayload(status, error, options));
});
}

Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@
"outDir": "./dist",
"esModuleInterop": true
},
"exclude": ["src/server.ts"]
"exclude": ["src/server.ts", "dist", "node_modules"]
}