This is an imaginatively named, configurable web server using Fastify atop Node.js.
The aim is to provide a standardized node web server that can be used to serve your web application without the need for duplicating from another example, or starting from scratch.
The intention is that you will extend via configuration, such that this provides the baseline functionality of a Fastify web server, and within your own application you will add on the features, logic, etc unique to your situation.
This module requires Node v14.x.x+.
- Installing
- Usage
- Configuration
- Configuration Options
- electrode-confippet
- Adding a Fastify plugin
- API
- Enable compression
- Increase bodyLimit Size
- Contributions
- License
npm i --save @xarc/fastify-server
Electrode Server comes with enough defaults such that you can spin up a Fastify server at http://localhost:3000
with one call:
require("@xarc/fastify-server")();
Of course that doesn't do much but getting a 404
response from http://localhost:3000
.
To handle your routes, you should create a Fastify plugin to install your handlers.
See below for configuration options on how to register your plugin through @xarc/fastify-server
.
Here is an example with a default route to return a Hello World
string for http://localhost:3000
.
Through a plugin:
require("@xarc/fastify-server")({
plugins: {
routes: {
register: async instance => {
instance.route({
method: "GET",
path: "/",
handler: async () => "Hello World"
});
}
}
}
});
Using deferStart
flag to get a server that hasn't started yet:
//
// fastify doesn't allow adding routes after server started
// so need to set deferStart flag true to be able to add a route and then start the server.
//
require("@xarc/fastify-server")({ deferStart: true }).then(server => {
server.route({
method: "GET",
path: "/",
handler: async () => "Hello World"
});
return server.start();
});
You can pass in a config object that controls every aspect of the Fastify server.
const config = {
connection: {
port: 9000
}
};
require("@xarc/fastify-server")(config);
However, for a more complex application, it's recommended that you use a config composer such as electrode-confippet to manage your app configuration.
Sample of supported options:
const config = {
server: {
// options to be passed to fastify constructor
},
connection: {
// host, port etc
},
plugins: {
// specify fastify plugins to be registered
},
listener: emitter => {
// setup server startup event handlers
},
deferStart: true, // don't start the server, you need to call server.start()
electrode: {
// options specific to Electrode such as logLevel
}
};
All properties are optional (if not present, the default values shown below will be used).
server.app.config
is set to a object that's the combination of your config with @xarc/fastify-server's
defaults applied.
- Server options to pass to Fastify
Default:
{
server: {
app: {
electrode: true;
}
}
}
- Connection to setup for the Fastify server. Contains connection details for the server.
- If you want multiple connections, you can start multiple instances of
@xarc/fastify-server
Default:
{
connection: {
host: process.env.HOST,
address: process.env.HOST_IP || "0.0.0.0",
port: parseInt(process.env.PORT, 10) || 3000,
routes: {
cors: true
}
}
}
- plugin registration objects, converted to an array of its values and passed to Fastify's
server.register
Default is just empty object:
{
plugins: {
}
}
Configure electrode provided options.
{
electrode: {
eventTimeout: 5000, // milliseconds to wait for server start event listeners to return
}
}
eventTimeout
- optional milliseconds to wait for your server start event listeners to return. default10000
. Set it to0
orfalse
to disable timeout completely.
-
A function to install event listeners for the electrode server startup lifecycle.
-
The following events are supported:
config-composed
- All configurations have been composed into a single oneserver-created
- Fastify server createdplugins-sorted
- Plugins processed and sorted by priorityplugins-registered
- Plugins registered with Fastifyserver-started
- Server startedcomplete
- Final step before returning
To receive events you must set config.listener
before calling electrodeServer
.
For example:
myConfig.listener = (emitter) => {
emitter.on("server-created", (data, next) => {
// do something
next();
});
});
-
The data object will contain these:
emitter
,server
,config
, andplugins
. -
Depending on the stage some may not be present. For example,
server
is not available untilserver-created
event andplugins
is not available untilplugins-sorted
event. -
These are async events so you have to take and call a
next
callback.
NodeJS defaults to 5 seconds keep-alive timeout. fastify-server
defaults to 60 seconds timeout. If you want a custom timeout, use the keepAliveTimeout
option (in milliseconds).
{
"keepAliveTimeout": 60000
}
You can control how much output the Electrode Server logs to the console by setting the logLevel
.
- Levels are
"info"
,"warn"
,"error"
,"none"
. - A level of
"warn"
means only warnning and error messages will be printed. - Default is
"info"
For example, to suppress the banner that is shown when the server starts up:
Fastify server running at http://mypc:4000
set the logLevel to "warn" or "error":
{
electrode: {
logLevel: "warn";
}
}
To keep your environment specific configurations manageable, you can use electrode-confippet.
Once you have your config files setup according to the configuration files setup, you can simply pass the config object to electrode server.
const config = require("electrode-confippet").config;
require("@xarc/fastify-server")(config);
You can have @xarc/fastify-server
register any Fastify plugin that you want
through your configuration file.
{
plugins: {
"<plugin-id>": {
enable: true,
options: {},
register: (fastify, opts, done) => { done() }, // mutual exclusive with module
module: "<plugin-module-name>",
requireFromPath: process.cwd(),
priority: 210,
fastifyPluginDecorate: false
}
}
}
<plugin-id>
- ID for the plugin. Generally the module name for the plugin, which is used to load it for registration.enable
- optional if set tofalse
then this plugin won't be registered. If it's not set then it's considered to betrue
.options
- optional Object that's passed to the plugin's register function.register
- optional The Fastify plugin function. Overridesmodule
.module
- optional name of the module to load for the plugin instead of the<plugin-id>
requireFromPath
- optional The path from which to callrequire
to load the plugin modulepriority
- optional integer value to indicate the plugin's registration order- Lower value ones are register first
- Default to
Infinity
if this field is missing or has no valid integer value (NaN
) (string of number accepted)
fastifyPluginDecorate
- optional fastify-server auto decorates your plugin with fastify-plugin - set this tofalse
to disable this behavior. This can also be set to an object to use as options for fastify-plugin.
Priority allows you to arrange plugins to be registered in an order you prefer. The plugins with lower priority values are registered first.
If you don't want to use <plugin-id>
to load the module, then you can optionally specify one of the following:
register
- if specified, then treat as the plugin'sregister
function to pass to Fastify, overides modulemodule
- Only used ifregister
is not specified- If it's a string the used as the name module to
require
for registration. - It it's
false
then electrode server will not load any module. - You can specify a require-from-path for the module using an object.
- If it's a string the used as the name module to
{
plugins: {
myPlugin: {
module: {
requireFromPath: process.cwd(),
name: "my-plugin-module"
}
}
}
}
Electrode fastify server will try to find your Fastify Plugin from your module by looking through these fields:
mod.fastifyPlugin
mod.default.fastifyPlugin
(ES6 Module)mod.plugin
mod.default
(ES6 Module)mod
itself
Examples:
- Exporting the plugin directly as the module:
CommonJS example:
module.exports = myPlugin;
module.exports.fastifyPlugin = myPlugin;
ES6 example:
export default myPlugin;
export fastifyPlugin;
With fastify-plugin
:
const fastifyPlugin = require("fastify-plugin");
module.exports = fastifyPlugin(myPlugin, {
name: "myPlugin"
});
There are three places you can specify a path to call require
from when loading your plugin modules.
config.plugins.requireFromPath
- The top one used for all pluginsconfig.plugins.<plugin-id>.requireFromPath
- Used for the specific plugin of<plugin-id>
, overrides the one aboveconfig.plugins.<plugin-id>.module.requireFromPath
- Used for the specific plugin of<plugin-id>
, overrides the two above
For more information: check out require-from-path
To configure the plugin timeout, use Fastify's pluginTimeout
option.
{
"server": {
"pluginTimeout": 10000
}
}
Uses the Fastify default if none is specified.
Here's an example using the fastify-static
plugin:
First, install the plugin as you normally would from npm
:
npm i --save fastify-static
Then, add your plugin to the config plugins
section.
{
plugins: {
"fastify-static": {
enable: true,
options: {
root: process.cwd()
},
priority: 210,
requireFromPath: process.cwd()
}
}
}
Above config tells @xarc/fastify-server
to require
from CWD
the module by its <plugin-id>
"fastify-static"
and register it as a plugin with Fastify. Options passes in the required root
option to fastify-static
.
The electrode server exports a single API.
electrodeServer(config, [decors], [callback])
-
config
is the electrode server config -
decors
- Optional extraconfig
or array ofconfig
. In case you have common config you want to put inside a dedicated module, you can pass them in here.- If it's an array like
[ decor1, decor2, decor3 ]
then each one is composed into the main config. ie: something similar to_.merge(mainConfig, decor1, decor2, decor3)
.
- If it's an array like
-
callback
is an optional errback with the signaturefunction (err, server)
- where
server
is the Fastify server
- where
-
Returns: a promise resolving to the Fastify server if callback is not provided
Elecrode server also provides the app
decorator on the server and request objects.
The app
object contains the fully merged final config
.
handler: (request, reply) => {
console.log("Listening on ", request.app.config.connection.port);
};
In Fastify compression can be achieved using @fastify/compress
'@fastify/compress':{
priority: 200,
options:
{ global: true, encodings:['gzip'] }
},
Currently, the following encoding tokens are supported, using the first acceptable token in this order:
br
gzip
deflate
* (no preference — @fastify/compress will use gzip)
identity (no compression)
server: {
bodyLimit: 1048576; //new size limit
}
Make sure you sign the CLA. Checkout the contribution guide
To run tests
% npm i
% clap test
To run tests and coverage
% clap check
To run sample server
% npm run sample
Hit http://localhost:9000
Hit http://localhost:9000/html/hello.html
to test static path.
Copyright 2016-present WalmartLabs
Licensed under the Apache License, Version 2.0.