{"id":22339784,"url":"https://github.com/scriptollc/take-five","last_synced_at":"2025-07-30T00:31:24.312Z","repository":{"id":65514261,"uuid":"64185172","full_name":"scriptoLLC/take-five","owner":"scriptoLLC","description":"JSON REST server","archived":false,"fork":false,"pushed_at":"2019-02-27T23:38:56.000Z","size":62,"stargazers_count":44,"open_issues_count":0,"forks_count":7,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-07-26T22:42:10.054Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scriptoLLC.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","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-07-26T03:00:09.000Z","updated_at":"2024-02-28T15:03:20.000Z","dependencies_parsed_at":"2023-01-26T20:55:20.983Z","dependency_job_id":null,"html_url":"https://github.com/scriptoLLC/take-five","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/scriptoLLC/take-five","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptoLLC%2Ftake-five","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptoLLC%2Ftake-five/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptoLLC%2Ftake-five/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptoLLC%2Ftake-five/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scriptoLLC","download_url":"https://codeload.github.com/scriptoLLC/take-five/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptoLLC%2Ftake-five/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267785731,"owners_count":24144119,"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","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-12-04T07:09:22.821Z","updated_at":"2025-07-30T00:31:24.047Z","avatar_url":"https://github.com/scriptoLLC.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# take-five\n\n[![Build Status](https://travis-ci.org/scriptoLLC/take-five.svg?branch=master)](https://travis-ci.org/scriptoLLC/take-five) [![Coverage Status](https://coveralls.io/repos/github/scriptoLLC/take-five/badge.svg?branch=master)](https://coveralls.io/github/scriptoLLC/take-five?branch=master)\n\nA minimal REST server that deals solely with JSON payloads, automatically\nhandles CORS requests, and limits the size of a POST bodies.\n\nFor 1.x docs, see https://github.com/scriptoLLC/take-five/tree/v1.4.1\n\n## Installation\n\n```\nnpm install -S take-five\n```\n\n## Usage\n\n```js\nconst Five = require('take-five')\nconst five = new Five()\nfive.get('/', async (req, res, ctx) =\u003e ctx.send('Hello, world'))\nfive.post('/', async (req, res, ctx) =\u003e ctx.send(req.body))\nfive.listen(3000)\n```\n\n```\ncurl -X GET localhost:3000\nHello, world\ncurl -X POST localhost:3000 -H 'content-type: application/json' -d '{\"hello\": \"world\"}'\n{\"hello\": \"world\"}\n```\n\n## Routing and route-handlers\n\nIn lieu of pre-set middleware, routes handlers can be arrays of functions that will\nbe iterated over asynchronously. To simplify handling of these handlers,\nit is expected that the handlers will return [thenables](https://promisesaplus.com/), or terminate the response\nstream.  This means any promises library should work (including the built in one),\nas well as using the `async` function keyword.\n\n**If you do not return a [thenable](https://promisesaplus.com/), handler processing will stop in that function**\n\ne.g.:\n\n```js\nfunction badSetContentHeader (req, res, ctx) {\n  res.setHeader('x-no-way', 'this is gonna do nothing')\n}\n\nfunction goodSetContentHeader (req, res, ctx) {\n  return new Promise((resolve) =\u003e {\n    res.setHeader('x-yes-way', 'this is gonna do everything!')\n    resolve()\n  })\n}\n\nfunction sendReply (req, res, ctx) {\n  ctx.send('beep!')\n}\n\nfive.get('/nope', [badSetContentHeader, sendReply])\nfive.get('/yup', [goodSetContentHeader, sendReply)\n```\n\nsince `badSetContentHeader` doesn't return a [`thenable`](https://promisesaplus.com/), take-five will not\nknow that it needs to call the `sendReply` function in the handler list for the `/nope`\nroute.\n\nIf you have either closed the response stream, or `reject`ed the [thenable](https://promisesaplus.com/) returned\nfrom your route handler, the next route will not be called. In the case that you have\n`reject`ed the [thenable](https://promisesaplus.com/), the error handler will be invoked as well. If you have\nclosed the response stream, the server assumes you were done processing the request\nand will just ignore the remaning functions in the queue.\n\nBy default, `get`, `post`, `put`, `delete`, `options` and `patch` will be available\nfor routing, but this can be changed by providing an array of methods on the options\nhash when instatiating a new TakeFive prototype.\n\n### Examples\n\n#### Using async/await\n\n```js\nfive.handleError = (err, req, res, ctx) =\u003e {\n  ctx.err(err.statusCode, err.message)\n}\n\nfive.get('/:secretdata', [\n  async (req, res, ctx) =\u003e {\n    try {\n      const session = await isAuthorized(req.headers.Authorization)\n      ctx.session = session\n    } catch (err) {\n      err.statusCode = 401\n      throw err\n    }\n  },\n  async (res, res, ctx) =\u003e {\n    try {\n      const data = await getResource(ctx.params.secretdata, ctx.session)\n      ctx.send(data)\n    } catch (err) {\n      err.statusCode = 500\n      reject(err)\n    }\n  }\n])\n```\n\n#### Using a \"then\"-able\n\n```js\nfive.get('/:secretdata', [\n  (req, res, ctx) =\u003e {\n    return new Promise((resolve, reject) =\u003e {\n      isAuthorized(req.headers.Authorization, (err, session) =\u003e {\n        if (err) {\n          ctx.err(401, 'Not Authorized')\n          return reject(new Error('Not authorized'))\n        }\n        ctx.session = session\n        resolve()\n      })\n    })\n  },\n  (res, res, ctx) =\u003e {\n    return new Promise((resolve, reject) =\u003e {\n      getResource(ctx.params.secretdata, ctx.session, (err, data) =\u003e {\n        if (err) {\n          ctx.err(500, 'Server error')\n          return reject(new Error('server error'))\n        }\n        ctx.send(data)\n        resolve()\n      })\n    })\n  }\n])\n```\n\n## API\n\n### `Five(opts?:object):object`\nCreate and return a new HTTP server object.\n\n* `opts.maxPost?:number`: the max size for a payload. Default: 512kb\n* `opts.allowHeaders?:array(string)`: an array of headers to accept besides the default. Default: `Content-Type`, `Accept`, `X-Requested-With`\n* `opts.allowOrigin?:string`: What origin(s) are accepted. Deafult: `*`\n* `opts.allowCredentials?:boolean`: Allow or deny credentials. Default: `true`\n* `opts.allowContentTypes?:string|string[]`: What content types are allowed to be used when sending data to the server. Default: `['application/json']`. Note: This is additive, so `application/json` will ALWAYS be allowed.\n* `opts.allowMethods?array(string)`: an array of methods to accept besides the default. Default: `PUT`, `POST`, `DELETE`, `GET`, `OPTIONS`, `PATCH`\n* `opts.methods?array(string)`: An array of methods to create route handlers for. Default: `PUT`, `POST`, `DELETE`, `GET`, `OPTIONS`, `PATCH`\n* `opts.http?object`: options for `http(s).createServer`. If you supply `key`,\n    `cert` and `ca` as options, it will assume you are trying to create an https server`\n\n`Access-Control-Allow-Headers` and `Access-Control-Allow-Methods` can also be changed during runtime\nby setting `allowHeaders` and `allowMethods` respectively.\n\n#### `Five#get(route:string, handler:(function|array(function)), routeOpts?:object)`\n#### `Five#post(route:string, handler:(function|array(function)), routeOpts?:object)`\n#### `Five#put(route:string, handler:(function|array(function)), routeOpts?:object)`\n#### `Five#patch(route:string, handler:(function|array(function)), routeOpts?:object)`\n#### `Five#delete(route:string, handler:(function|array(function)), routeOpts?:object)`\n#### `Five#options(route:string, handler:(function|array(function)), routeOpts?:object)`\nAdd a new route to the server. Routes may be added after the server has been\nstarted. You can supply either a single function or an array of functions to call.\nThe array will be traversed in the order it is supplied\n\n* `route:string` A [wayfarer](https://github.com/yoshuawuyts/wayfarer) route definition.\n* `handler(request:object, response:object, ctx:object):function`: The handler for this route.\n* `routeOpts?:object` - overrides for this specific chain of handlers\n    * `maxPost:number` - set the maximum size of a payload for this set of handlers\n    * `allowedContentTypes:string|string[]` - add new allowable content-types for this set of handlers\n\n#### `ctx:object`\n* `query?:object`: query parameters from the URL (if any)\n* `params?:object`: Named parameters from the route definition as provided by wayfarer\n* `body?:object`: The parsed JSON body available on POST requests\n* `send(statusCode?:number, body?:(string|object)):function`: Send a response.\n* `err(statusCode?:number, message?:string):function`: Send an error. If no status code is provided it will default to a 500 error.  If no message is provided, it will use the default message for that status code. The message will be wrapped in a JSON object under the key `message`\n* `next():function`: Go to the next handler\n\nThe `ctx` object can also be extended to contain user defined objects, through\nsetting `this.ctx` to an object. The object will be copied over using `Object.assign`.\n\nThe keys from above will overwrite any keys you provide named the same.\n\n#### `Five#handleError(err:Error, req:Request, res:Response, ctx:Context)`\nThis function is invoked when either an error object is passed to the `ctx.next`\nmethod, or the `then`-able function's `reject` handler is invoked.\n\nThis is a no-op by default, allowing you to customize it's behavior.\n\n#### `Five#listen(port:number, handle?:function)`\nStart listening for requests and call the optional callback when you've started\nlistening\n\n#### `Five.addParser(type:string, parser:functon):void`\nAdd a new content parser to the parsers list. By default there is only a single\nparser (`application/json`: JSON.parser). This can be overridden with a custom\nJSON parser if you'd like.\n\n#### `Five#close()`\nShutdown the underlying server\n\n### Getters/Setters\n\n#### `Five.server`\nThe underlying http(s) server from node can be accessed directly. This is non-writeable\n\n#### `Five.maxPost`\nGlobally control the maximum payload size after creation\n\n#### `Five.allowContentTypes`\nAdd new allowable content types for clients to send data with. You can use either\nan array of strings or a string\n\n#### `Five.allowHeaders`\nSet a new allowable header or headers for CORS requests. You can use either an\narray of strings or a string.\n\n#### `Five.allowMethods`\nSet a new allowable method for CORS requests.\n\n#### `Five.ctx`\nAdd new keys to the ctx objects\n\n## Do we need another REST server?\n\nProbably not, but [`restify`](http://restify.com), [`hapi`](http://hapijs.com) and [`express`](http://expressjs.com) are all over-kill on the types of services I'm building for the most part.\n\n* Setting up CORS is difficult or laborious: most REST services need to support CORS, this should be enabled by default (and easily configurable)\n* It has no need to accept anything other than `application/json` payloads, but you can easily extend it to\n* By default it will respond with `application/json` as well, but allow it be override-able if needed\n* Should be trivial to reject large payloads to prevent DOS attacks\n* Each route should have the ability to have multiple placeholders, regardless of the payload type\n* It shouldn't mutate the built-in request and response objects\n* It should be as fast as possible\n\nI found that the other three projects aim to support way more than this, which means supporting these features involves jumping through hoops or installing a ton of\nvarious other packages.\n\n## License\n\nCopyright © 2019 Scripto LLC, Todd Kennedy. Reuse permitted under the Apache-2.0 license\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscriptollc%2Ftake-five","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscriptollc%2Ftake-five","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscriptollc%2Ftake-five/lists"}