Create DataPoint service with Express middleware support
- Node 10 LTS (or higher)
- Redis (Optional for development)
- Peer dependencies: data-point, Express
NOTE: Express and DataPoint are peer dependencies
npm install --save express data-point data-point-express
A simplistic example of creating a DataPoint Express service.
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.
Service.create({
entities: Object,
entityTypes: Object,
cache: {
localTTL: Number,
redis: Object,
isRequired: true,
prefix: String
}
}):Promise<Object>
The following table describes the properties of the options
argument:
option | type | required | description |
---|---|---|---|
entities | Object |
yes | DataPoint entities Object. |
entityTypes | Object |
optional | Create your DataPoint custom entity types. |
cache.localTTL | Number |
optional | Value in Milliseconds of in memory TTL, by default it's set to 2000 (2 seconds) |
cache.redis | Object |
optional | Value passed to the ioredis constructor |
cache.isRequired | Boolean |
optional | Defaults to false . If true the service will throw an error when getting created. |
cache.prefix | String |
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:
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:
Hello World!!
Service: http://localhost:3000/api/greet/darek
Returns:
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.
service.mapTo(entityId:String):Function
arguments:
argument | type | description |
---|---|---|
entityId | String |
DataPoint entity Id. |
Example:
Maps path '/api/hello-world'
to entityId 'entry:HelloWorld'
app.get('/api/hello-world', service.mapTo('entry:HelloWorld'))
Create DataPoint aware routes. This method returns a Express.Router Object.
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.
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:
{
routeId: {
path: String,
priority: Number,
enabled: Boolean,
method: String,
middleware: Array<Function|String>|Function|String
}
}
Each property of a route is described in the table below:
property | type | description |
---|---|---|
path | String |
Valid Express route |
priority | Number |
Number to order the routes, since in express this order matters make sure you place these numbers correctly |
enabled | Boolean |
Enable/disable a route from being added. true by default, unless explicitly set to false |
method | String |
http method mapped to the route. Defaults to 'GET'. Available methods: 'GET', 'PUT', 'DELETE', 'POST'. |
middleware | Array<Function|String> |
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:
{
helloWorld: {
path: '/hello/:person',
priority: 100,
middleware: (req, res) => res.send('hello ${req.params.person}!')
}
}
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:
// data point entities:
{
'entry:HelloWorld': {
value: (input, acc) => `hello ${acc.locals.params.person}!`
}
}
// routes
{
helloWorld: {
path: '/hello/:person',
priority: 100,
middleware: 'entry:HelloWorld'
}
}
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:
// data point entities:
{
'entry:HelloWorld': {
value: (input, acc) => `hello ${acc.locals.params.person}!`
}
}
// 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:
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' |
req | Express.RequestObject |
Reference to current Express req |
url | String |
Node's message.url |
query | Object |
Reference to current Express req.query |
params | Object |
Reference to current Express req.params |
queryParams | Object |
This is a defaults merge of req.query with req.params |
paramsQuery | Object |
This is a defaults merge of req.params with req.query |
Example:
DataPoint Reducer that prints out the acc.locals.url
to the console.
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