A basic HTTP-Router with zero dependencies. Made for NodeJS-HTTP(s)-Server but the route-handling only depends on request.url
and request.method
so it should work with anything. The Middlewares require response.write()
, response.end()
and response.writableEnded
.
const NodeHttp = require('http');
const {Router} = require('yaf-router')
const router = new Router()
// GET /bestUser/foobar => "Hello bestUser, foobar"
router.addRoute('/{user}/{msg}', ({parameter, response}) => {
response.write(`Hello ${parameter.user}, ${parameter.msg}`)
response.end()
})
const server = NodeHttp.createServer((request, response) => {
router.handle(request, response)
});
server.listen(8080);
The HandlerData stores the Parameter, Request, Response and almost always the written data.
interface HandlerData {
parameter: {[key: string]: string},
request: NodeHttp.IncomingMessage,
response: NodeHttp.ServerResponse,
written?: Array<any>
}
A Route consists of a Method and URL: GET /hallo/User
. The Method is optional and defaults to GET
.
router.addRoute('/me', ({parameter, response}) => {
// ...
response.end()
})
// is the same as
router.addRoute('GET /me', ({parameter, response}) => {
// ...
response.end()
})
You can define multiple Routes for the same Handler by passing an Array of Routes
router.addRoute(['/me', 'POST /you'], ({parameter, response}) => {
// handles: GET /me and POST /you
response.end()
})
Parameter are part of the URL like /search/{query}
.
// GET /search/foobar => "Searching: foobar"
router.addRoute('/search/{query}', ({parameter, response}) => {
response.write(`Searching ${parameter.query}`)
response.end()
})
Order and count of Parameter do not matter. There may be multiple Paramater in a row.
// GET /user/bestUser/foobar => "Hello bestUser, foobar"
router.addRoute('/user/{user}/{msg}', ({parameter, response}) => {
response.write(`Hello ${parameter.user}, ${parameter.msg}`)
response.end()
})
Wildcard-Parameter are defined by the URI-Part /*
and have to come last. Wildcard-Parameter are accessed by the *
-Key. Normal Parameter are prioritized and matched first.
// GET /wildcard/BestUser/foo/bar => "BestUser is trying to access foo/bar"
router.addRoute('/wildcard/{user}/*', ({parameter, response}) => {
response.write(`${parameter.user} is trying to access ${parameter['*']}`)
response.end()
})
If there are multiple Parameter with the same key in a URL only the last value will be used. In the future this case may be handled as an Array
.
// GET /search/foo/spacing/bar => "Searching: bar"
router.addRoute('/search/{search}/spacing/{search}', ({parameter, response}) => {
response.write(`Searching: ${parameter.search}`)
response.end()
})
A Middleware implements the MiddlewareInterface
. Middlewares are called after routing so it is not possible to reroute via Middlewares.
class CorsMiddleware implements MiddlewareInterface {
getTypes() {
return [
MiddlewareOrderType.Header
]
}
getName() {
return 'CorsMiddleware'
}
async handleByType(type: MiddlewareOrderType, handlerData: HandlerData, path: string = '') {
if(type !== MiddlewareOrderType.Header) {
return
}
handlerData.response.setHeader('Access-Control-Allow-Origin', '*');
handlerData.response.setHeader('Access-Control-Request-Method', '*');
handlerData.response.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
handlerData.response.setHeader('Access-Control-Allow-Headers', '*');
}
}
The order of Middleware execution and their suggested usage is:
- Security: Makes security checks and ends the response if needed
- Header: Only adds header to the response
- Data: Enriches the HandlerData with additional information
- Result: Manipulates the Response
- Finisher: Can be used for caching for example
Every Handler can end the Response but only expected should do so.
A simple Cache implementation. Uses MiddlewareOrderType.Data
if cache hit and MiddlewareOrderType.Finisher
to fill cache. Default Cache-TTL is 60 seconds.
Only caches GET, OPTIONS and HEAD methods.
A simple Middleware that adds CORS-Header.