{"id":13432229,"url":"https://github.com/shipharbor/merry","last_synced_at":"2025-03-16T23:30:57.052Z","repository":{"id":57294904,"uuid":"58484029","full_name":"shipharbor/merry","owner":"shipharbor","description":":ocean::ocean::sailboat::ocean::ocean: - cute streaming API framework","archived":false,"fork":false,"pushed_at":"2018-04-27T09:28:57.000Z","size":156,"stargazers_count":312,"open_issues_count":10,"forks_count":21,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-07-31T02:19:58.955Z","etag":null,"topics":["batteries-included","framework","ndjson","node","small","streams"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shipharbor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-05-10T18:21:34.000Z","updated_at":"2024-07-31T02:19:58.956Z","dependencies_parsed_at":"2022-08-29T07:51:37.662Z","dependency_job_id":null,"html_url":"https://github.com/shipharbor/merry","commit_stats":null,"previous_names":["yoshuawuyts/merry"],"tags_count":47,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipharbor%2Fmerry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipharbor%2Fmerry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipharbor%2Fmerry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipharbor%2Fmerry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shipharbor","download_url":"https://codeload.github.com/shipharbor/merry/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243949860,"owners_count":20373653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["batteries-included","framework","ndjson","node","small","streams"],"created_at":"2024-07-31T02:01:09.594Z","updated_at":"2025-03-16T23:30:57.044Z","avatar_url":"https://github.com/shipharbor.png","language":"JavaScript","readme":"\u003ch1 align=\"center\"\u003emerry\u003c/h1\u003e\n\n\u003cdiv align=\"center\"\u003e\n  🌊🌊⛵️🌊🌊\n\u003c/div\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003cstrong\u003eCute streaming API framework\u003c/strong\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n---\n\n\u003cdiv align=\"center\"\u003e\n  \u003c!-- Stability --\u003e\n  \u003ca href=\"https://nodejs.org/api/documentation.html#documentation_stability_index\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/stability-experimental-orange.svg?style=flat-square\"\n      alt=\"API stability\" /\u003e\n  \u003c/a\u003e\n  \u003c!-- NPM version --\u003e\n  \u003ca href=\"https://npmjs.org/package/merry\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/merry.svg?style=flat-square\"\n      alt=\"NPM version\" /\u003e\n  \u003c/a\u003e\n  \u003c!-- Build Status --\u003e\n  \u003ca href=\"https://travis-ci.org/shipharbor/merry\"\u003e\n    \u003cimg src=\"https://img.shields.io/travis/shipharbor/merry/master.svg?style=flat-square\"\n      alt=\"Build Status\" /\u003e\n  \u003c/a\u003e\n  \u003c!-- Test Coverage --\u003e\n  \u003ca href=\"https://codecov.io/github/shipharbor/merry\"\u003e\n    \u003cimg src=\"https://img.shields.io/codecov/c/github/shipharbor/merry/master.svg?style=flat-square\"\n      alt=\"Test Coverage\" /\u003e\n  \u003c/a\u003e\n  \u003c!-- Downloads --\u003e\n  \u003ca href=\"https://npmjs.org/package/merry\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/merry.svg?style=flat-square\"\n      alt=\"Downloads\" /\u003e\n  \u003c/a\u003e\n  \u003c!-- Standard --\u003e\n  \u003ca href=\"https://codecov.io/github/shipharbor/merry\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square\"\n      alt=\"Standard\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\nMerry is a little Node framework that helps you build performant applications\nwith little effort. We don't think that \"fast\" and \"cute\" should be mutually\nexclusive. Out of the box we've included consistent logging, standardized error\nhandling, a clean streams API and plenty of nuts, bolts and options to\ncustomize merry to fit your use case. We hope you have a good time using it.\n:v: _-Team Merry_\n\n## Features\n- __fast:__ using Node streams, merry handles request like no other\n- __fun:__ helps with boring stuff like error handling\n- __communicative:__ standardized [ndjson][ndjson] logs for everything\n- __sincere:__ doesn't monkey patch Node's built-ins\n- __linear:__ smooth sailing from tinkering to production\n- __very cute:__ 🌊🌊⛵️🌊🌊\n\n## Table of Content\n- [Usage](#usage)\n- [Logging](#logging)\n- [Error Handling](#error-handling)\n- [Configuration](#configuration)\n- [Routing](#routing)\n- [Middleware](#middleware)\n- [HTTP2](#http2)\n- [API](#api)\n- [Installation](#installation)\n- [See Also](#see-also)\n\n## Usage\n```js\nvar merry = require('merry')\n\nvar app = merry()\n\napp.route('GET', '/', function (req, res, ctx) {\n  ctx.log.info('oh hey, a request here')\n  ctx.send(200, { cute: 'butts' })\n})\n\napp.route('default', function (req, res, ctx) {\n  ctx.log.info('Route doesnt exist')\n  ctx.send(404, { message: 'nada butts here' })\n})\n\napp.listen(8080)\n```\n\n```sh\n$ node index.js | merry\n```\n\n## Logging\nMerry uses the `pino` logger under the hood. When you create a new `merry` app,\nwe enable a log forwarder that by default prints all logs to `process.stdout`.\n\nThere are different log levels that can be used. The possible log levels are:\n- __debug:__ used for developer annotation only, should not be enable in\n  production\n- __info:__ used for transactional messages\n- __warn:__ used for expected errors\n- __error:__ used for unexpected errors\n- __fatal:__ used for critical errors that should terminate the process\n\n```js\nvar merry = require('merry')\nvar app = merry()\n\napp.route('GET', '/', function (req, res, ctx) {\n  ctx.log.debug('it works!')\n  ctx.log.info('hey')\n  ctx.log.warn('oh')\n  ctx.log.error('oh no!')\n  ctx.log.fatal('send help')\n})\n```\n\nThe difference between an expected and unexpected error is that the first is\ngenerally caused by a user (e.g. wrong password) and the system knows how to\nrespond, and the latter is caused by the system (e.g. there's no database) and\nthe system doesn't know how to handle it.\n\n## Error handling\nError handling is different for each application. Errors come in different\nshapes, have different status codes, so we can't provide a one-size-fits-all\nsolution. But we do think that having consistent error messages is useful, so\nMerry comes with a recommended pattern to handle errors.\n\n```js\n// errors.js\nexports.ENOTFOUND = function (req, res, ctx) {\n  ctx.log.warn('ENOTFOUND')\n  ctx.send(404, {\n    type: 'invalid_request_error',\n    message: 'Invalid request data'\n  })\n}\n\nexports.EDBOFFLINE  = function (req, res, ctx) {\n  ctx.log.error('EDBOFFLINE')\n  ctx.send(500, {\n    type: 'api_error',\n    message: 'Internal server error'\n  })\n}\n```\n\n```js\n// index.js\nvar errors = require('./errors')\nvar merry = require('merry')\nvar db = require('my-cool-db')\n\nvar app = merry()\n\napp.route('GET', '/', function (req, res, ctx) {\n  db.get('some-key-from-request', function (err, data) {\n    if (err) return errors.ENOTFOUND(req, res, ctx)\n    ctx.send(200, data)\n  })\n})\n\napp.listen(8080)\n```\n\n## Configuration\nGenerally there are two ways of passing configuration into an application.\nThrough files and through command line arguments. In practice it turns out\npassing environment variables can be done with less friction than using files.\nEspecially in siloed environments such as Docker and Kubernetes where mounting\nvolumes can at times be tricky, but passing environment variables is trivial.\n\nMerry ships with an environment argument validator that checks the type of\nargument passed in, and optionally falls back to a default if no value is\npassed in. To set the (very common) `$PORT` variable to default to `8080` do:\n```js\nvar merry = require('merry')\nvar env = { PORT: 8080 }\nvar app = merry({ env: env })\napp.listen(app.env.PORT)\n```\n\nAnd then from the CLI do:\n```sh\nnode ./server.js\n// =\u003e port: 8080\n\nPORT=1234 node ./server.js\n// =\u003e port: 1234\n```\n\n## Routing\nMerry uses `server-router` under the hood to create its routes. Routes are\ncreated using recursive arrays that are turned into an efficient `trie`\nstructure under the hood. You don't need to worry about any of this though; all\nyou need to know is that we've tested it and it's probably among the fastest\nmethods out there. Routes look like this:\n```js\nvar merry = require('merry')\nvar app = merry()\napp.route('GET', '/', handleIndex)\napp.route('PUT', '/foo', handleFoo)\napp.route('GET', '/foo/:bar', handleFoobarPartial)\napp.listen()\n```\n\nPartial routes can be set using the `':'` delimiter. Any route that's\nregistered in this was will be passed to the `ctx` argument as a key. So\ngiven a route of `/foo/:bar` and we call it with `/foo/hello`, it will show up\nin `ctx` as `{ bar: 'hello' }`.\n\n## Middleware\nWe do provide you with a way to access your route's `ctx` via `app.use`\n```js\nvar merry = require('merry')\nvar app = merry()\napp.use(function (req, res, ctx) {\n  ctx.foo = 'bar'\n})\n```\n\n## HTTP2\nFor [http2](https://nodejs.org/api/http2) support you will need to provide a\n`key` and a `cert` to establish a secure connection. These can be passed as\npart of merry's opts. If `http2` is not available, an error will be thrown to\nupgrade your node version to \u003e v8.4.0.\n```js\nvar merry = require('merry')\nvar fs = require('fs')\n\nvar opts = {\n  key: fs.readFileSync('server-key.pem'),\n  cert: fs.readFileSync('server-cert.pem')\n}\nvar app = merry(opts)\n\napp.listen(8080)\n```\n\n## API\n### app = merry(opts)\nCreate a new instance of `merry`. Takes optional opts:\n- __opts.logLevel:__ defaults to `'info'`. Determine the cutoff point for\n  logging\n- __opts.logStream:__ defaults to `process.stdout`. Set the output writable stream to\n  write logs to\n- __opts.env:__ pass an object containing env var assertions\n- __opts.key:__ key to create an http2 connection\n- __opts.cert:__ cert to create an http2 connection \n\n### app.use(req, res, ctx)\nAllows you to modify `req`, `res` and `ctx` objects prior to handling a route.\n\n### app.route(method|methods, route, handler)\nRegister a new handler for a route and HTTP method. Method can be either a\nsingle HTTP method, or an array of HTTP methods.\n\n### app.route('default', handler)\nRegister a new default handler that will be called if no other handlers match.\n\n#### routes\nEach route has a signature of `(req, res, ctx)`:\n- __req:__ the server's unmodified `req` object\n- __res:__ the server's unmodified `res` object\n- __ctx:__ an object that can contain values and methods\n\n#### ctx.params\nParameters picked up from the `router` using the `:route` syntax in the route.\n\n#### ctx.env\nEnvironment variables passed into the `merry({ env })` constructor.\n\n#### ctx.log[loglevel]\\([…data])\nLog data. Loglevel can be one of `trace`, `debug`, `info`, `warn`, `error`,\n`fatal`. Can be passed varying arguments.\n\n#### ctx.send(statusCode, data, [headers])\nEfficiently encode JSON, set the appropriate headers and end the request. Uses\nstreams under the hood.\n\n#### ctx.parse(jsonStream, callback(err, data))\nParse a stream of JSON into an object. Useful to decode a server's `req` stream\nwith.\n\n### handler = app.start()\nCreate a handler that can be passed directly into an `http` server.\n\n```js\nvar merry = require('merry')\nvar http = require('http')\n\nvar app = merry()\napp.route('GET', '/', handleRoute)\n\nvar handler = app.start()\nvar server = http.createServer(handler)\nserver.listen(8080)\n\nfunction handleRoute (req, res, ctx, done) {\n  done(null, 'hello planet')\n}\n```\n\n### app.listen(port)\nStart the application directly and listen on a port:\n```js\nvar merry = require('merry')\n\nvar app = merry()\napp.route('GET', '/', handleRoute)\napp.listen(8080)\n\nfunction handleRoute (req, res, ctx, done) {\n  done(null, 'hello planet')\n}\n```\n\n## Installation\n```sh\n$ npm install merry\n```\n\n## See Also\n- [yoshuawuyts/choo](https://github.com/yoshuawuyts/choo) - fun frontend framework\n- [yoshuawuyts/bankai](https://github.com/yoshuawuyts/bankai) - streaming asset compiler\n- [yoshuawuyts/server-router](https://github.com/yoshuawuyts/server-router) - efficient server router\n- [lrlna/pino-colada](https://github.com/lrlna/pino-colada) - cute ndjson formatter\n\n## License\n[MIT](https://tldrlegal.com/license/mit-license)\n\n[0]: https://img.shields.io/badge/stability-experimental-orange.svg?style=flat-square\n[1]: https://nodejs.org/api/documentation.html#documentation_stability_index\n[2]: https://img.shields.io/npm/v/merry.svg?style=flat-square\n[3]: https://npmjs.org/package/merry\n[4]: https://img.shields.io/travis/shipharbor/merry/master.svg?style=flat-square\n[5]: https://travis-ci.org/shipharbor/merry\n[6]: https://img.shields.io/codecov/c/github/shipharbor/merry/master.svg?style=flat-square\n[7]: https://codecov.io/github/shipharbor/merry\n[8]: http://img.shields.io/npm/dm/merry.svg?style=flat-square\n[9]: https://npmjs.org/package/merry\n[10]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square\n[11]: https://github.com/feross/standard\n[pino]: https://github.com/pinojs/pino\n[ndjson]: http://ndjson.org/\n[corsify]: https://github.com/Raynos/corsify\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshipharbor%2Fmerry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshipharbor%2Fmerry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshipharbor%2Fmerry/lists"}