Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fatmatto/express-toolkit
Auto generate REST endpoints on top of express and mongoose
https://github.com/fatmatto/express-toolkit
api express http mongodb mongoose rest
Last synced: about 23 hours ago
JSON representation
Auto generate REST endpoints on top of express and mongoose
- Host: GitHub
- URL: https://github.com/fatmatto/express-toolkit
- Owner: fatmatto
- Created: 2019-04-25T06:27:43.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2025-01-16T03:06:19.000Z (11 days ago)
- Last Synced: 2025-01-16T04:22:00.308Z (11 days ago)
- Topics: api, express, http, mongodb, mongoose, rest
- Language: JavaScript
- Homepage: https://fatmatto.github.io/express-toolkit
- Size: 2.89 MB
- Stars: 7
- Watchers: 4
- Forks: 1
- Open Issues: 23
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# Express Toolkit
Tiny little utilities for reducing expressjs boilerplate code when building simple, mongodb backed, http apis
[![Build Status](https://travis-ci.org/fatmatto/express-toolkit.svg?branch=master)](https://travis-ci.org/fatmatto/express-toolkit)
[![Maintainability](https://api.codeclimate.com/v1/badges/40cff05fa81b87114ae2/maintainability)](https://codeclimate.com/github/fatmatto/express-toolkit/maintainability)
[![codecov](https://codecov.io/gh/fatmatto/express-toolkit/branch/master/graph/badge.svg)](https://codecov.io/gh/fatmatto/express-toolkit)- [Express Toolkit](#express-toolkit)
- [TL;DR](#tldr)
- [Models, Controllers and Routers](#models-controllers-and-routers)
- [Default endpoints](#default-endpoints)
- [Disable endpoints](#disable-endpoints)
- [Sorting](#sorting)
- [Pagination](#pagination)
- [Projection](#projection)
- [Custom primary key](#custom-primary-key)
- [Hooks](#hooks)
- [List of hooks](#list-of-hooks)
- [Examples](#examples)
## TL;DRWith express-toolkit you can easily create a basic REST resource and mount it into an express application. The app will provide basic CRUD methods:
```javascript
const express = require('express')
const Resource = require('../../src/resource')
const mongoose = require('mongoose')// Let's create our Model with Mongoose
const schema = new mongoose.Schema({
name: String
})const PetsResource = new Resource({
name: 'pets',
id: 'uuid',
model: mongoose.model('pets', schema, 'pets')
})PetsResource.registerHook('pre:find', (req, res, next) => {
console.log('Looking for Pets')
next()
})// Remember to extend the router AFTER adding hooks,
// otherwise the router will be overwritten without this route
PetsResource.router.get('/actions/eat',(req,res) => {
return res.send('Om nom nom')
})// Now the Express related stuff
const app = express()
const port = 3000app.get('/', (req, res) => res.send('Hello World!'))
// Here we mount the Pets resource under the /pets path
PetsResource.mount('/pets', app)// After mongoose is ready, we start listening on the TCP port
mongoose.connect('mongodb://localhost:27017/pets', {})
.then(() => {
console.log('Connection to Mongodb Established')
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
})
.catch(error => {
console.log('Unable to establish connection to Mongodb', error)
})```
Under the hood, the resource object uses three other objects: Model, Controller and Router.
- Routers are plain express routers to which the library mounts some default REST routes
- Controllers implement CRUD methods
- Models define a mongoose modelWhen you create a resource object, the library will create a model a controller and a router for you, if you need to add custom logic to those components you can retrieve them as properties of the resource:
```javascript
// Let's create our Model with Mongoose
const schema = new mongoose.Schema({
name: String
})const PetsResource = new Resource({
name: 'pets',
id: 'uuid',
model: mongoose.model('pets', schema, 'pets')
})// Let's add a hook to the controller
const ctrl = PetsResource.controller// more on this method later in this document
ctrl.registerHook('post:create',(req,res,next) => {
console.log("Hello I created a resource")
next()
})// Let's add a custom route to the router
const router = PetsResource.routerrouter.get('/hello/world',(req,res,next) => {
res.send("Hello")
})
```## Models, Controllers and Routers
Suppose we need to build an http microservice for handling dinosaurs (tired of cats).
First of all we will need a model file, powered by mongoose
```javascript
const mongoose = require('mongoose')
const Schema = mongoose.Schema// Dinosaurs are simple
const DinosaurSchema = new Schema({
name: {
type: String,
required:true
},
type: {
type: String,
required: true
}
})const DinosaurModel = mongoose.model('Dinosaur', DinosaurSchema, 'dinosaurs')
module.exports = {DinosaurSchema, DinosaurModel}
```Then the controller file
```javascript
const { Controller } = require('express-toolkit')
const { DinosaurModel } = require('./path/to/dinosaur.model.js')const myController = new Controller({
model: DinosaurModel,
name: 'dinosaurs'
})
module.exports = myController
```Finally the router file
```javascript
const { buildRouter } = require('express-toolkit')
const DinosaurController = require('./dinosaur.controller.js')module.exports = buildRouter({
controller: DinosaurController,
options: {} // See expressJS router options
})
```Then, somewehere in your express app, where you mount routes:
```javascript
const express = require('express')
const app = express()
const dinosaursResource = require('./path/to/dinosaur.router.js')
//...
app.use('/dinosaurs',dinosaursResource)
// ...
app.listen(1337)
```
## Default endpointsIn the following table, every path showed in the Path column is meant to be appended to the resource base path which simply is `/`. Following the dinosaurs examples, would be `/dinosaurs`
| Name | Http verb | Path | Description |
| ------------- | --------- | --------------- | -------------------------------------------------------------------------------------- |
| Create | POST | / | Creates a new resource and returns it. |
| List | GET | / | Get a paginated and filtered list of resources of the given type |
| GetById | GET | /{uuid} | Get a resource by id |
| UpdateById | PUT | /{uuid} | Updates a resource |
| UpdateByQuery | PUT | / | Updates resources that matches query parameters |
| PatchById | PATCH | /{uuid} | Updates a resource by id using PATCH semantics |
| ReplaceById | PUT | /{uuid}/replace | Replaces a resource by id. Primary id field (and _id if not the same) are not replaced |
| DeleteById | DELETE | /{uuid} | Deletes a resource |
| DeleteByQuery | DELETE | / | Deletes resources matching filters in the querystring |
| Count | GET | / | Count resources in collection matching filters in the querysting |## Disable endpoints
By default, all endpoints are enabled, to control which endpoints should be disabled, you can use the `endpoints` router parameter
```javascript
// Default behaviour, endpoints is an optional parameter
const router = buildRouter({
controller: require('./mycontroller.js'),
endpoints: {
find: true,
findById: true,
create: true,
updateById: true,
updateByQuery: true,
deleteById: true,
deleteByQuery: true,
count: true,
patchById: true,
replaceById: true
}
})
// Default resource deletion
const router = buildRouter({
controller: require('./mycontroller.js'),
endpoints: {
deleteById: true,
deleteByQuery: true
}
})
```## Sorting
GET endpoints support result sorting thanks to two query string parameters:
- `sortorder ` that can only have two values: `DESC` for descending sorting and `ASC` for ascending order.
- `sortby ` can be used to select the sorting parameter.For example, the following request would sort dinosaurs by age, oldest to youngest:
```http
GET /dinosaurs?sortby=age&sortorder=DESC
```
## Pagination
GET endpoints support result pagination through `skip` and `limit` parameters:
- `skip ` tells the endpoint how many results to skip
- `limit `
- `middleware`
- `post:`
- `post:*`
- `pre:finalize`
- `finalize`For example, you might want to check the `Accept` HTTP header and convert the response from JSON to YAML, or XML.
### Examples
```javascript
const { Controller } = require('express-toolkit')
const { DinosaurModel } = require('./path/to/dinosaur.model.js')const myController = new Controller({
model: DinosaurModel,
name: 'dinosaurs'
})// Check authorization on all dinosaurs routes:
myController.registerHook('pre:*', (req,res,next) => {
//This is just an example, a bad auth example.
if (req.headers.authorization !== "supersecret") {
return res.sendStatus(401)
}
next()
})// Force all find queries to look for velociraptor type
myController.registerHook('pre:find', (req,res,next) => {
req.query.type = 'velociraptor'
next()
})// Before returning dinosaurs to the client we convert timestamps to date strings
myController.registerHook('post:find', (req,res,next) => {
req.toSend = req.toSend.map(dinosaur => {
let dino = Object.assign({},dinosaur)
dino.createdAt = String(new Date(dino.createdAt))
return dino
})
next()
})module.exports = myController
```