Skip to content

Rework README, add info from docs site #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 5, 2017
Merged
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
172 changes: 139 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,106 @@
# loopback-context

Current context for LoopBack applications, based on
cls-hooked.
Current context for LoopBack applications, based on cls-hooked.

## USAGE WARNING
## WARNING

**Only if you use this package, it's recommended to not run your app using `slc run` or `node .`**
**`cls-hooked` module uses undocumented `AsyncWrap` API that was introduced to Node.js relatively recently. While this new API seems to be more reliable than the old `async-listener` used by `continuation-local-storage`, there are still cases where the context (local storage) is not preserved correctly. Please consider this risk before using loopback-context.**

Run using (recommended):
### Known issues

`node -r cls-hooked .`
- [when](https://www.npmjs.com/package/when), a popular Promise
implementation, breaks context propagation. Please consider using the
built-in `Promise` implementation provided by Node.js or
[Bluebird](https://www.npmjs.com/package/bluebird) instead.

This uses the `-r` option in order to require `cls-hooked` before your app (see warnings below for more info).
Discussion: https://github.com/strongloop/loopback-context/issues/17

If you wish to use `strong-supervisor`, you would need to pass node options to `slc run`, which currently has issues, according to [strong-supervisor#56](https://github.com/strongloop/strong-supervisor/issues/56).
In general, any module that implements a custom task queue or a connection pool
is prone to break context storage. This is an inherent problem of continuation
local storage that needs to be fixed at lower level - first in Node.js core
and then in modules implementing task queues and connection pools.

A less reliable, less recommended way, which instead should be compatible with `strong-supervisor`, would be to add this Javascript line at the first line of your app, and no later than that (**the order of require statements matters**):
## Installation

`require('cls-hooked')`
```
$ npm install --save loopback-context cls-hooked
```

Warning: to rely on the order of `require` statements is error-prone.
Make sure you are running on a Node.js version supported by this module
(`^4.5`, `^5.10` or `^6.0`). When installing, check the output of `npm install`
and make sure there are no `engine` related warnings.

## Usage

## INSTALL WARNING
### Setup cls-hooked

**Only if you use this package, do NOT install your app using `npm install`.**
To minimize the likelihood of loosing context in your application, you should
ensure that `cls-hooked` is loaded as the first module of your application, so
that it can wrap certain Node.js APIs before any other modules start using these
APIs.

Install using:
Our recommended approach is to add `-r cls-hooked` to node's list of
arguments when starting your LoopBack application.

```
npm config set engine-strict true
npm install
$ node -r cls-hooked .
```

This keeps you from using Node < v4.5.
If you are using a process manager like `strong-pm` or `pm2`, then consult
their documentation whether it's possible to configure the arguments used to
spawn worker processes. Note that `slc run` does not support this feature yet,
see [strong-supervisor#56](https://github.com/strongloop/strong-supervisor/issues/56).

## WARNING
Alternatively, you can add the following line as the first line of your main
application file:

**We recommend AGAINST using the loopback-context module until there is a stable solution to the issue below!**
```js
require('cls-hooked');
```

The module node-continuation-local-storage is known to have many problems,
see e.g. [issue #59](https://github.com/othiym23/node-continuation-local-storage/issues/59).
As a result, loopback-context does not work in many situations, as can be
seen from issues reported in LoopBack's
[issue tracker](https://github.com/strongloop/loopback/issues?utf8=%E2%9C%93&q=is%3Aissue%20getCurrentcontext).
This approach should be compatible with all process managers, including
`strong-pm`. However, we feel that relying on the order of `require` statements
is error-prone.

The new alternative
[cls-hooked](https://github.com/Jeff-Lewis/cls-hooked) is known to possibly inherit these problems if it's not imported before everything else, that's why you are required to follow the advice above if using this.

## Usage
### Configure context propagation

To setup your LoopBack application to create a new context for each incoming
HTTP request, configure `per-context` middleware in your
`server/middleware.json` as follows:


```json
{
"initial": {
"loopback-context#per-request": {
}
}
}
```

1) Add `per-request` middleware to your
`server/middleware-config.json`:
**IMPORTANT: By default, the HTTP req/res objects are not set onto the current context. You
need to set `enableHttpContext` to true to enable automatic population
of req/res objects.**

```json
{
"initial": {
"loopback-context#per-request": {
"params": {
"enableHttpContext": true
}
}
}
}
```

2) Then you can access the context from your code:
### Use the current context

Once you’ve enabled context propagation, you can access the current context
object using `LoopBackContext.getCurrentContext()`. The context will be
available in middleware (if it is loaded after the context middleware),
remoting hooks, model hooks, and custom methods.

```js
var LoopBackContext = require('loopback-context');
Expand All @@ -75,6 +114,73 @@ MyModel.myMethod = function(cb) {
});
```

See the official LoopBack
[documentation](https://docs.strongloop.com/display/APIC/Using+current+context)
for more details.
### Use current authenticated user in remote methods

In advanced use cases, for example when you want to add custom middleware, you
have to add the context middleware at the right position in the middleware
chain (before the middleware that depends on
`LoopBackContext.getCurrentContext`).

**IMPORTANT: `LoopBackContext.perRequest()` detects the situation when it is
invoked multiple times on the same request and returns immediately in
subsequent runs.**

Here is a snippet using a middleware function to place the currently
authenticated user into the context so that remote methods may use it:

**server/middleware/store-current-user.js**
```js
module.exports = function(options) {
return function storeCurrentUser(req, res, next) {
if (!req.accessToken) {
return next();
}

app.models.UserModel.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
var loopbackContext = LoopBackContext.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
next();
});
};
};
```

**server/middleware.json**
```json
{
"initial": {
"loopback-context#per-request": {}
},
"auth": {
"loopback#token": {}
},
"auth:after": {
"./middleware/set-current-user": {}
}
}
```

**common/models/YourModel.json**
```js
var LoopBackContext = require('loopback-context');
module.exports = function(YourModel) {
...
//remote method
YourModel.someRemoteMethod = function(arg1, arg2, cb) {
var ctx = LoopBackContext.getCurrentContext();
var currentUser = ctx && ctx.get('currentUser');
console.log('currentUser.username: ', currentUser.username); // voila!
...
cb(null);
};
...
};
```