• Stars
    star
    160
  • Rank 233,383 (Top 5 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 10 years ago
  • Updated about 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Serve Mongoose models on a RESTful API.

Preston

Piece-of-cake REST on Node.

Preston serves Mongoose models on an extensible RESTful API. It handles routing and provides extensibility.

Stories in Ready build status Coverage Status

Features at a Glance

  • Tight integration with Mongoose and Express.
    • An Express middleware. Put it on its own route and the rest of your code is left untouched.
    • Configured within the Mongoose schema. No need to deal with messy configuration objects.
  • Query/Create/Get/Update/Destroy
    • Everything you'd ever need from a REST API (other than auth) is already included.
    • Middleware supported on each route, so integration with things like Passport is very simple
  • Flexible query filtering system.
  • Document transformer system. Control what gets sent to which clients.
  • Built with Angular in mind.

Installation

This module is installed via npm:

$ npm install preston --save

Example

The following example serves the User and Badge models on a RESTful API.

var express = require('express');
var preston = require('preston');
var mongoose = require('mongoose');

var app = express();

app.use(require('body-parser').json()); // Required

var User = preston(mongoose.model('User', new mongoose.Schema({
  name: {
    type: String,
    id: true, // The id used in the route
    unique: true
  },
  password: {
    type: String,
    restricted: true // Don't display this field to anyone!
  }
})));

// A nested route
var Badge = User.submodel('badges', 'owner', mongoose.model('Badge', new mongoose.Schema({
  owner: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  },
  id: {
    type: Number,
    id: true,
    unique: true
  },
  content: String
})));

app.use('/api', preston.middleware()); // Serve the api on /api.

app.listen(3000);

REST API

Preston uses the MongoDB collection name to determine the name of the base route, so the User model would create routes under /users.

Query

Querying takes in the following parameters:

  • field - Replace field with any field in your Mongoose model, and it will check for equality.
  • populate - Comma-delimited list of fields to populate
  • sort - Sorts by the given fields in the given order, comma delimited. A - sign will sort descending.
  • limit - Limits the number of returned results.
  • skip - Skips a number of results. Useful for pagination when combined with limit.
  • filter - Applies a filter. See the Filters section for more details.
GET /users
GET /users?field=value
GET /users?populate=posts,comments
GET /users?sort=field,-field2
GET /users?limit=10&skip=10
GET /users?filter=filter1|filter2
GET /users/Bob/badges?sort=date

Create

POST /users
POST /users/Bob/badges

Get

Get supports one parameter, the populate field.

GET /users/Bob
GET /users/Bob?populate=posts
GET /users/Bob/badges/1
GET /users/Bob/badges/1?populate=things

Update

PUT and PATCH are handled the same way.

PUT /users/Bob
PATCH /users/Bob
PUT /users/Bob/badges/1
PATCH /users/Bob/badges/1

Destroy

DELETE /users/Bob
DELETE /users/Bob/badges/1

Creating an API

First, declare all of your models using preston(mongooseModel). This function returns a Model object which can be altered. (see the JSDocs)

Next, serve the API as middleware:

app.use('/api', preston.middleware());

This will create a middleware that will be used by Express.

Namespace Collision

In the case of namespace collision, routes are handled sequentially by Express. Declare your custom routes before using the middleware. For example:

app.post('/api/login', myLoginHandler);
app.use('/api', preston.middleware());

is the appropriate way to add functionality to your API.

Route middleware

There are 5 types of routes: query, create, get, update, and destroy. You can apply middleware to a single one of these routes by doing the following:

model.use('get', function(req, res, next) {
  console.log('Get middleware on model ' + model.model.modelName + ' called!');
});

You can also apply middleware to all of a model's routes:

model.use('all', function(req, res, next) {
  console.log('Middleware on model ' + model.model.modelName + ' called!');
});

The following fields are exposed in the request object:

  • doc -- The document being retrieved, or null if not operating on a document route
  • parentDoc -- The parent document being retrieved. Used for nested routes.
  • req.query - The populate and sort fields are parsed beforehand, populate being an Array of Strings and sort being an object.

Error middleware can also be added to each route or for all routes:

model.use('get', function(err, req, res, next) {
  console.log('Error on model');
});

Authentication middleware example with Passport

Here is an example of using Passport to restrict access to a document:

model.use('get', function(req, res, next) {
  if (req.user._id !== req.doc.owner) {
    res.status(403).send('Unauthorized!');
  }
  return next();
});

Passport exposes a user property on the request, so we can deal with that directly in our middleware. If we were to use something like connect-roles, we would do something like this:

model.use('all', user.can('operate on the model'));

The Query Pipeline

Preston was designed to be very flexible so it could be used as a backend for any app. Thus, queries go through a series of steps before being transformed into what is sent to the client.

Modifiers --> Parameters --> Filters --> Population --> Execution --> Transformers

Modifiers

Modifiers alter the query parameters that will be passed to the pipeline. For example, you could have a modifier that forces sorting by name ascending, as shown below:

model.modifyParam('sort', function(req, value) {
  value.name = 1;
  return value;
});

To modify a parameter, just pass the name of the parameter you wish to modify and a callback that returns the modified value of the parameter.

sort and populate are the only parameters that are objects.

The sort parameter looks like this:

{
  name: 1, // Ascending
  date: -1 // Descending
}

The populate parameter looks like this:

['users', 'comments', 'posts']

Parameters

There are 4 types of parameters: limit, skip, sort, and field equality. These are all described in the Query section.

Filters

Filters are user-defined functions that modify the query. They work very similarly to AngularJS filters. They can be chained and take parameters, allowing immense flexibility for developers to add features to APIs.

Filters are defined as follows:

model.filter('children', function(req, query) {
  query.where('age').lt(18);
});

Here is an example of a filter that takes parameters:

model.filter('proximity', function(req, query, distance) {
  query.where('location').maxDistance(distance);
});

This filter would be called using proximity 5 if one wanted to check if the location was within a distance of 5.

Chaining filters is pretty simple; just use the | (pipe) operator to do so.

GET /people?filter=children | proximity 5

Population

Fields that were marked for population in the query are now populated. You can change what fields are returned using population transformers.

Execution

At this point in the pipeline, query.exec() is called and we query the database.

Transformers

Transformers change the returned results. One transformer is built in, the restricted transformer, and cannot be changed. Here is an example of using a transformer:

model.transform(function(req, doc) {
  delete doc._id;
  delete doc.password;
  doc.type = 'This is a string that isn\'t in the database!';
});

Transformers are applied to each individual document or document array in a query result.

Population Transformers

Population transformers are transformers that operate on populated fields. They can be used to make your application more secure by removing fields you don't want people to see.

model.transformPopulate('owners', function(req, doc) {
  delete doc._id;
  delete doc.password;
});

Model Setup Within Schema

You can also easily set up a model by declaring a setupPreston(model) static function within the schema like so:

MySchema.statics.setupPreston = function(model) {
  // Anything can go here modifying the model
  model.transform(function(req, doc) {
    doc.modifiedWithinModel = true;
  });
}

This is to keep your code organized if you like to have a separate file for each model.

AngularJS Integration

This software was built with Angular in mind. Use the module Restangular to deal with the generated API in a very intuitive manner.

Example Apps

Here are some apps that use Preston. If you have one you'd like to share, please don't be afraid to send a PR!

  • todo-preston - A Preston-powered Todo app made with Angular, Restangular, Bootstrap, and Preston.

Contributing

Contributions are very welcome! Just send a pull request. Feel free to contact me using one of the options on my website!

Running the Tests

Do npm install to install all of the dependencies, ensure that MongoDB is installed, then run npm test to run the unit tests.

Note: The tests like to fail on Travis because Travis's database stuff is slow.

License

Copyright (c) 2014 Ian Macalinao. Released under the MIT License, which can be viewed in the attached LICENSE file.

More Repositories

1

resonance-finder

Finds the resonant frequency of objects. Inspired by the XKCD comic.
Python
123
star
2

how-to-get-an-internship

A guide on how to get an internship in technology as a student.
121
star
3

motivate

Posts a daily motivational message in your terminal.
JavaScript
113
star
4

dotfiles

My dotfiles.
Nix
25
star
5

injecta

Inject JavaScript libraries into your current page.
JavaScript
19
star
6

sysadmincraft

Admin your server in Minecraft!
Scala
18
star
7

resume

My resume.
HTML
18
star
8

node-skyscanner

Skyscanner's public API for Node.js.
JavaScript
16
star
9

SNESJS

[DEFUNCT] Super NES emulator written in Javascript based on BSNES.
JavaScript
14
star
10

archie

NOTE: This was made before Yeoman came out. I suggest you use that instead. Simple archetype system for Node.js.
CoffeeScript
10
star
11

github-hack

Hello @mojombo followers!
8
star
12

shrug

Provides a way to shrug.
JavaScript
8
star
13

node-ctci

Cracking the Coding Interview questions implemented in Node.js
JavaScript
7
star
14

cloudgame

A minigame framework for the Bukkit API.
Java
7
star
15

IEEE754Converter

An app to convert IEEE-754 format to different representations.
Java
7
star
16

TeraSpout

[DEFUNCT -- Spout is dead] An alternative client for the Spout engine based on Terasology.
Java
7
star
17

macalinao.github.io-deprecated

My personal website.
HTML
6
star
18

koa-joi-schema

Koa middleware to validate input using Joi.
JavaScript
6
star
19

rarmy

A collection of Python scripts to create and manipulate multiple Reddit accounts.
Python
6
star
20

tripblaze

Trip creation app built for the Sabre Hackathon
JavaScript
5
star
21

studyblue-bypass

JavaScript
5
star
22

school

My school notes.
5
star
23

texembed

Embed TeX in your documents easily.
JavaScript
5
star
24

functionize

Turns an object into an object that can be called as a function.
JavaScript
5
star
25

ish

ian shell. A really crappy shell written in C++.
C++
4
star
26

ian-website

Personal website v4
TypeScript
4
star
27

licedate

Command-line license date updater
JavaScript
4
star
28

ajaxdict

AJAX dictionary that hooks into the Wiktionary API.
CSS
4
star
29

PersonasBanker

A simple banker for Personas.
Java
3
star
30

node-quizlet

Node.js Quizlet API interface
CoffeeScript
3
star
31

quicksort-js

JavaScript
3
star
32

voxelscript

[DEFUNCT -- Spout is dead] Javascript modding platform for Spout.
Java
3
star
33

s3-maven-details

Find out the structure of the dependencies in an S3 Maven repo.
Haskell
3
star
34

spillway

Go
3
star
35

node-evalin

Eval.in API reverse engineered for Node.js
JavaScript
3
star
36

bukkit-bootstrap

Bootstrap for a new Bukkit project
Groovy
3
star
37

yo-reddit

Sends a Yo whenever a new post is made in your subreddits!
JavaScript
3
star
38

react-swipe-cards

JavaScript
3
star
39

Personas

Personas is a Bukkit plugin that brings NPCs to Bukkit.
Java
3
star
40

SavageGames

Mirah
3
star
41

mirah-hello

Testing of the Mirah programming language for Bukkit.
Mirah
3
star
42

hexo-theme-simplyian

The theme to simplyian.com, based off of yuche's hexo-theme-kael.
CSS
3
star
43

PersonasAPI

The API for the Personas framework. Personas is a Bukkit plugin that attempts to make NPCs more accessible to plugin developers.
Java
3
star
44

mercedes-hackathon

Java
3
star
45

ideas

Collection of ideas to work on when I'm bored
2
star
46

Flaggables

Java
2
star
47

albkit

Just another Bukkit utility library.
Java
2
star
48

up

Extremely simple file uploader
Shell
2
star
49

cyantranslate

JavaScript
2
star
50

codeview

JavaScript
2
star
51

stanford-corenlp

Github mirror of Stanford CoreNLP by Stanford NLP team.
Java
2
star
52

ExoSuit

...this code was found via archaeology too.
Java
2
star
53

json-to-scala

Create Scala case classes from a JSON sample.
HTML
2
star
54

remembrall

Clojure
2
star
55

simplyian.com

My personal blog.
HTML
2
star
56

osx-prank

A harmless prank that can be put on a Mac. Make sure to run it in screen.
Shell
2
star
57

ModTheModAPI

The ModTheMod API.
Java
2
star
58

2graph

Visualize your 2d equations!
2
star
59

normaleyes

JavaScript
2
star
60

stats-cheat-sheet

A collection of useful equations for statistics. Used for UTD CS 3341.
Shell
2
star
61

mrchung

JavaScript
2
star
62

SuperPlayer

General Minecraft commands to use in your server.
CoffeeScript
2
star
63

keypool

Key pooling library to circumvent rate limits.
Go
2
star
64

todo-preston

Todo app using a Preston-powered backend.
JavaScript
2
star
65

mongoose-password-bcrypt-nodejs

Mongoose plugin providing a password field using bcrypt-nodejs
JavaScript
2
star
66

icarus

An Android application that allows you to carry artificial intelligence on your Android device.
2
star
67

kanjidic-index

https://kanjidic-index.ianm.com/
TypeScript
2
star
68

snakey

Snake with ncurses and bloopsaphone.
C
2
star
69

VRTransit

Public transit visualization application for Samsung Gear VR.
Java
2
star
70

speedway-bricks

JavaScript
2
star
71

CrimsonIRC

The IRC plugin for CrimsonRPG.
Java
2
star
72

httputil

Go
2
star
73

cookie-ai

Your personal assistant for cooking.
JavaScript
2
star
74

mips-hangman

Simple Hangman game implemented in the MIPS architecture for the MARS emulator.
Assembly
2
star
75

SpawnCountdown

A spawn countdown timer.
Java
2
star
76

co-optionals

The Option type, compatible with tj/co.
JavaScript
2
star
77

cpp-cs-concepts

Learning data structures, algorithms, and concepts written in C++.
C++
2
star
78

atom-dotfiles

My Atom configuration.
CoffeeScript
2
star
79

hackdfw-backend-old

The backend to HackDFW.
Scala
1
star
80

deque-js

Deque implementation in JavaScript
JavaScript
1
star
81

modapi-core

The Mod SDK.
CoffeeScript
1
star
82

PlasmaMachineGun

...I don't want to lose my code again.
Java
1
star
83

list-schools

Python
1
star
84

1plus1

JavaScript
1
star
85

hottrade

Find the hottest stocks to trade!
Java
1
star
86

mtm-js-loader

Javascript loader for ModTheMod.
Java
1
star
87

qirc

Quick, robust IRC client.
JavaScript
1
star
88

safewalk-api

SafeWalk API
JavaScript
1
star
89

tokengen

JavaScript
1
star
90

hubmaster

Hubmaster is a tool for interating with Github API in terminal.
Ruby
1
star
91

CommandTimer

A plugin that makes command warmups, cooldowns, and restrictions easy
Java
1
star
92

heavymetalnames

HTML
1
star
93

liblol

A library containing LoL data.
CoffeeScript
1
star
94

weaved-for-ifttt

Python
1
star
95

todomvc-preston-angular

TodoMVC for Preston/Angular
JavaScript
1
star
96

courselookup

UTD course suggestion tool.
JavaScript
1
star
97

PermClasses

Permission-based class system.
Java
1
star
98

mozart

Runs docker compose
JavaScript
1
star
99

Revenge-for-Bukkit

Java
1
star
100

visualsort

Visual representation of various sorting algorithms using HTML5.
1
star