A minimal REST server that deals with JSON payloads by default, automatically handles CORS requests, and limits the size of a POST bodies written for Deno
import {TakeFive} from 'https://static-pkg.dev/take-five/{VERSION}/mod.ts'
const five = new TakeFive()
five.get('/', (_, res) => {
res.body = 'Hello, world'
return Promise.resolve(res)
})
five.post('/', (req, res) => {
res.headers.set('content-type', req.headers.get('content-type'))
res.body = req.body
return Promise.resolve(res)
})
five.listen(3000)
curl -X GET localhost:3000
Hello, world
curl -X POST localhost:3000 -H 'content-type: application/json' -d '{"hello": "world"}'
{"hello": "world"}
In lieu of pre-set middleware, routes handlers can be arrays of functions that will
be iterated over asynchronously. To simplify handling of these handlers,
your handler must return a Promise
.
If you would like to abort the remainder of the handlers, you can set res.finished = true
. This will cause the remainder of the handlers to be ignored and the response to emitted immediately after the current handler resolves.
If you do not return a promise
it won't work!
e.g.:
function badSetContentHeader (req, res, ctx) {
res.headers.set('x-no-way', 'this is gonna do nothing')
}
function goodSetContentHeader (req, res, ctx) {
res.headers.set('x-yes-way', 'this is gonna do everything!')
return Promise.resolve(res)
}
five.get('/nope', [badSetContentHeader])
five.get('/yup', [goodSetContentHeader])
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.
five.handleError = async (err, req, res, ctx) => {
ctx.finished = true
return ctx.err(err.statusCode, err.message)
}
five.get('/:secretdata', [
async (req, res, ctx) => {
try {
const session = await isAuthorized(req.headers.Authorization)
ctx.session = session
} catch (err) {
if (err.message = "Unauthorized")
res.status = 401
res.body = 'Unauthorized'
return res
} else {
throw err
}
}
},
async (res, res, ctx) => {
try {
const data = await getResource(ctx.params.secretdata, ctx.session)
res.body = data
res.headers.set('content-type', 'application/json')
return res
} catch (err) {
throw err
}
}
])
Throwing an error will attempt to call the user-supplied ErrorHandler
method. If none is supplied this will return a 500 error with Internal Server Error
as the default.
Take-Five supports the ability to automatically parse the payload from a string
into a data structure, and then stringify that data structure to go back out. By
default it handles application/json
using the built in JSON.parse
and JSON.stringify
methods.
You can supply (or override) what happens for a specific content type, by calling
addParser
method, and supplying a Parser<T>
object.
e.g.:
import {Parser} from './parsers.ts'
type stringArray = string[]
const StringArrayParser:Parser<stringArray> = {
toStructure: (content:string, route:string):stringArray => {
return content.split(';')
},
toString: (content:stringArray, route:string):string => {
return content.join(';')
}
}
five.addParser('application/vnd.stringarray', StringArrayParser)
When a request with the content-type application/vnd.stringarray
is recieved, it'll automatically call the toStructure
method from the parser, and if the response has the same content-type, it'll call the toString
method to return the data to the client.
Create and return a new HTTP server object.
opts.maxPost?:number
: the max size for a payload. Default: 512kbopts.allowHeaders?:string|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, soapplication/json
will ALWAYS be allowed.opts.allowMethods?string|string[]: an array of methods to accept besides the default. Default:
PUT,
POST,
DELETE,
GET,
OPTIONS,
PATCH`opts.methods?string|string[]
: An array of methods to create route handlers for. Default:PUT
,POST
,DELETE
,GET
,OPTIONS
,PATCH
opts.http?HTTPOptions
: options forhttp(s).createServer
. If you supplykeyFile
andcertFile
as options, it will assume you are trying to create an https server`
This almost Partial<HTTPSOptions>
from deno. See the deno docs for more info.
http.port:number
: port to listen to. This can be supplied to thelisten
method as well.http.addr?:string
: address on which to listenhttp.certFile?:string
: path to the HTTP certificatehttp.KeyFile?:string
: path to the HTTP key file
Access-Control-Allow-Headers
and Access-Control-Allow-Methods
can also be changed during runtime
by setting allowHeaders
and allowMethods
respectively.
Five#patch(route:string, handler:RouteHandler|RouteHandler[], ctxOpts?:{[key: string]: any}) => void
Five#delete(route:string, handler:RouteHandler|RouteHandler[], ctxOpts?:{[key: string]: any}) => void
Five#options(route:string, handler:RouteHandler|RouteHandler[], ctxOpts?:{[key: string]: any}) => void
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 route definition.handler:RouteHandler|RouteHandler[]
: The handler for this route.routeOpts?:{[key: string]: any}
- overrides for this specific chain of handlers or add a specific context for this handlermaxPost:number
- set the maximum size of a payload for this set of handlersallowedContentTypes:string|string[]
- add new allowable content-types for this set of handler
type RouteHandler = (req: ServerRequest, res: Response, ctx: TakeFiveContext) => Promise<void>
body?: any
: the body will be parsed by this point, if there is a parser for itfinished: boolean
: if this is true, no further handlers will be processedmaxPost?: number
: the maximum body size for this routeallowContentTypes?: string[]
: allowed content types for this routequery: {[key: string]: string}
: the query string, parsed into an objectparams: {[key: string]: string}
: the route params, parsed into an object[key: string]: any
: any additional user-defined parameters }
The ctx
object can also be extended to contain user defined objects, through
setting this.ctx
to an object. The object will be copied over using Object.assign
.
The keys from above will overwrite any keys you provide named the same.
Five#handleError(err: Error, req: ServerRequest, res: Response, ctx: TakeFiveContext) => Promise<Response>
This is a no-op by default, allowing you to customize it's behavior.
If you do not modify the res
object, the default will be 500 Internal Service Error
Start listening for requests and call the optional callback when you've started listening
Add a new content parser to the parsers list. By default there is only a single parser installed. The parser method is called with the content to be parsed and the URL of the request. This allows you to have specific encodings per-route if you are using something like Protocol Buffers
Shutdown the underlying server
The underlying http(s) server from deno can be accessed directly. This is non-writeable
Globally control the maximum payload size after creation
Add new allowable content types for clients to send data with. You can use either an array of strings or a string
Set a new allowable header or headers for CORS requests. You can use either an array of strings or a string.
Set a new allowable method for CORS requests.
Add new keys to the ctx objects
Copyright © 2018 Scripto LLC, Copyright © 2018-2021 Todd Kennedy. Reuse permitted under the Apache-2.0 license
Includes router.ts and trie.ts based on wayfarer, which is © Yoshua Wuyts