5.1.1 • Published 3 months ago

data-point-express v5.1.1

Weekly downloads
10
License
Apache-2.0
Repository
github
Last release
3 months ago

DataPoint Express

Build Status codecov Coverage Status All Contributors

Create DataPoint service with Express middleware support

Requirements

Install

NOTE: Express and DataPoint are peer dependencies

npm install --save express data-point data-point-express 

Quick start

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:

Create a DataPoint Service Object

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:

optiontyperequireddescription
entitiesObjectyesDataPoint entities Object.
entityTypesObjectoptionalCreate your DataPoint custom entity types.
cache.localTTLNumberoptionalValue in Milliseconds of in memory TTL, by default it's set to 2000 (2 seconds)
cache.redisObjectoptionalValue passed to the ioredis constructor
cache.isRequiredBooleanoptionalDefaults to false. If true the service will throw an error when getting created.
cache.prefixStringoptionalDefaults to os.hostname(). In production you may be using multiple node instances and might want to instead share the prefix.

Example

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!!

DataPoint Service Object

When Service.create is resolved it returns a service instance that exposes the following api:

service.mapTo()

Maps a DataPoint entityId to a middleware method. This method returns an Express Middleware function.

service.mapTo(entityId:String):Function

arguments:

argumenttypedescription
entityIdStringDataPoint entity Id.

Example:

Maps path '/api/hello-world' to entityId 'entry:HelloWorld'

app.get('/api/hello-world', service.mapTo('entry:HelloWorld'))

service.router()

Create DataPoint aware routes. This method returns a Express.Router Object.

service.router(routes:Object):Router

arguments:

argumenttypedescription
routesObjectRoutes 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: 'transform:HelloWorld'
  },
  addUser: {
    priority: 200,
    path: '/user',
    method: 'POST',
    middleware: 'entry:addUser'
  },
  deleteUser: {
    priority: 300,
    path: '/user',
    method: 'DELETE',
    middleware: 'entry:deleteUser'
  }
}))

Routes Object

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:

propertytypedescription
pathStringValid Express route
priorityNumberNumber to order the routes, since in express this order matters make sure you place these numbers correctly
enabledBooleanEnable/disable a route from being added. true by default, unless explicitly set to false
methodStringhttp method mapped to the route. Defaults to 'GET'. Available methods: 'GET', 'PUT', 'DELETE', 'POST'.
middlewareArray<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

Route Middleware can be written in multiple ways:

Middleware as a Function

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}!')
  }
}

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:

// data point entities:
{
  'entry:HelloWorld': {
    value: (input, acc) => `hello ${acc.locals.params.person}!`
  }
}

// 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:

// 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]
  }
}

service.inspector()

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

Accumulator.locals

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.

PropertyTypeDescription
routeRequestTypeStringType of route request being made: 'api', 'rdom', 'html'
reqExpress.RequestObjectReference to current Express req
urlStringNode's message.url
queryObjectReference to current Express req.query
paramsObjectReference to current Express req.params
queryParamsObjectThis is a defaults merge of req.query with req.params
paramsQueryObjectThis 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
}

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

License

This project is licensed under the Apache License Version 2.0 - see the LICENSE file for details

5.0.4-alpha.0

3 months ago

5.1.1

4 years ago

5.1.0

4 years ago

5.0.3

5 years ago

5.0.2

5 years ago

5.0.1

5 years ago

5.0.0

5 years ago

4.1.5

5 years ago

4.1.4

5 years ago

4.1.4-1

5 years ago

4.1.4-0

5 years ago

4.1.3

5 years ago

4.1.3-0

6 years ago

4.1.2

6 years ago

4.1.1

6 years ago

4.1.0

6 years ago

4.0.0

6 years ago

3.2.0

6 years ago

3.1.0

6 years ago

3.0.0

6 years ago

2.0.0

6 years ago

1.7.0

6 years ago

1.6.3

6 years ago

1.5.0

6 years ago