Skip to content

Commit

Permalink
Overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
toddself committed Nov 16, 2018
1 parent 05408d4 commit fbbfabf
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 177 deletions.
100 changes: 50 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ npm install -S take-five
```js
const Five = require('take-five')
const five = new Five()
five.get('/', (req, res, ctx) => ctx.send('Hello, world'))
five.post('/', (req, res, ctx) => ctx.send(req.body))
five.get('/', async (req, res, ctx) => ctx.send('Hello, world'))
five.post('/', async (req, res, ctx) => ctx.send(req.body))
five.listen(3000)
```

Expand All @@ -30,44 +30,23 @@ curl -X POST localhost:3000 -H 'content-type: application/json' -d '{"hello": "w

## Routing and route-handlers
In lieu of pre-set middleware, routes handlers can be arrays of functions that will
be iterated over asynchronously.

There are two ways to inform the server to go to the next handler: either by calling
`ctx.next`, or by returning a "then-able" and either resolving or rejecting it.
be iterated over asynchronously. To simplify handling of these handlers,
it is expected that the handlers will return then-ables. This means any promises
library should work (including the built in one), as well as using the `async` function
keyword.

If you have either closed the response stream, or `reject`ed the then-able returned
from your route handler, the next route will not be called. In the case that you have
`reject`ed the then-able, the error handler will be invoked as well.
`reject`ed the then-able, the error handler will be invoked as well. If you have
closed the response stream, the server assumes you were done processing the request
and will just ignore the remaning functions in the queue.

By default, `get`, `post`, `put`, `delete`, `options` and `patch` will be available
for routing, but this can be changed by providing an array of methods on the options
hash when instatiating a new TakeFive prototype.

### Examples

#### Using `ctx.next`
```js
five.get('/:secretdata', [
(req, res, ctx) => {
isAuthorized(req.headers.Authorization, (err, session) => {
if (err) {
return ctx.err(401, 'Not Authorized')
}
ctx.session = session
ctx.next()
})
},
(res, res, ctx) => {
getResource(ctx.params.secretdata, ctx.session, (err, data) => {
if (err) {
return ctx.err(500, 'Server error')
}
ctx.send(data)
})
}
])
```

#### Using async/await
```js
five.handleError = (err, req, res, ctx) => {
Expand Down Expand Up @@ -135,6 +114,7 @@ Create and return a new HTTP server object.
* `opts.allowHeaders?:array(string)`: an array of headers to accept besides the default. Default: `Content-Type`, `Accept`, `X-Requested-With`
* `opts.allowOrigin?:string`: What origin(s) are accepted. Deafult: `*`
* `opts.allowCredentials?:boolean`: Allow or deny credentials. Default: `true`
* `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.
* `opts.allowMethods?array(string)`: an array of methods to accept besides the default. Default: `PUT`, `POST`, `DELETE`, `GET`, `OPTIONS`, `PATCH`
* `opts.methods?array(string)`: An array of methods to create route handlers for. Default: `PUT`, `POST`, `DELETE`, `GET`, `OPTIONS`, `PATCH`
* `opts.http?object`: options for `http(s).createServer`. If you supply `key`,
Expand All @@ -143,18 +123,21 @@ Create and return a new HTTP server object.
`Access-Control-Allow-Headers` and `Access-Control-Allow-Methods` can also be changed during runtime
by setting `allowHeaders` and `allowMethods` respectively.

### `Five#get(route:string, handler:(function|array(function)))`
### `Five#post(route:string, handler:(function|array(function)))`
### `Five#put(route:string, handler:(function|array(function)))`
### `Five#patch(route:string, handler:(function|array(function)))`
### `Five#delete(route:string, handler:(function|array(function)))`
### `Five#options(route:string, handler:(function|array(function)))`
#### `Five#get(route:string, handler:(function|array(function)), routeOpts?:object)`
#### `Five#post(route:string, handler:(function|array(function)), routeOpts?:object)`
#### `Five#put(route:string, handler:(function|array(function)), routeOpts?:object)`
#### `Five#patch(route:string, handler:(function|array(function)), routeOpts?:object)`
#### `Five#delete(route:string, handler:(function|array(function)), routeOpts?:object)`
#### `Five#options(route:string, handler:(function|array(function)), routeOpts?:object)`
Add a new route to the server. Routes may be added after the server has been
started. You can supply either a single function or an array of functions to call.
The array will be traversed in the order it is supplied

* `route:string` A [wayfarer](https://github.com/yoshuawuyts/wayfarer) route definition.
* `handler(request:object, response:object, ctx:object):function`: The handler for this route.
* `routeOpts?:object` - overrides for this specific chain of handlers
* `maxPost:number` - set the maximum size of a payload for this set of handlers
* `allowedContentTypes:string|string[]` - add new allowable content-types for this set of handlers

#### `ctx:object`
* `query?:object`: query parameters from the URL (if any)
Expand All @@ -169,41 +152,58 @@ setting `this.ctx` to an object. The object will be copied over using `Object.as

The keys from above will overwrite any keys you provide named the same.

### `Five#handleError(err:Error, req:Request, res:Response, ctx:Context)`
#### `Five#handleError(err:Error, req:Request, res:Response, ctx:Context)`
This function is invoked when either an error object is passed to the `ctx.next`
method, or the `then`-able function's `reject` handler is invoked.

This is a no-op by default, allowing you to customize it's behavior.

### `Five#listen(port:number, handle?:function)`
#### `Five#listen(port:number, handle?:function)`
Start listening for requests and call the optional callback when you've started
listening

### `Five#close()`
#### `Five.addParser(type:string, parser:functon):void`
Add a new content parser to the parsers list. By default there is only a single
parser (`application/json`: JSON.parser). This can be overridden with a custom
JSON parser if you'd like.

#### `Five#close()`
Shutdown the underlying server

### `Five.server`
The underlying http(s) server from node can be accessed directly
### Getters/Setters

#### `Five.server`
The underlying http(s) server from node can be accessed directly. This is non-writeable

#### `Five.maxPost`
Globally control the maximum payload size after creation

#### `Five.allowContentTypes`
Add new allowable content types for clients to send data with. You can use either
an array of strings or a string

#### `Five.allowHeaders`
Set a new allowable header or headers for CORS requests. You can use either an
array of strings or a string.

#### `Five.allowMethods`
Set a new allowable method for CORS requests.

#### `Five.ctx`
Add new keys to the ctx objects

## Do we need another REST server?
Probably 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.
* Setting up CORS is difficult or laborious: most REST services need to support CORS, this should be enabled by default (and easily configurable)
* It has no need to accept anything other than `application/json` payloads, and doesn't need the cruft associated with other payload types.
* It has no need to accept anything other than `application/json` payloads, but you can easily extend it to
* By default it will respond with `application/json` as well, but allow it be override-able if needed
* Should be trivial to reject large payloads to prevent DOS attacks
* Each route should have the ability to have multiple placeholders, regardless of the payload type
* It shouldn't mutate the built-in request and response objects
* It should be as fast as possible

I 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
various other packages.

There are some scripts used for the (extremely reductive) benchmarking in `/benchmark`. Using my Core-i7, I get the following data using `wrk -t12 -c400 -d30s http://localhost:3000/test`. You might see different results. As with all benchmarks, these are likely indicative of nothing!

```
take-five: Requests/sec: 20296.63
express: Requests/sec: 10974.18
restify: Requests/sec: 9201.09
```

## License
Copyright © 2018 Scripto LLC, Todd Kennedy. Reuse permitted under the Apache-2.0 license
6 changes: 0 additions & 6 deletions benchmark/express.js

This file was deleted.

15 changes: 0 additions & 15 deletions benchmark/package.json

This file was deleted.

6 changes: 0 additions & 6 deletions benchmark/restify.js

This file was deleted.

6 changes: 0 additions & 6 deletions benchmark/take-five.js

This file was deleted.

Loading

0 comments on commit fbbfabf

Please sign in to comment.