README
Apone
Configure and validate express routes with objects. Register routes in any order. Extend as necessary.
Install and Quick Start
npm install apone
const Express = require('express')
const Apone = require('apone')
let app = Express()
let apone = new Apone(app)
apone.register({
path: '/hello',
method: 'get',
handle: (req, res, next) => {
res.send('world')
return next()
}
})
app.listen(3000)
table of contents
API
new Apone(app, options)
app
-Express
instanceoptions
-object
(optional)prefix
-string
prepends all routes without prefixsextensions
-array
of extensions
- throws
AponeValidationError
for invalid options
register(routes, state)
routes
-array|object
state
-object
(optional) if a route declareshandle(state)
this will be passed in (simple DI)- throws
AponeValidationError
for invalid or duplicate routes
Routes
By defining the following properties, routes opt-into different behaviors. Each property name resolves to a function and each property value is an argument to that function. Custom properties, or extensions, can be specified in Apone's constructor options.
Properties
method
-string
http verbs: GET, POST, PATCH, DELETEpath
-string
endpoint at whichhandle
will be invoked (no regex allowed)handle
-function|array
middleware OR function which returns middleware and accepting one potential argument. If an argument is specified,state
(from the register method) is passed invalidation
-object
object with up to three properties, each a valid schemaparams
-Schema
query
-Schema
body
-Schema
prefix
-string
path prefix which takes priority over Apone's constructor option global prefixmetadata
-object
- flexible bucket for anything else
example:
let route = {
method: 'PUT',
path: '/puppers/:id',
metadata: {
favorite: true
},
prefix: '/api/v2',
validation: {
params: Joi.object().keys({ id: Joi.number().integer().min(1).max(1).required() }),
body: {
validate: (query) => {
// can coerce a more complicated type here, and return valid payload only
if (query.name && typeof query.name === 'string') {
return { value: { name: query.name } }
}
return new { error: Error('name is required!') }
}
}
},
// injected dependency makes for easy testing
handle: (state) => [
// if you regularly need internalOnly, create an extension!
state.internalOnly,
(req, res, next) => {
res.send(state.pupperRepository.update(req.body))
return next()
}
]
}
Schemas
Schemas are used to validate user requests. Joi is reccomended, but any schema can be used. Coerce and sanitize requests here.
Properties
validate
-function
which must return a ValidationResult
example:
let schema = {
validate: (body) => {
if (body.id === 1) {
return { value: { id: body.id } }
}
return { error: new Error('value was not 1!') }
}
}
ValidationResult
Returned by schema validation
Properties
error
-Error
objectvalue
-any
coerced and sanitized result
example:
let validationResult = {
error: isValid ? null : new Error(),
value: isValid ? value : null
}
Extensions
Extensions are custom route properties. They must be defined in the Apone constructor options and will execute in their array order, according to request behavior.
Properties
name
-string
used by routes to opt into this extension behaviorfactoryFunc
-function
invoked by Apone during route registration, returning middleware which is inserted into request behavior.type
-string
defaults topre
, or optionallypost
.
example:
The following extension adds a traceId to each request prior to validation. First, Apone is instantiated with the trace
extension. During registration, trace(true)
is called and the return function is added to the stack. Extensions execute in order, by type.
const Express = require('express')
const Apone = require('apone')
let app = Express()
let extensions = [{
name: 'trace',
factoryFunc: (doTag) => (req, res, next) => {
if (doTag) {
res.locals.traceId = Math.random()
return next()
}
else {
next()
}
}
}]
let apone = new Apone(app, { extensions })
apone.register({
path: '/hello'
method: 'get',
trace: true,
handle: (req, res, next) => {
console.log(res.locals.traceId) // 0.23456...
res.send('world')
return next()
}
})
Route Behavior
The lifecycle of middleware steps assembled by Apone for routes
- Apone appends the finished route object to
res.locals.route
for logging, etc pre
extensions- request
param
validation - request
query
validation - request
body
validation handle
middlewarepost
extensions
Contributing
Contributions are welcome.
Reporting Bugs
Please open an issue and describe the situation clearly. Include reproduction instructions, expected outcomes, and project version number.
Pull Requests
- One feature per request
- Descriptive commit messages
- Test coverage is manditory
- Pass project linter
FAQ
- If I use this with an existing application will the dupe check work
- Not currently
- Any plans to expand the framework?
- Apone was designed to be small and flexible, but feel free to open an issue
- What about express-router
- You can probably live without it. This is a simple alternative.
- How do I pronounce it
- like in Aliens