Code Monkey home page Code Monkey logo

merry's Introduction

merry

๐ŸŒŠ๐ŸŒŠโ›ต๏ธ๐ŸŒŠ๐ŸŒŠ
Nimble HTTP framework
Create tiny servers that run fast


Features

  • fast: using Node streams, merry handles request like no other
  • fun: helps with boring stuff like error handling
  • communicative: standardized ndjson logs for everything
  • sincere: doesn't monkey patch Node's built-ins
  • linear: smooth sailing from tinkering to production
  • very cute: ๐ŸŒŠ๐ŸŒŠโ›ต๏ธ๐ŸŒŠ๐ŸŒŠ

Usage

Given the following index.js:

const listen = require('merry/listen')
const string = require('merry/string')
const notFound = require('merry/404')
const error = require('merry/error')
const Env = require('merry/env')
const merry = require('merry')

const env = Env({ PORT: 8080 })
const app = merry()

app.router({ default: '/404' }, [
  [ '/', (req, res, params, done) => {
    done(null, string('hello world'))
  }],
  [ '/error', (req, res, params, done) => {
    done(error(500, 'server error!'))
  }],
  ['/api', {
    get: (req, res, params, done) => {
      done(null, string('hello very explicit GET'))
    }
  }],
  [ '/404', notFound() ]
])

const handler = app.start()
listen(env.PORT, handler)

Run using:

$ node index.js | merry-pretty

Logging

Merry uses the bole logger under the hood. When you create a new merry app, we enable a log forwarder that by default prints all logs to process.stdout.

To send a log, we must first create an instance of the logger. This is done by requireing the merry/log file, and instantiating it with a name. The name is used to help determine where the log was sent from, which is very helpful when debugging applications:

const Log = require('merry/log')
const log = Log('some-filename')
log.inf('logging!')

There are different log levels that can be used. The possible log levels are:

  • debug: used for developer annotation only, should not be enable in production
  • info: used for transactional messages
  • warn: used for expected errors
  • error: used for unexpected (critical) errors
const Log = require('merry/log')
const log = Log('my-file-name')
log.debug('it works!')
log.info('hey')
log.warn('oh')
log.error('oh no!')

The difference between an expected and unexpected error is that the first is generally caused by a user (e.g. wrong password) and the system knows how to respond, and the latter is caused by the system (e.g. there's no database) and the system doesn't know how to handle it.

Error handling

The send(err, stream) callback can either take an error or a stream. If an error has .statusCode property, that value will be used for res.statusCode. Else it'll use any status code that was set previously, and default to 500.

โš ๏ธ If errors are in the 4xx range, the full error is returned to the client and the error will be logged as loglevel 'info'. It's important to not disclose any internal information in 4xx type errors, as it can lead to serious security vulnerabilities. All errors in other ranges (typically 5xx) will send back the message 'server error' and are logged as loglevel 'error'.

Configuration

Generally there are two ways of passing configuration into an application. Through files and through command line arguments. In practice it turns out passing environment variables can be done with less friction than using files. Especially in siloed environments such as Docker and Kubernetes where mounting volumes can at times be tricky, but passing environment variables is trivial.

Merry ships with an environment argument validator that checks the type of argument passed in, and optionally falls back to a default if no value is passed in. To set the (very common) $PORT variable to default to 8080 do:

const Env = require('merry/env')
const env = Env({ PORT: 8080 })
console.log('port: ' + env.PORT)

And then from the CLI do:

node ./server.js
// => port: 8080

PORT=1234 node ./server.js
// => port: 1234

JSON

If Object and Array are the data primitives of JavaScript, JSON is the primitive of APIs. To help create JSON there's merry/json. It sets the right headers on res and efficiently turns JavaScript to JSON:

const json = require('merry/json')
const merry = require('merry')
const http = require('http')

const app = merry()
app.router(['/', (req, res, params, done) => {
  done(null, json(req, res, { message: 'hello JSON' }))
}])
http.createServer(app.start()).listen(8080)

Routing

Merry uses server-router under the hood to create its routes. Routes are created using recursive arrays that are turned into an efficient trie structure under the hood. You don't need to worry about any of this though; all you need to know is that we've tested it and it's probably among the fastest methods out there. Routes look like this:

const merry = require('merry')
const app = merry()
app.router([
  ['/', handleIndex],
  ['/foo', handleFoo, [
    ['/:bar', handleFoobarPartial]
  ]]
])

Partial routes can be set using the ':' delimiter. Any route that's registered in this was will be passed to the params argument as a key. So given a route of /foo/:bar and we call it with /foo/hello, it will show up in params as { bar: 'hello' }.

API

app = merry(opts)

Create a new instance of merry. Takes optional opts:

  • opts.logLevel: defaults to 'info'. Determine the cutoff point for logging.
  • opts.logStream: defaults to process.stdout. Set the output stream to write logs to

app.router(opts?, [routes])

Register routes on the router. Take the following opts:

  • default: (default: '/404') Default route handler if no route matches

routes

Each route has a signature of (req, res, params, done):

  • req: the server's unmodified req object
  • res: the server's unmodified res object
  • params: the parameters picked up from the router using the :route syntax in the route
  • done: a handler with a signature of (err, stream), that takes either an error or a stream. If an error is passed it sets a statusCode of 500 and prints out the error to stdout and sends a 'server error' reply. If a stream is passed it pipes the stream to res until it is done.

handler = app.start()

Create a handler that can be passed directly into an http server.

const string = require('merry/string')
const merry = require('merry')
const http = require('http')

const app = merry()
app.router(['/', handleRoute])

const handler = app.start()
http.createHttpServer(handler).listen(8080)

function handleRoute (req, res, params, done) {
  done(null, string('hello planet'))
}

string = merry/string(string)

Create a readableStream from a string. Uses from2-string under the hood

json = merry/json(req, res, object)

Create a readableStream from an object. req and res must be passed in to set the appropriate headers. Uses from2-string under the hood

error = merry/error(statusCode, message, err?)

Create an HTTP error with a statusCode and a message. By passing an erorr as the third argument it will wrap the error using explain-error to keep prior stack traces.

notFound = merry/404()

Create a naive /404 handler that can be passed into a path.

log = merry/log(name)

Create a new log client that forwards logs to the main app. See the logging section for more details.

log = merry/env(settings)

Create a new configuration client that reads environment variables from process.env and validates them against configuration.

Installation

$ npm install merry

See Also

  • choo - fun frontend framework
  • bankai - streaming asset compiler

License

MIT

merry's People

Contributors

greenkeeperio-bot avatar yoshuawuyts avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.