Create DataPoint Express middleware
npm install data-point-express   
> Create DataPoint service with Express middleware support
- Node 8 LTS (or higher)
- Redis (Optional for development)
- Peer dependencies: data-point, Express
NOTE: Express and DataPoint are peer dependencies
``bash`
npm install --save express data-point data-point-express
A simplistic example of creating a DataPoint Express service.
`js
const express = require('express')
const Service = require('data-point-express')
const app = new express()
Service.create({
// add DataPoint entities
entities: {
'entry:hello-world': () => 'Hello World!!'
}
})
.then((service) => {
// expose DataPoint inspector
app.use(
'/api/inspect',
service.inspector()
)
// maps route: /api/hello-world to
// entityId: entry:hello-world
app.get(
'/api/hello-world',
service.mapTo('entry:hello-world')
)
// start Express server
app.listen(3000, (err) => {
console.log('DataPoint service ready!')
})
})
`
The code above should expose two paths:
- http://localhost:3000/api/inspect - DataPoint entity inspector
- http://localhost:3000/api/hello-world - should return the string Hello World!!
This method returns a Promise that resolves to a DataPoint Service Object.
`js`
Service.create({
entities: Object,
entityTypes: Object,
cache: {
localTTL: Number,
redis: Object,
isRequired: true,
prefix: String
}
}):Promise
The following table describes the properties of the options argument:
| option | type | required | description |
|:---|:---|:---|:---|
| entities | Object | yes | DataPoint entities Object. |Object
| entityTypes | | optional | Create your DataPoint custom entity types. |Number
| cache.localTTL | | optional | Value in Milliseconds of in memory TTL, by default it's set to 2000 (2 seconds) |Object
| cache.redis | | optional | Value passed to the ioredis constructor |Boolean
| cache.isRequired | | optional | Defaults to false. If true the service will throw an error when getting created. |String
| cache.prefix | | optional | Defaults to os.hostname(). In production you may be using multiple node instances and might want to instead share the prefix. |
Creates a new DataPoint Service:
`js
const express = require('express')
const Service = require('data-point-express')
const app = new express()
Service.create({
// add DataPoint entities
entities: {
'entry:HelloWorld': (input, acc) => 'Hello World!!',
'entry:Greet': (input, acc) => Hello ${acc.locals.params.person}!!
}
})
.then(service => {
// create Express routes
app.get('/api/hello-world', service.mapTo('entry:HelloWorld'))
app.get('/api/greet/:person', service.mapTo('entry:Greet'))
app.listen(3000, (err) => {
if(err) {
throw err
}
console.info('DataPoint service ready!')
})
})
.catch(error => {
console.info('Failed to Create Service')
console.error(error)
process.exit(1)
})
`
Service: http://localhost:3000/api/hello-world
Returns:
`text`
Hello World!!
Service: http://localhost:3000/api/greet/darek
Returns:
`text`
Hello darek!!
When Service.create is resolved it returns a service instance that exposes the following api:
- service.mapTo() - creates an express middleware that gets mapped to a DataPoint Entity id.
- service.router() - creates an Express.Router with DataPoint aware routes.
- service.inspector() - exposes a DataPoint Entity inspector.
Maps a DataPoint entityId to a middleware method. This method returns an Express Middleware function.
`js`
service.mapTo(entityId:String):Function
arguments:
| argument | type | description |
|:---|:---|:---|
| entityId | String | DataPoint entity Id. |
Example:
Maps path '/api/hello-world' to entityId 'entry:HelloWorld'
`js`
app.get('/api/hello-world', service.mapTo('entry:HelloWorld'))
Create DataPoint aware routes. This method returns a Express.Router Object.
`js`
service.router(routes:Object):Router
arguments:
| argument | type | description |
|:---|:---|:---|
| routes | Object | Routes Object |
Example:
Create a set of routes under the path '/api'. Notice how you can set the _http method_ on each route as well as the priority.
`js`
app.use('/api', service.router({
helloWorld: {
priority: 100,
path: '/hello-world',
method: 'GET',
middleware: 'reducer:HelloWorld'
},
addUser: {
priority: 200,
path: '/user',
method: 'POST',
middleware: 'entry:addUser'
},
deleteUser: {
priority: 300,
path: '/user',
method: 'DELETE',
middleware: 'entry:deleteUser'
}
}))
This object must follow a specific structure:
`js`
{
routeId: {
path: String,
priority: Number,
enabled: Boolean,
method: String,
middleware: Array
}
}
Each property of a route is described in the table below:
| property | type | description |
|:---|:---|:---|
| path | String | Valid Express route |Number
| priority | | Number to order the routes, since in express this order matters make sure you place these numbers correctly |Boolean
| enabled | | Enable/disable a route from being added. true by default, unless explicitly set to false |String
| method | | http method mapped to the route. Defaults to 'GET'. Available methods: 'GET', 'PUT', 'DELETE', 'POST'. |
| middleware | Array | This is the actual middleware function used for the route. For information on how to use please look at route.middleware |
Route Middleware can be written in multiple ways:
Using standard express functions, it accepts standard Express middleware methods as described in using express middleware.
Example:
`js`
{
helloWorld: {
path: '/hello/:person',
priority: 100,
middleware: (req, res) => res.send('hello ${req.params.person}!')
}
}
#### Middleware as an Entity Id
You must pass a string that points to a valid DataPoint entity id; this maps the middleware to the given entity Id. The entity's resolution becomes the result sent to the client.
Example:
`jshello ${acc.locals.params.person}!
// data point entities:
{
'entry:HelloWorld': {
value: (input, acc) =>
}
}
// routes
{
helloWorld: {
path: '/hello/:person',
priority: 100,
middleware: 'entry:HelloWorld'
}
}
`
#### Mixed middleware
You may also use a mix of functions and entity ids, for example you may want to do authentication or parameter normalization before executing a DataPoint entity.
One caveat is that you may only pass one entity id and it must be the last middleware otherwise it throws an error.
Example:
`jshello ${acc.locals.params.person}!
// data point entities:
{
'entry:HelloWorld': {
value: (input, acc) =>
}
}
// routes
{
helloWorldProtected: {
path: '/hello/:person',
priority: 100,
middleware: [requireAuthentication, 'entry:HelloWorld']
},
badRoute: {
path: '/hello/:person',
priority: 100,
// this is not allowed, entity
// must be at the end.
middleware: ['entry:HelloWorld', requireAuthentication]
}
}
`
This service comes with a DataPoint entity _inspector_ web interface. To mount you must pass the result of service.inspector() to Express app.use. Make sure you specify a _path_ where to mount the inspector.
IMPORTANT: for security do not expose this middleware in production environments.
Basic implementation example:
`js
const express = require('express')
const Service = require('data-point-express')
const app = new express()
Service.create({
// DataPoint entities
entities: {
'entry:hello-world': (input, acc) => 'Hello World!!'
}
})
.then((service) => {
// expose DataPoint inspector
app.use('/api/inspect', service.inspector())
app.listen(3000)
})
.catch(error => {
console.info('Failed to Create Service')
console.error(error)
process.exit(1)
})
`
Working example at /examples/inspector-demo.js
When an entity executes through a DataPoint Middleware, it appends some useful information to the Accumulator.locals property. These values are persistent across the execution of the request.
| Property | Type | Description |
|---|---|---|
| routeRequestType | String | Type of route request being made: 'api', 'rdom', 'html' |Express.RequestObject
| req | | Reference to current Express req |String
| url | | Node's message.url |Object
| query | | Reference to current Express req.query |Object
| params | | Reference to current Express req.params |Object
| queryParams | | This is a defaults merge of req.query with req.params |Object
| paramsQuery | | This is a defaults merge of req.params with req.query |
Example:
DataPoint Reducer that prints out the acc.locals.url to the console.
`js``
const reducer = (input, acc) => {
console.log('url that originated this call:', acc.locals.url)
return input
}
Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.
This project is licensed under the Apache License Version 2.0 - see the LICENSE file for details