Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/rune/aileron
Functional Rest API framework for Connect / Express
https://github.com/rune/aileron
connectjs middleware nodejs router
Last synced: about 1 month ago
JSON representation
Functional Rest API framework for Connect / Express
- Host: GitHub
- URL: https://github.com/rune/aileron
- Owner: rune
- Created: 2017-01-05T18:32:12.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2022-12-06T15:26:24.000Z (about 2 years ago)
- Last Synced: 2024-11-20T07:23:16.491Z (about 1 month ago)
- Topics: connectjs, middleware, nodejs, router
- Language: JavaScript
- Homepage:
- Size: 201 KB
- Stars: 2
- Watchers: 6
- Forks: 2
- Open Issues: 5
-
Metadata Files:
- Readme: readMe.md
Awesome Lists containing this project
README
# Aileron
[![Build Status](https://travis-ci.org/rune/aileron.svg?branch=master)](https://travis-ci.org/rune/aileron.svg?branch=master)
Aileron simplifies an API server to these steps:
- The connect server maps `URL pattern` strings to `handler` functions.
- If the `handler` returns a value (typically a JSON object), this is sent as a 200 response.
- If the `handler` throws an error, this is sent as a 500 response.Additional features:
- `URL pattern` strings support `:wildcards`, which are useful for specifying IDs in the URL string for example.
- You can specify different `handler` functions for different request methods (GET, POST, PUT, PATCH, DELETE).
- You can customize the centralized `successHandler` and `errHandler` functions to perform tasks like logging, specifying different status codes etc.
- You can specify type definitions for `inputs` to APIs. Aileron will check requests and reject incorrect inputs with a 409 response. This response can also be customized through a centralized `badInputHandler` function.Middlewares:
- For tasks like authentication, we require a way to create a "gatekeeper" functions, that allow only some requests through.
- For this, aileron allows you to define a `middleware`.
- Middleware are different from normal routes in two ways:
- You can use `middleware` when you only want to match against the beginning of the URL, rather than exact matches.
- When a middleware `handler` function returns, we don't send a response, we call `next()`, sending the request forward along the connect server chain.## Typical Use
```javascript
const connect = require("connect")
const aileron = require("aileron")const { router, middleware } = aileron()
const teamDetails = {
get: {
errMsg: "Unable to retrieve team details"
handler: async (req, data) => {
const teamDetails = await getTeamDetails(data.teamId)
return { id: data.teamId, teamDetails }
}
},
put: {
errMsg: "Unable to update team details"
handler: async (req, data) => {
const result = await updateTeamDetails(data.teamId, data.teamList)
return result
}
}
}const authMiddleware = {
errMsg: "Unauthorized request",
handler: (req, data) => {
const isAuthorized = await authorize(req)
}
}let app = connect()
app
.use(middleware("/api/:apiVersion"), authMiddleware)
.use(router("/api/:apiVersion/team/:teamId", teamDetails))
```## Router
```javascript
router(urlFormat, routeConfig)
```- `urlFormat` is a string URL, where you can have `:wildcard` placeholders by prefixing a `:`
```javascript
// urlFormatExample
"/api/:apiVersion/authenticate"
```
- `routeConfig` is an object containing a handler for each supported request method.
```javascript
// route config example
const routeConfig = {
post: // Request method
{
inputs: // Input type definitions,
errMsg: // Error message string,
handler: (req, data) => {
// Function that returns a value.
// Returned value is passed to the successHandler which sends a response
// If an err is thrown, it is passed errHandler which sends a response
}
}
}
```
- Each handler receives `(req, data)`
- If the URL exactly matches the `urlFormat`, the handler for the corresponding `req.method` is called.
- If the matching fails, `next()` is called.
- Each route allows you to specify the `inputs` it receives and their types. If inputs are missing / incorrect, aileron will automatically invoke `badInputHandler` with a detailed error object. For advanced input validation, see the `Input Checking` section.
- Each handler function is passed a `data` parameter. This will contain the wildcard values and the parsed inputs, ready for use.For example:
```javascript
const loginApi = {
post: {
inputs: { username: "String", password: "String" },
errMsg: "Unable to login. Please try again!",
handler: async (req, data) => {
const userDetails = await loginUser(data.username, data.password, data.apiVersion)
return {message: "Login successful", userDetails}
}
}
}let app = connect()
app
// Other routes and middleware
.use(...)
.use(...)
// The team route
.use(router("/api/:apiVersion/team/:teamId", teamApi))
// Other routes and middleware
.use(...)
.use(...)
```- Note that the URL format that you provide decides the key (`apiVersion`) under which the variable is made available to the handler function.
## Middleware
```javascript
middleware(urlFormat, routeConfig)
```Very similar to router, so we only explain the differences:
- If the beginning of the URL matches the provided `urlFormat`, the middleware function is called.
- If the `handler` function returns, `next()` is called.
- If the `handler` function throws, the `errHandler` is called.For example:
```javascript
const printRequestInfo = (req, data) => {
console.log(req.method, req.url, data.apiVersion)
}let app = connect()
app
.use(middleware("/api/:apiVersion", printVersionNumber))
// other middleware / routes follow
.use(...)
.use(...)
```- Note that the URL format that you provide decides the key (`apiVersion`) under which the variable is made available to the middleware function.
### Input checking
- As mentioned above, aileron supports input checking by simply configuring an "inputs" object and an error message.
- Aileron uses the [type-check](https://www.npmjs.com/package/type-check) library to validate inputs. Check the library docs for a list of valid type definitions.
- For advanced input checking, aileron allows you to define an `inputCheck` function. This function receives all the parsed inputs specified in your `inputs` object. Simply throw an error inside this function and `badInputHandler` will be called with the thrown error.
- For optional inputs, you can use `?` to specify they're optional. `{age: "Number | Undefined"}` can be written as `{age: "Number?"}````javascript
const inputCheckingController = {
post: {
inputs: { name: "String", age: "Number" },
inputCheck: parsedInputs => {
// Custom check to disallow the name "Jon Snow"
if (parsedInputs.name === "Jon Snow") {
throw "You know nothing, Jon Snow"
}
},
errMsg: "Unable to process your request.",
handler: (req, res, next, data) => {
const { name, age } = data
res.ok().json({ name, age })
}
}
}
```## Error handling
- Aileron automatically wraps all your middlewares / handlers in a try-catch block and sends an error response if an uncaught error occurs.
- This prevents your node server from crashing :)
- Aileron allows you customize these error handlers.
- You can supply an `errHandler` and a `badInputHandler` when you initialize Aileron.
- Here is an example:```javascript
// MyCoolProject
const { router, middleware } = aileron({
badInputHandler: (req, res, err, errMsg) =>
res.forbidden().json({ err, message: "Bad Input: " + errMsg }),
errHandler: (req, res, err, errMsg) =>
res.error().json({ err, message: "Uncaught error!!" })
})
```## Strict Mode
- Aileron allows a strict mode, where it will force you to provide `inputs` and `errMsg` for all handlers.
- Strict mode will also complain if you have not supplied custom error handlers.
- This ensures that error handling code is always supplied for every API in your project.
- To enable strict mode, simply supply it as an option when you initialize aileron.```javascript
// MyCoolProject
const { router, middleware } = aileron({
strict: true,
badInputHandler: (req, res, err, errMsg) =>
res.forbidden().json({ err, message: "Bad Input: " + errMsg }),
errHandler: (req, res, err, errMsg) =>
res.error().json({ err, message: "Uncaught error!!" })
})
```