Provides an extended Express, as well as useful tools for building consistent Origami services.
Running the Origami Service module requires Node.js 10.x and npm. You can install with:
npm install @financial-times/origami-service
This library makes use of promises, and provides a wrapper around Express – familiarity is assumed in the rest of the API documentation. You'll also need to require the module with:
const origamiService = require("@financial-times/origami-service")
origamiService( [options] )
This function returns a new Express application preconfigured to run as an Origami service. You can configure the created service with an options object if you need to override any defaults.
const app = origamiService({
port: 1234,
})
The application's listen
function has a different signature to Express, it's wrapped in a promise and uses the port
option from the constructor:
const app = origamiService({
port: 1234,
})
app.listen() // runs on port 1234 and returns a promise
The Express application will have some additional properties, added by the Origami Service module. These will also be added to app.locals
so they're available in your views.
app.ft.log
: The logger passed in to the serviceapp.ft.metrics
: The Next Metrics instance which can be used to send additional data to Graphiteapp.ft.options
: The defaulted options passed into the origamiService
callapp.ft.server
: The HTTP Server which was returned by app.listen
The following Express settings will be set:
case sensitive routing
: Enabledjson spaces
: Configured to prettify output JSONx-powered-by
: Disabledviews
: Configured to the views
folderview engine
: Configured to use HandlebarsHandlebars will be used as a view engine with the extension .html
. We use Express Handlebars for this, which means layouts and partials are supported. Add layouts and partials to views/layouts
and views/partials
respectively. See the examples for more information.
Some middleware will also be mounted by default, in this order:
/__about
, /__health
, and /__gtg
endpoints (as well as /__error
if configured to)/__gtg
, /__health
, and /favicon.ico
are never logged)public
folderThe Origami Service module can be configured with a variety of options, passed in as an object to the origamiService
function or as environment variables. The priority given to each type of configuration is important:
options
object will take priority over everything. E.g. this means if you hard-code the port
configuration in your code, the PORT
environment variable will cease to work.options
object, then for some options an environment variable will be checked. The names of these are slightly different, and will be documented below.options
object or environment, then it will use the default value as documented below.The available options are as follows. Where two names are separated by a /
, the first is the object key and the second is the environment variable:
about
: About information to populate the /__about
endpoint with. This should be an object which conforms to the FT's about/runbook standard. This defaults the name/purpose
properties to name/description
from your package.json
file if they're not set. This option gets passed on to Express Web ServicebasePath
: The base path of the application, which paths (e.g. public files) will be relative to. Defaults to process.cwd()
defaultLayout
: The default layout file to use in view rendering. This should be the name of an HTML file in the views/layouts
directory, e.g. 'main'
would map to views/layouts/main.html
. Defaults to false
handlebarsHelpers
: Handlebars helpers configuration. This should be an object. Defaults to an empty object.exposeErrorEndpoint/EXPOSE_ERROR_ENDPOINT
: Whether to expose the /__error
endpoint for debugging purposes. This should be false
in production. Defaults to false
environment/NODE_ENV
: The environment to run in. This affects things like public file max ages. One of 'production'
, 'development'
, or 'test'
. Defaults to 'development'
goodToGoTest
: A function to use in calculating whether the application is good to go. See Express Web Service for more informationgraphiteAppUUID/FT_GRAPHITE_APP_UUID
: The graphite UUID to use when recording metrics.healthCheck
: A function to use in calculating how healthy the application is. See Express Web Service for more informationlog
: A console object used to output non-request logs. Defaults to the global console
objectmetricsAppName
: The name of the application to use when logging to Graphite. Defaults to about.systemCode
then about.name
port/PORT
: The port that the application should run on. Defaults to 8080
.region/REGION
: The region to use in logging and reporting for the application. Defaults to 'EU'
requestLogFormat
: The Morgan log format to output request logs in. If set to null
, request logs will not be output. Defaults to 'combined'
sentryConfig
: Additional Sentry configurations. This is passed directly to the Raven configuration, see here for more information. Defaults to {}
sentryDsn/SENTRY_DSN
: The Sentry DSN to send errors to. If set to null
, errors will not be sent to Sentry. Defaults to null
origamiService.middleware.notFound( [message] )
Create and return a middleware for throwing 404
"Not Found" errors. The returned middleware will pass on an error which can be caught later by an error handling middleware. The error will have a status
property set to 404
so that your error handler can differentiate it from other errors, as well as a cacheMaxAge
property set to '30s'
.
This middleware should be mounted after all of your application routes:
// routes go here
app.use(origamiService.middleware.notFound())
// error handler goes here
By default, the error message will be set to 'Not Found'
. If you wish to specify a custom message you can specify one as a parameter:
app.use(origamiService.middleware.notFound("This page does not exist"))
origamiService.middleware.errorHandler()
Create and return a middleware for rendering errors that occur in the application routes. The returned middleware logs errors to Sentry (if the sentryDsn
option is present) and then renders an error page.
The following properties can be set on an error object to change the behaviour of the error handler:
status
: decide on which error type to render and send the HTTP status codecacheMaxAge
: the maximum cache age of the error, which gets passed to the cacheControl
middlewareskipSentry
: if set to true
, the error will not be reported to SentryThis middleware should be mounted after all of your application routes, and is useful in conjunction with origamiService.middleware.notFound
:
// routes go here
app.use(origamiService.middleware.notFound())
app.use(origamiService.middleware.errorHandler())
The error handling middleware will look for a Handlebars template in views/error.html
and use it to render the page. If no template is found in that location then it falls back to basic HTML output. This allows you to style your error pages and use your application's default layout.
origamiService.middleware.getBasePath()
Calculate the base path that the application is running on and provide it for use in later middleware and templates. This middleware reads and normalises the FT-Origami-Service-Base-Path
header (which should be set by the CDN) and adds it to:
request.basePath
: for later middleware to useresponse.locals.basePath
: for templates to useapp.use(origamiService.middleware.getBasePath())
// routes go here
origamiService.middleware.cacheControl( options )
Add a Cache-Control
header to the response, including stale-if-error
and stale-while-revalidate
directives. The available options are:
maxAge
: The max age to set in the Cache-Control
header. Must be a valid string that the ms library can parse. RequiredstaleIfError
: Override the stale-if-error
directive. Must be a valid string that the ms library can parse. Defaults to the same value as maxAge
staleWhileRevalidate
: Override the stale-while-revalidate
directive. Must be a valid string that the ms library can parse. Defaults to the same value as maxAge
This middleware is best used per-route, rather than at the application level:
app.get(
"/docs",
origamiService.middleware.cacheControl({maxAge: "1 day"}),
() => {
// route stuff
}
)
origamiService.middleware.purgeUrls( options )
Create a purge route which will purge all of the given URLs in Fastly when the endpoint is POSTed to. This endpoint accepts general options as well as per-request options in the querystring.
The general options are:
fastlyApiKey/FASTLY_PURGE_API_KEY
: The Fastly API key to use when purging URLspurgeApiKey/PURGE_API_KEY
: The API key that is required when a user requests the purge endpoint. If they don't provide this, then the purge will not be performedurls
: The URLs that will be purged as an array of strings. This should contain full URLs with a scheme and hostname, that are permitted to be purged with the given Fastly API keyWhen a user requests the created endpoint, they can use query string parameters to change behaviour:
apiKey
: This is required, and must match the purgeApiKey
option to work. If this is not provided or is incorrect, an error will be displayedwait
: A number of milliseconds to wait before the purge is performed. This can be used to ensure that the purge is performed only once a new version of the application is running. Defaults to 0
This middleware should be mounted as a route, rather than at the application level:
app.post(
"/purge",
origamiService.middleware.purgeUrls({
urls: ["https://example.com/url-1", "https://example.com/url-2"],
})
)
You can find example implementations of Origami-compliant services in the examples
folder of this repo:
node examples/basic
node examples/options
node examples/middleware
node examples/views
This module has a full suite of unit tests, and is verified with ESLint. You can use the following commands to check your code before opening a pull request.
make verify # verify JavaScript code with ESLint
make test # run the unit tests and check coverage
New versions of the module are published automatically by CI when a new tag is created matching the pattern /v.*/
.
Origami Service major versions are normally supported for 3–6 months after their last minor release. This means that patch-level changes will be added and bugs will be fixed. The table below outlines the end-of-support dates for major versions, and the last minor release for that version.
We also maintain a migration guide to help you migrate.
If you're opening issues related to these, please mention the version that the issue relates to.
If you have any questions or comments about this module, or need help using it, please either raise an issue, visit #origami-support or email Origami Support.
This software is published by the Financial Times under the MIT licence.