https://github.com/protectwise/apone
Configure and validate express routes with objects. Register routes in any order. Extend as necessary.
https://github.com/protectwise/apone
Last synced: about 1 year ago
JSON representation
Configure and validate express routes with objects. Register routes in any order. Extend as necessary.
- Host: GitHub
- URL: https://github.com/protectwise/apone
- Owner: protectwise
- License: mit
- Created: 2017-08-25T20:53:37.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2018-08-29T00:11:50.000Z (almost 8 years ago)
- Last Synced: 2025-03-22T16:02:07.425Z (over 1 year ago)
- Language: JavaScript
- Homepage:
- Size: 27.3 KB
- Stars: 6
- Watchers: 7
- Forks: 3
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Apone
Configure and validate express routes with objects. Register routes in any order. Extend as necessary.
## Install and Quick Start
`npm install apone`
```js
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](#api)
- [routes](#routes)
- [schemas](#schemas)
- [validationResults](#validationresult)
- [extensions](#extensions)
- [route-behavior](#route-behavior)
- [contributing](#contributing)
- [faq](#faq)
# API
### `new Apone(app, options)`
- `app` - `Express` instance
- `options` - `object` (optional)
- `prefix` - `string` prepends all routes without prefixs
- `extensions` - `array` of [extensions](#extensions)
- throws `AponeValidationError` for invalid options
### `register(routes, state)`
- `routes` - `array|object`
- `state` - `object` (optional) if a route declares `handle(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](#extensions), can be specified in Apone's [constructor options](#api).
## Properties
- `method` - `string` http verbs: GET, POST, PATCH, DELETE
- `path` - `string` endpoint at which `handle` 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](#api) method) is passed in
- `validation` - `object` object with up to three properties, each a valid [schema](#schemas)
- `params` - `Schema`
- `query` - `Schema`
- `body` - `Schema`
- `prefix` - `string` path prefix which takes priority over Apone's [constructor option](#api) global prefix
- `metadata` - `object` - flexible bucket for anything else
### example:
```js
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](https://github.com/hapijs/joi) is reccomended, but any schema can be used. Coerce and sanitize requests here.
## Properties
- `validate` - `function` which must return a [ValidationResult](#validationresult)
### example:
```js
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](#schemas) validation
## Properties
- `error` - `Error` object
- `value` - `any` coerced and sanitized result
### example:
```js
let validationResult = {
error: isValid ? null : new Error(),
value: isValid ? value : null
}
```
# Extensions
Extensions are custom [route properties](#routes). They must be defined in the Apone [constructor options](#api) and will execute in their array order, according to [request behavior](#route-behavior).
## Properties
- `name` - `string` used by routes to opt into this extension behavior
- `factoryFunc` - `function` invoked by Apone during route registration, returning [middleware](#https://expressjs.com/en/guide/using-middleware.html) which is inserted into [request behavior](#route-behavior).
- `type` - `string` defaults to `pre`, or optionally `post`.
### 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.
```js
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
1. Apone appends the finished route object to `res.locals.route` for logging, etc
2. `pre` [extensions](#Extensions)
3. request `param` validation
4. request `query` validation
5. request `body` validation
6. `handle` middleware
7. `post` [extensions](#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
1) If I use this with an existing application will the dupe check work
- Not currently
2) Any plans to expand the framework?
- Apone was designed to be small and flexible, but feel free to open an issue
3) What about express-router
- You can probably live without it. This is a simple alternative.
4) How do I pronounce it
- like in [Aliens](https://www.youtube.com/watch?v=woB1zvaSXag)