Boilerplate middleware for Node projects in MyCareersFuture.
- [x] express compatible server
- [x] cookie handling
- [x] json body data handling ("application/json")
- [x] form body data handling ("application/x-www-form-urlencoded")
- [x] basic http header security
- [x] content security policy
- [x] cross-origin-resource-sharing support
- [x] application metrics
- [x] server request logging
- [x] distributed tracing
- [ ] endpoint:
/healthz
- [ ] endpoint protection for
/healthz
- [ ] endpoint:
/readyz
- [ ] endpoint protection for
/readyz
- [x] endpoint:
/metrics
- [ ] endpoint protection for
/metrics
Install it via npm
or yarn
:
npm i @mcf/server-boilerplate-middleware
# or
yarn add @mcf/server-boilerplate-middleware
import {createServer} from '@mcf/server-boilerplate-middleware';
const server = createServer();
// ...
The returned server is an Express server with the following additional APIs.
Options are passed into the constructor function to create the server
import {createServer} from '@mcf/server-boilerplate-middleware';
const {server} = createServer({
...options,
});
Type | Default | Example |
---|---|---|
Boolean |
true |
serverBoilerplate({enableCookieParser: true}) |
Type | Default | Example |
---|---|---|
Boolean |
true |
serverBoilerplate({enableCompression: true}) |
Type | Default | Example |
---|---|---|
Boolean |
true |
serverBoilerplate({enableCSP: true}) |
Type | Default | Example |
---|---|---|
Boolean |
true |
serverBoilerplate({enableCORS: true}) |
Type | Default | Example |
---|---|---|
Boolean |
true |
serverBoilerplate({enableHttpHeadersSecurity: true}) |
Type | Default | Example |
---|---|---|
Boolean |
true |
serverBoilerplate({enableMetrics: true}) |
Type | Default | Example |
---|---|---|
Boolean |
true |
serverBoilerplate({enableSerializer: true}) |
Type | Default | Example |
---|---|---|
Boolean |
true |
serverBoilerplate({enableServerLogging: true}) |
Type | Default | Example |
---|---|---|
Boolean |
true |
serverBoilerplate({enableXray: true}) |
This configuration is only relevant if the
enableCompression
parameter was not set tofalse
Key | Type | Notes | Defaults To |
---|---|---|---|
chunkSize |
Number |
size in bytes of chunk | 16384 |
level |
Number |
0-9 - see https://www.npmjs.com/package/compression for more information | 9 |
threshold |
Number |
minimum size in bytes before compression kicks in | 102400 |
Defaults to:
const conpressionOptions = {
chunkSize: 16 * 1024, // 16kb
level: 9,
threshold: 300 * 1024, // 300kb
}
This option is only relevant if the
enableCSP
flag is not set tofalse
.
Key | Type | Notes | Defaults To |
---|---|---|---|
childSrc |
Array<String> |
populates the child-src value of the CSP header | ['\'none\''] |
connectSrc |
Array<String> |
populates the connect-src value of the CSP header | ['\'none\''] |
defaultSrc |
Array<String> |
populates the default-src value of the CSP header | ['\'none\''] |
fontSrc |
Array<String> |
populates the font-src value of the CSP header | ['\'none\''] |
imgSrc |
Array<String> |
populates the img-src value of the CSP header | ['\'none\''] |
scriptSrc |
Array<String> |
populates the script-src value of the CSP header | ['\'none\''] |
styleSrc |
Array<String> |
populates the style-src value of the CSP header | ['\'none\''] |
frameAncestors |
Array<String> |
populates the frame-ancestors value of the CSP header | ['\'none\''] |
reportUri |
String |
populates the report-uri value of the CSP header | '/csp-report' |
Defaults to:
const cspOptions = {
childSrc: ['\'none\''],
connectSrc: ['\'none\''],
defaultSrc: ['\'none\''],
fontSrc: ['\'none\''],
imgSrc: ['\'none\''],
scriptSrc: ['\'none\''],
styleSrc: ['\'none\''],
frameAncestors: ['\'none\''],
reportUri: '/csp-report',
}
The above configuration produces the following CSP:
"child-src 'none'; connect-src 'none'; default-src 'none'; font-src 'none'; img-src 'none'; script-src 'none'; style-src 'none'; frame-ancestors 'none'; report-uri /csp-report"
This configuration is only relevant if the
enableCORS
parameter was not set tofalse
Key | Type | Notes | Defaults To |
---|---|---|---|
allowedHeaders |
Array<String> |
provides the Access-Control-Allow-Headers header value | [] |
allowedMethods |
Array<String> |
provides the Access-Control-Allow-Methods header value | ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'] |
allowedOrigins |
Array<String> |
provides the Access-Control-Allow-Origins header value | [] |
credentials |
Boolean |
provides the Access-Control-Allow-Credentials header value | true |
preflightContinue |
Boolean |
decides whether to pass the request on or respond with 204 | false |
Defaults to:
const corsOptions = {
allowedHeaders: [],
allowedMethods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'],
allowedOrigins: [],
credentials: true,
preflightContinue: false,
}
This configuration is only relevant if the
enableMetrics
parameter was not set tofalse
Key | Type | Notes | Defaults To |
---|---|---|---|
livenessCheckEndpoint |
String |
defines the liveness check endpoint for ignoring in metrics | '/healthz' |
metricsEndpoint |
String |
defines the metrics endpoint for ignoring in metrics | '/metrics' |
probeIntervalInMilliseconds |
Number |
defines interval between metrics scrape | 3000 |
readinessCheckEndpoint |
String |
defines the readiness check endpoint for ignoring in metrics | '/readyz' |
pushgatewayUrl |
String |
defines the pushgateway URL - when this is not null , the pushgateway is considered activated |
null |
pushgatewayJobName |
String |
defines the job name of the job being pushed to the pushgateway - use this to define the application instance when running in a cluster | `process.env.USER |
pushgatewayTimeout |
Number |
defines the timeout of the pushgateway if enabled | 10000 |
Defaults to:
const metricsOptions = {
livenessCheckEndpoint: '/healthz',
metricsEndpoint: '/metrics',
probeIntervalInMilliseconds: 3000,
readinessCheckEndpoint: '/readyz',
pushgatewayUrl: null,
pushgatewayJobName: process.env.USER || 'unknown',
pushgatewayTimeout: 10000,
}
This configuration is only relevant if the
enableServerLogging
parameter was not set tofalse
Key | Type | Notes | Defaults To |
---|---|---|---|
additionalTokenizers |
Array of Tokenizers | Additional tokenizers with the schema {id: string, fn: (req: Request, res: Response) => any}
|
[] |
logger |
IApplicationLogger | Used by the server to create a child logger | undefined |
logStream |
String | Specifies a stream to use instead of the default console . For example, use this to link Morgan up with Winston |
null |
hostnameType |
String | If set to "os" , the os.hostname() will be used. For all other values, process.env[hostnameType] is used. |
"os" |
Defaults to:
const loggingOptions = {
additionalTokenizers: [],
logger: createLogger(),
logStream: null,
hostnameType: 'os',
}
Run the following from the root of the repository to initialise the dependencies since Lerna manages the dependencies for us across the multiple packages:
npx lerna bootstrap
Note: seems like the tests will fail if run locally on a Macbook. This is because prom-client emits metrics which are platform specific and the tests care catered to Linux. So please spin up linux container by doing docker-compose up
and running the tests from within the node
container.
To run the tests during development, use at the root directory:
npx lerna run --scope @mcf/server-boilerplate-middleware test:watch
To run the tests on the built package, use:
npx lerna run --scope @mcf/server-boilerplate-middleware test
To run a test server using the boilerplate server, use:
npx lerna run --scope @mcf/server-boilerplate-middleware start
npx lerna run --scope @mcf/server-boilerplate-middleware build
Run the following to setup an example environment:
Open a new terminal and run the following to create server a on port 11111:
SVC_ID=a PORT=11111 npm start;
Open another terminal and run the following to create server b on port 22222:
RSVC_ID=a SVC_ID=b PORT=22222 PROXY_PORT=11111 npm start;
Verify that your local Zipkin instance works and then run the following in yet another terminal to demonstrate tracing:
curl "http://localhost:22222/proxy";
- set frameguard action (i.e.
x-frame-options
) todeny
- set frameAncestors csp directive to
none
- set referrer policy to
strict-origin-when-cross-origin
explicitly
- update 'strict-transport-security : max-age to the recommended period of 2 years
- update 'strict-transport-security : max-age=31536000; includeSubDomains; preload' as per CSA requirements
- removed zipkin
- added aws xray tracing
- added keepalive and header timeout configuration
- changed configuration signature for tracing
- added distributed tracing capabilities
- server instance now exports the following methods:
.getTracer()
.getContext()
.getRequest()
- added
:logStream
property inloggingOptions
options for providing Morgan with a custom logger to use
- added Morgan server request logging
- changed the
preflightContinue
option to befalse
by default
- added features to accommodate a push gateway model (see
pushgatewayUrl
,pushgatewayTimeout
andpushgatewayJobName
for more info) - if
pushgatewayUrl
is defined in themetricsOptions
options property, the push gateway metrics flow model is activated, metrics will be pushed every:probeIntervalInMilliseconds
milliseconds`
- added Prometheus metrics (see
enableMetrics
andmetricsOptions
properties)
- added CORS support (see
enableCORS
andcorsOptions
)
- added gzip compression module
- refactored security module into two submodules: http headers and csp
- also changed API for content security policy (CSP)
- added new flag,
enableCSP
, for server initialisation
- added
connect-src
to CSP configuration
- fixed ci pipeline problems, no changes to functionality, we can now expect stably numbered patch releases
- added content security policy configuration for:
- 'default-src'
- 'child-src'
- 'font-src'
- 'img-src'
- 'script-src'
- 'style-src'
- 'report-uri'
- added basic http security headers
- hides 'x-powered-by'
- adds 'x-xss-protection: 1; mode=block'
- adds 'x-content-type-options : nosniff'
- adds 'x-dns-prefetch-control : off'
- adds 'x-download-options : noopen'
- adds 'x-frame-options : SAMEORIGIN'
- adds 'strict-transport-security : max-age=15552000; includeSubDomains'
- added body data parsing suport for
Content-Type: application/json
- added body data parsing suport for
Content-Type: application/x-www-form-urlencoded
- added cookie parsing superpowers
- fixed behaviour to allow import via
require('@mcf/server-boilerplate-middleware')
without a.default
property
- initial commit with an Express compatible server