2.14.3 • Published 3 years ago

@pubcore/node-composition v2.14.3

Weekly downloads
52
License
MIT
Repository
github
Last release
3 years ago

Express middleware to map requests to functions of components

Within the terminology of HTTP we do have requests and responses. Given a response is just a function of a request, this package provide the option to configure such functional mappings.

In order to structure such functions the words "component" and "composition" are used in this way:
A component is a set of functions.
A composition is a set of components.
A requested domain (on a specific port) is mapped to a composition.

The purpose of this package is to support such a structure by configuration.

Prerequisites

  • nodejs
  • expressjs

Auto invalidation of require-cache in development mode

Since nodejs caches required (imported) packages, changes within a file does not affect a running express server, it needs to be restared. This is expensive for continuously change-save-and-review cycles web-developers love.
This package implements automatic invalidation of require-cache per component-package level. If a script file changes, all modules of corresponding component-package gets invalidated.

Example composition

Let's assume we compose a todo-list component together with a calendar component. Composition's package directory consists of:

config.js
package.json
server.js

config.js (map request to components based on context-path)

//composition config
module.exports = {
	//a composition is a set of components ...
	components:{
		'@yourOrg/todo-list':{
			public: true,
			context_path: '/todo'
		},
		'@yourOrg/calendar':{
			public: true,
			context_path: '/calendar'
		}
	}
}

npm's package.json

{
	"name": "@yourScope/example-composition",
	"version": "1.0.0",
	"main": "server.js",
	"dependencies": {
		"express": "^4.17.1",
		"@pubcore/node-composition": "^2.8.0",
		"@yourScope/todo-list": "^0.1.0",
		"@yourScope/calendar": "^0.1.0",
	}
}

server.js

const
	express = require('express'),
	app = express(),
	composition = require('@pubcore/node-composition').default,
	config = require('./config.js')

app.use('/', composition(config, require))
Configuration options
module.exports = {
	componentDefault:{
		//if true, login (next option) is required
		public: false
		//login middleware, required, if component is not public
		login: (req, res, next) => {next()},

		//optional, build arbitrary data added to req.resources
		resources: async (req) => {}

		//optional error handler middleware
		error: (err, req, res, next) => {},

		//optional urlencoder middleware
		//see http://expressjs.com/de/api.html#express.urlencoded
		urlencoded: {extended: true}
	},
	components: {
		"@company/component-one":{
			//component ID
			id: "@company/component-one"

			//context path used for express Router
			context_path: "/basePathOfComponentOne"

			//optional to define (overwrite) defaults, see "componentDefault" ...
		}
	},
	accesscontrol:{
		//see https://developer.mozilla.org/en-US/docs/Glossary/CORS
		//CORS headers are responded for requests send from sites of following
		allowedOrigins: ["https://foo.net"],

		//see https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
		contentSecurityPolicy: "default-src 'self' data:; script-src 'self' font-src https:; style-src 'unsafe-inline' https:;"

		//Cross Site Request Forgery (SCRF) Protection by Double Submit Cookie Pattern
		//see https://github.com/expressjs/csurf
		//Secure config to store token-secret by cookie:
		csrfProtection: {key:'__Host-Csrf', secure:true, sameSite:'lax', httpOnly:true}
	},
	//optional
	options:{
		//optional
		requestJsonLimit: '2mb' //default 100kb
	}
}

Example component

A component package exports the mapping of URI sub-path to a express middleware function:

  1. src/index.js
//import express middleware functions
import list from './lib/getList'
import addItem from './lib/addItem'
export default {
	public:true,
	http: [
		{
			routePath: '/list',
			map: list,
			method: 'GET',
			accepted: ['text/plain']
		},
		{
			routePath: '/list',
			map: addItem,
			method: 'POST',
			accepted: ['application/json'],
			urlencoded: {extended: true} //optional, see above
		},
	]
}
  1. Optional, public "htdocs" directory contain some static files (e.g. imgage, css, js)
    htdocs/

Features test output

compose components by configuration
	✓ serves requests for some configured component functions
	✓ serves requests for configured second "component-two"
	✓ requires a login middleware function, if component is private
	✓ reloads modules in development mode, if corresponding js file changed (131ms)
	✓ reloads modules in development mode, if corresponding js file changed (113ms)
	✓ supports CORS - CrossOriginResourceSharing by config (allowedOrigins)
	✓ sends CSP - Content Security Policy header, if configured
	✓ offers req.cookies and req.cookiesByArray object, if there are cookies

compose, if validation of a component fails
	✓ should skip corresponding component and response with status 500


compose, if environment is in PRODUCTION mode
	✓ does not load changed module’s script file and shows same result as before

component router
	✓ routes requests based on component config
	✓ support different methods for same path
	✓ checks accept header
	✓ checks http method
	✓ checks http method before login
dev-mode: synchronous reload for @scope-a/component-one
dev-mode: synchronous reload for ./js/index
	✓ responses "not found" for other paths
	✓ requires authentication, if component or function is not public
	✓ invokes a "login" promise for private resources
	✓ removes passwort after login, for security reasons
	✓ invokes a "resources" promise, if configured
	✓ invokes a "resources" promise, if configured and use config data
	✓ it catches up failed "resources" promise
	✓ supports error handler middleware
	✓ supports Content-Type: application/x-www-form-urlencoded
	✓ supports to turn off "urlencoded" middleware endpoint specific
	Cross Site Request Forgery protection
		✓ response a session-secret cookie, based on config)
		✓ serves 403, if token is invalid
		✓ accepts valid token send by form hidden field "_csrf"

References

CQRS protection "__Host-" cookie prefix

2.14.3

3 years ago

2.14.2

3 years ago

2.14.1

3 years ago

2.14.0

3 years ago

2.13.0

3 years ago

2.12.0

3 years ago

2.11.2

4 years ago

2.11.0

4 years ago

2.11.1

4 years ago

2.10.1

4 years ago

2.10.0

4 years ago

2.9.0

4 years ago

2.8.1

4 years ago

2.8.0

5 years ago

2.7.1

5 years ago

2.7.0

5 years ago

2.6.2

5 years ago

2.6.1

5 years ago

2.6.0

5 years ago

2.5.0

5 years ago

2.4.0

5 years ago

2.3.0

5 years ago

2.2.0

5 years ago

2.1.5

5 years ago

2.1.4

5 years ago

2.1.3

5 years ago

2.1.2

5 years ago

2.1.1

5 years ago

2.1.0

5 years ago

2.0.0

5 years ago

1.8.0

5 years ago

1.7.0

5 years ago

1.6.4

5 years ago

1.6.3

5 years ago

1.6.2

6 years ago

1.6.1

6 years ago

1.6.0

6 years ago

1.5.2

6 years ago

1.5.1

6 years ago

1.5.0

6 years ago

1.4.1

6 years ago

1.4.0

6 years ago

1.3.0

6 years ago

1.2.2

6 years ago

1.2.1

6 years ago

1.2.0

6 years ago

1.1.0

6 years ago

1.0.0

6 years ago