Skip to content

Commit

Permalink
refactor: Refer to the response data as the "body" instead of "payload"
Browse files Browse the repository at this point in the history
This aligns with the nomenclature in Express and Koa and is also shorter (which is nice for onSend hooks).
  • Loading branch information
nwoltman committed Jul 3, 2019
1 parent 56a6e47 commit 7c0a859
Show file tree
Hide file tree
Showing 17 changed files with 159 additions and 145 deletions.
2 changes: 1 addition & 1 deletion benchmarks/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ app
})

app
.addHook('onSend', (req, res, payload, next) => {
.addHook('onSend', (req, res, body, next) => {
next()
})

Expand Down
2 changes: 1 addition & 1 deletion docs/Getting-Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ app.addHook('onRequest', (req, res, next) => {
next();
});

app.addHook('onSend', (req, res, payload, next) => {
app.addHook('onSend', (req, res, body, next) => {
res.sendStartTime = Date.now();
next();
});
Expand Down
34 changes: 17 additions & 17 deletions docs/Hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,54 +133,54 @@ app.addHook('onRequest', async (req, res) => {

```js
// Callback version
app.addHook('onSend', (req, res, payload, next) => {
app.addHook('onSend', (req, res, body, next) => {
// Handle onSend
next();
});

// Async version
app.addHook('onSend', async (req, res, payload) => {
app.addHook('onSend', async (req, res, body) => {
// Handle onSend
});
```

+ `req` - Medley [Request](Request.md) object.
+ `res` - Medley [Response](Response.md) object.
+ `payload` - The serialized payload.
+ `next([error [, payload]])` - Function to continue to the next hook and optionally update the payload.
+ `body` - The serialized response body.
+ `next([error [, body]])` - Function to continue to the next hook and optionally update the body.

The `onSend` hooks are run right after `res.send()` is called and the payload
The `onSend` hooks are run right after `res.send()` is called and the body
has been serialized. They provide an opportunity to save application state
(e.g. sessions) and set extra headers before the response is sent.

### Modifying the Payload
### Modifying the Response Body

It is possible to modify the payload before it is sent by passing the modified
payload as the second argument to `next()`. The payload may only be changed
It is possible to modify the response body before it is sent by passing the modified
body as the second argument to `next()`. The body may only be changed
to a `string`, a `Buffer`, a `stream`, or `null`.

```js
app.get('/', (req, res) => {
res.send({ hello: 'world' });
});

app.addHook('onSend', (req, res, payload, next) => {
console.log(payload); // '{"hello":"world"}'
const newPayload = Buffer.from(payload);
next(null, newPayload);
app.addHook('onSend', (req, res, body, next) => {
console.log(body); // '{"hello":"world"}'
const newBody = Buffer.from(body);
next(null, newBody);
});
```

To modify the payload using an `async` hook, return the new payload.
To modify the body using an `async` hook, return the new body.

```js
app.addHook('onSend', async (req, res, payload) => {
return Buffer.from(payload);
app.addHook('onSend', async (req, res, body) => {
return Buffer.from(body);
});
```

Changing the payload is mainly intended to be used for encoding the payload
(e.g. compressing it) or clearing the payload by setting it to `null` for a
Changing the response body is mainly intended to be used for encoding the
body (e.g. compressing it) or setting the body to `null` for a
`304 Not Modified` response.

### `onSend` Hooks and Errors
Expand Down
22 changes: 11 additions & 11 deletions docs/Lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Incoming Request
└─▶ Route Handler
|
└─▶ Serialize Payload
└─▶ Serialize Body
└─▶ onSend Hooks
Expand All @@ -23,7 +23,7 @@ Incoming Request
1. [Routing](#routing)
1. [onRequest Hooks](#onrequest-hooks)
1. [Route Handler](#route-handler)
1. [Serialize Payload](#serialize-payload)
1. [Serialize Body](#serialize-body)
1. [onSend Hooks](#onsend-hooks)
1. [Send Response](#send-response)
1. [onFinished Hooks](#onfinished-hooks)
Expand All @@ -46,7 +46,7 @@ app.addHook('onRequest', (req, res, next) => {
});
```

These hooks may send an early response with `res.send()`. If a hook does this, the rest of the hooks will be skipped and the lifecycle will go straight to the [*Serialize Payload*](#serialize-payload) step.
These hooks may send an early response with `res.send()`. If a hook does this, the rest of the hooks will be skipped and the lifecycle will go straight to the [*Serialize Body*](#serialize-body) step.

#### Route-level `preHandler`

Expand All @@ -66,11 +66,11 @@ app.get('/', {

## Route Handler

This is the main handler for the route. The route handler sends the response payload.
This is the main function that sends the response.

```js
app.get('/', (req, res) => {
res.send('payload');
res.send('body');
});
```

Expand All @@ -80,26 +80,26 @@ See the [`Routes` documentation](Routes.md) for more information on route handle

If the request URL does not match any routes, the [`notFoundHandler`](Medley.md#notfoundhandler) is invoked. Global hooks **are** run before/after this handler.

## Serialize Payload
## Serialize Body

In this step, the payload that was passed to `res.send()` is serialized (if it needs to be) and an appropriate `Content-Type` for the payload is set (if one was not already set).
In this step, the body that was passed to `res.send()` is serialized (if it needs to be) and an appropriate `Content-Type` is set (if one was not already set).

See the [`res.send()`](Response.md#send) and [Serialization](Serialization.md) documentation for more information.

## `onSend` Hooks

[`onSend` hooks](Hooks.md#onSend-hook) are run after the payload has been serialized and before the payload is sent to the client.
[`onSend` hooks](Hooks.md#onSend-hook) are run after the body has been serialized and before the response is sent to the client.

```js
app.addHook('onSend', (req, res, payload, next) => {
app.addHook('onSend', (req, res, body, next) => {
// Do something, like save the session state
next();
});
```

## Send Response

The serialized payload is sent to the client. Medley handles this step automatically.
The response is sent to the client. Medley handles this step automatically.

## `onFinished` Hooks

Expand Down Expand Up @@ -131,7 +131,7 @@ Routing
│ ⯆
└─▶ Route Handler ╌╌╌╌ (error) ╌╌╌▶ onError Hooks
| ┆
└─▶ Serialize Payload ◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
└─▶ Serialize Body ◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
└─▶ onSend Hooks
Expand Down
16 changes: 8 additions & 8 deletions docs/Response.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ app.get('/user/:id', function(req, res) {
+ [`.hasHeader(field)`](#hasHeader)
+ [`.redirect([statusCode,] url)`](#redirect)
+ [`.removeHeader(field)`](#removeHeader)
+ [`.send([payload])`](#send)
+ [`.send([body])`](#send)
+ [`.setHeader(field [, value])`](#setHeader)
+ [`.status(statusCode)`](#status)
+ [`.type(contentType)`](#type)
Expand Down Expand Up @@ -222,16 +222,16 @@ res.removeHeader('content-type')
```

<a id="send"></a>
### `res.send([payload])`
### `res.send([body])`

Sends the payload to respond to the request.
Sends the HTTP response.

`.send()` handles payloads differently based on their type. The behavior for each type
`.send()` handles the `body` differently based on its type. The behavior for each type
is described below.

#### No Value / `null` / `undefined`
#### `null` / `undefined`

Sends a response with an empty body.
Sends a response without a body.

```js
res.send()
Expand Down Expand Up @@ -270,10 +270,10 @@ app.get('/stream', (req, res) => {

#### JSON

If the payload is not one of the previous types, it will be JSON-serialized.
If the `body` is not one of the previous types, it will be JSON-serialized.
If not already set, the `Content-Type` header will be set to `'application/json'`.

JSON payloads are serialized with [`compile-json-stringify`](https://www.npmjs.com/package/compile-json-stringify)
The `body` will be serialized with [`compile-json-stringify`](https://www.npmjs.com/package/compile-json-stringify)
if a response schema was set, otherwise `JSON.stringify()` is used.

```js
Expand Down
36 changes: 26 additions & 10 deletions docs/Serialization.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
# Serialization

When sending a JSON response, it is serialized with `JSON.stringify()` by
default. However, a response schema can be set to enable the payload to be
serialized with [`compile-json-stringify`](https://www.npmjs.com/package/compile-json-stringify)
instead. `compile-json-stringify` will stringify the payload 2-8x faster than
`JSON.stringify()` and it will exclude any properties that are not included in
the schema (which can prevent accidental disclosure of sensitive information,
although it is not recommended to use this as the primary method of preventing
data leaks).

**Example:**
Routes can define a `responseSchema` to optimize serialization JSON responses.
The `responseSchema` is compiled by [`compile-json-stringify`](https://www.npmjs.com/package/compile-json-stringify)
which will stringify the response body 2-8x faster than `JSON.stringify()`.

```js
const responseSchema = {
Expand Down Expand Up @@ -58,6 +51,29 @@ app.post('/info', { responseSchema }, (req, res) => {
});
```

The compiled `stringify` function will also exclude any properties that are not
included in the schema (which can prevent accidental disclosure of sensitive
information, although it is not recommended to use this as the primary method
of preventing data leaks).

```js
const responseSchema = {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
};

app.get('/', { responseSchema }, (req, res) => {
res.send({
hello: 'world',
greetings: 'universe', // This property will be excluded from the response
});
});
```

For more information on how to define a response schema, see the
[`compile-json-stringify` documentation](https://github.com/nwoltman/compile-json-stringify).

Expand Down
16 changes: 8 additions & 8 deletions lib/HookRunners.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,30 @@ function runOnRequestHooks(hooks, req, res, done) {
})(undefined)
}

function runOnSendHooks(res, payload, done) {
function runOnSendHooks(res, body, done) {
const hooks = res._route.onSendHooks
const req = res.request
var handleResolve = null
var i = 0;

(function next(err, newPayload) {
(function next(err, newBody) {
if (err) {
res._route.onErrorSending(err, res)
} else if (newPayload !== undefined) {
payload = newPayload
} else if (newBody !== undefined) {
body = newBody
}

if (i === hooks.length) {
done(res, payload)
done(res, body)
return
}

const result = hooks[i++](req, res, payload, next)
const result = hooks[i++](req, res, body, next)

if (result && typeof result.then === 'function') {
if (handleResolve === null) {
handleResolve = function(nextPayload) {
next(undefined, nextPayload)
handleResolve = function(value) {
next(undefined, value)
}
}
result.then(handleResolve, next)
Expand Down
Loading

0 comments on commit 7c0a859

Please sign in to comment.