coiso v1.0.0-beta.7
coiso
Versatile HTTP API template built out of a need to have predictable patterns when building HTTP services. It can be used both as a command line utility and a library.
Install
npm i --save coisoUse
Create a resources folder in your project's root and enter it:
$ mkdir resources && cd resourcesThen, create an index.js file and export a function that accepts the standard
NodeJS HTTP handler signature:
module.exports = (req, res) => {
res.end('Hello World!');
}Next, add a start script in your package.json file:
{
"scripts": {
"start": "coiso"
}
}Finally, the server can be started like this:
$ npm startYou can now consume http://localhost:8080/.
From this moment on, you can create as many routes as you want, both Request/Response or WebSocket style.
CLI
coiso's command line interface wrap around it's API to provide a convenient
scaffolding to help create projects faster and in a consistent fashion.
coiso expects to find one directory in the root of your project — resources.
Resources are Javascript modules whose the resources-relative path map to a
HTTP route. If the file name ends in .ws.js it will be considered a WebSocket
handler, otherwise it is a request handler.
- A request handler exports a function thats receives unaltered NodeJS request and response objects as arguments.
module.exports = (req, res) => {
res.end('Hello World!');
}- A WebSocket handler exports a function that receives unaltered websocket and NodeJS request objects as arguments.
module.exports = (ws, req) => {
// Echo back the received messages
ws.on('message', ws.send);
// On connect
ws.send('Hello websocket');
};Both handler types receive an optional request context object as the third argument.
There are some simple rules for naming the files that define your routes:
- The file
resources/index.jscorresponds to the root (/) of your app. - A file called
resources/about.jscorresponds to the/aboutroute.resources/about/index.jsis treated the same asresources/about.js. - A file called
resources/blog/[slug].jscorresponds to the/blog/:slugroute in which caseparams.slugis available to the route via the request context object. This is called parametric routing. - A file called
resources/[blog_name]/[slug].jscorresponds to the/:blog_name/:slugroute, in which caseparams.slugandparams.blog_nameare available to the route via the request context object. This is called multi-parametric routing. - Files and directories with a leading underscore do not create routes. This
allows you to co-locate helper modules and components with the routes that
depend on them — for example you could have a file called
resources/_helpers/datetime.jsand it would not create a/_helpers/datetimeroute
Configuration
coiso looks for a coisoconfig.toml file in the project root and if it finds
one loads and transforms it to a plain javascript object for consumption.
This file is split in 2 sections:
- core - coiso's own configuration directives
- the rest - your own configuration directives
Initialization
Frequently, APIs need to perform some tasks before they start accepting requests.
For this purpose coiso looks into your module package.json main
field, loads it and treats it as an async function with the signature:
module.exports = async function setup(server /*CoisoServer*/, config/*configuration object*/) {
// Implement your logic here
}Examples include:
- load an in memory cache
- fetch runtime configurations
- execute an API call
If this function throws coiso will log the error and exit. If it succeeds, it
moves on to setup the HTTP server and start accepting requests.
The return value of this function is ignored.
More Info
Run the help command to get to know what else the CLI offers:
$ npx coiso --helpDeploy to Production
Make sure you run through this checklist before deploying to production:
- set environment variable NODE_ENV=production
- set log level to a level higher than
warn - run with node --abort-on-uncaught-exception
TODO
- Codebase cleanup
Add log support (pino, logpp)(Done)- Unit tests
- Integration tests
- Improve documentation
- http/2 support?
- windows support?
Server metrics(performance hooks; can be done by an external library)cors support(can be done as middleware)circuit breaker(can be done as middleware)cluster support(outside intended scope)websocket support?(done)
Benchmarks
The bulk of time is spent in your application code rather than in coiso's code,
so don't expect coiso to solve your performance problems.
Nevertheless, coiso aims to be as lightweight as possible and performs similarly
to a NodeJS raw HTTP server, latency-wise.
Synthetic Hello World Benchmark
This benchmark is not representative of a real world situation but it asserts the claims made above.
The test consists in a brief 15 seconds warmup:
$ echo "GET http://localhost:8080" | vegeta attack \
-duration=15s \
-rate 500 \
-keepalive false \
-timeout 0.3s \
-workers 200 \
| vegeta reportand then exercises the endpoint for 10 minutes at a rate of 1000 requests/seconds using 200 workers. A request is considered to timeout if it takes more than 100 milliseconds to reply. Keep-alive is disabled by default:
$ echo "GET http://localhost:8080/" | vegeta attack \
-duration=600s \
-rate 1000 \
-keepalive false \
-timeout 0.1s \
-workers 200 \
| vegeta reportRaw NodeJS Server (see file benchmark/raw-node-server.js)
$ NODE_ENV=production node benchmark/raw-node-server.js
Requests [total, rate] 600000, 1000.06
Duration [total, attack, wait] 9m59.965012258s, 9m59.964663988s, 348.27µs
Latencies [mean, 50, 95, 99, max] 239.989µs, 206.255µs, 354.381µs, 555.784µs, 107.924215ms
Bytes In [total, mean] 7200000, 12.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:600000
Error Set:
coiso (see examples/hello-world)
$ NODE_ENV=production npx coiso
Requests [total, rate] 600000, 1000.06
Duration [total, attack, wait] 9m59.964622632s, 9m59.96438675s, 235.882µs
Latencies [mean, 50, 95, 99, max] 270.008µs, 202.969µs, 472.339µs, 798.797µs, 87.146503ms
Bytes In [total, mean] 7200000, 12.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:600000
Error Set:Frequently Asked Questions
Q: Why not ExpressJS or KoaJS?
A: Because I believe development time should be spent building business logic instead of focusing on details such as transport, logging, routing, etc. I strongly believe in repeatable patterns and love the idea to treat NFR's as a dependency as opposed to having to think about them everytime one builds something new.
coiso's opinionated filesystem layout was designed for companies that build and maintain HTTP API's as an everyday task. While not perfect, it scales well for a multi-team environment where ownership is spread amongst a large number of people.
In any case, ExpressJS and KoaJS (and many others) are exceptional tools to build HTTP API's.
Q: What does coiso offer?
A: coiso is just a wrapper around the raw NodeJS HTTP server. It makes opinionated decisions around libraries to handle common requirements such as routing, logging, url parsing, etc.... It's designed to be:
- Easy: Designed for usage with async/await
- Complete: Features initialization routine, logging, routing, websockets, webping and request body parsing
- Agile: Built for easy development, easy deployment and containerization
- Simple: Small and explicit API surface
- Standard: Just HTTP. Optional lock in to the
RequestContextobject - Explicit: No middleware - modules declare all dependencies
- Operations-friendly: Open to metric agents and designed with post-mortem analysis in mind
Prior Art
All inspired this package:
License
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago