45

I'm a beginner in Node (and generally all back-end web development), and I have started to write a RESTful API in Node. There are a few things I'm trying to get my head around.

My application uses Express and Mongoose, and I am using the express-resource module to easily create my CRUD routes for the API resources. But there are a couple of things I am unhappy about, and think I could do better.

The first is Mongoose. If I want to write tests for my API, I have no way of stubbing Mongoose to force it to in memory data. All of the tutorials out there seem to point to Mongoose, however, and I'm really not sure what I should be using.

Secondly, my resource seems to have a lot of boilerplate code. Is this really the best way to create a RESTful API in Node.js? Are there other modules that will help me to create my CRUD routes? I believe there are ways you can create CRUD routes right from your schema, without anymore code, but I'm really not sure how.

I have seen projects out there such as Tower.js and CompoundJS (formally RailwayJS) that seem to be these comprehensive solutions that solve much more than my issues here. Perhaps I should be using them, but I really only want the Node.js application to be an API and nothing more. I am dealing with the front-end independently of the API.

To provide some context, here is my current situation. Currently, I have a model defined in Mongoose:

var mongoose = require('mongoose')
  , Schema = mongoose.Schema
  , Link

var LinkSchema = new Schema({
  uri: String,
  meta: {
    title: String,
    desc: String
  },
  shares: [{
    uid: Schema.Types.ObjectId,
    date: Date,
    message: String
  }]
})

Link = module.exports = mongoose.model('Link')

Next, I define the controllers for the CRUD routes:

var mongoose = require('mongoose')
  , _ = require('underscore')
  , Link = mongoose.model('Link')

exports.load = function (req, id, fn) {
  Link.findById(req.params.link, function (err, link) {
    if (err) {
      return res.send(err)
    }

    fn(null, link)
  })
}

exports.index = function (req, res) {
  var filterByUser = req.query.user ? { 'shares.uid': req.query.user } : {}

  Link.find(filterByUser, function (err, links) {
    if (err) {
      return res.send(err)
    }

    res.send(links)
  })
}

exports.create = function (req, res) {
  var link = new Link(req.body)

  link.save(function (err) {
    if (err) {
      // TODO: send 404
      return res.send(err)
    }

    res.send(link)
  })
}

exports.show = function (req, res) {
  res.send(req.link)
}

exports.update = function (req, res) {
  req.link = _(req.link).extend(req.body)

  req.link.save(function (err, link) {
    if (err) {
      return res.send(err)
    }

    res.send(link)
  })
}

exports.patch = exports.update

exports.destroy = function (req, res) {
  req.link.remove(function (err) {
    if (err) {
      return res.send(err)
    }

    res.send()
  })
}

Finally, I use the express-resource module to map these controllers to the necessary CRUD routes on top of the Express app.

app.resource('api/links', require('../resources/links'))
  • You may be intrested to easy the process using this tool https://codecanyon.net/item/nodejs-automatic-api-generator-from-mysql-database-ultimate-edition/25033454 – Nishant Kumar Verma Dec 02 '19 at 16:20

14 Answers14

29

You should look into restify

If you want to use express, you can also check out this project that I made -- called node-restful.

This library seems to be much more mature and have more features though: https://github.com/jspears/mers

Benoir
  • 1,244
  • 10
  • 10
  • Thanks for sharing node-restful and mers. Both look good. Why do you say I should use restify? It looks like it does just as much as Express does, if not less… I'm not sure how it helps. It would be good if you could explain! –  Feb 21 '13 at 10:06
  • I've heard good things about restify, but after looking at it it seems like it doesn't actually generate the CRUD operations for you. Seems like mers would probably be the best option. – Benoir Feb 21 '13 at 20:43
  • Thanks. I'm going to leave this question open for now to see if I get any other suggestions. –  Feb 21 '13 at 20:49
  • A similar library to mers: [mongo-rest](https://github.com/enyo/mongo-rest) –  Feb 21 '13 at 21:35
  • After reading the intro docs, it looks like neither mongo-rest nor MERS allows me to easily turn on/off certain routes (e.g. GET, POST, no PUT, DELETE) for certain models, nor do they allow me to easily permission models (in the simplest case, ensuring that e.g. only authenticated users can PUT/POST/DELETE). Am I missing anything? Thanks! – Lane Rettig Jan 06 '16 at 00:30
  • this answer needs some serious updating... – PirateApp Jun 08 '20 at 12:42
5

Strongloop Loopback seeems to be another good alternative for generating Node/MongoDB APIs. It can also generate mocha tests too.

Sean McClory
  • 4,195
  • 3
  • 32
  • 35
  • +! Loopback has a web interface on the localhost to help design your models and inspect the generated routes. It is database agnostic, so you can use MongoDB or MySQL behind it. And it allows you to specify [ACL rules](https://docs.strongloop.com/display/public/LB/Controlling+data+access) to ensure users can only access the data they have permission to. – joeytwiddle Jan 29 '16 at 10:47
4

Take a look at Hapi its a configuration-centric framework for building web applications and APIs its used as restful service.

Other options are sails.js and actionhero

Travis Webb
  • 14,688
  • 7
  • 55
  • 109
ramon22
  • 3,528
  • 2
  • 35
  • 48
  • 1
    Right. On. This opened a whole new world for me, a Front-End dude. Thanks Travis! – Cody Jun 17 '15 at 03:46
4

Strapi is a new (2015) framework.

The admin interface on their website allows you to create an API and specify relationships between models. (As can be seen in their introduction video.)

However it is designed to run on the Koa server, not on Express.

joeytwiddle
  • 29,306
  • 13
  • 121
  • 110
  • I agree. Strapi is fantastic. Generating a fast and simple MVC API can be done in seconds and adding your own custom business logic is simple. Uses crud out of the box but a plugin can be used to enable a graphql endpoint. – Ben Lewis Watson May 15 '18 at 16:43
2

I recommend Baucis + Express. Thousands of users, model-driven design based on Mongoose, very flexible, spec-compliant, HATEOAS/Level 3 ready. Fits all my needs perfectly. But then, I'm the author :) https://github.com/wprl/baucis

wprl
  • 24,489
  • 11
  • 55
  • 70
2

Check this out: enter image description here

With Feathers you can build prototypes in minutes and production ready real-time backends and REST APIs in days. Seriously.

yokodev
  • 1,266
  • 2
  • 14
  • 28
2

Here is the issues about the frameworks nowadays.

When you come here from google searching "best", "fastest" framework blah, blah, people will drop a line says "Hey, you should try this sails.js, feathers, Derby etc..."

Ok The question is: - Are you just for fun with those frameworks - if yes, you can easily get a list of frameworks and then start to benchmark them whatsoever.

  • I am assuming most of people are "I want to build a product, build a site can make money in the future, or at least it will become popular";

Ok, all you search keywords and attentions here is wrong, try to search "production ready", "enterprise ready", "case study" those keywords then, or maybe go to indeed.com and search node.js, further dig out what node.js framework most companies using, the answer maybe just simply say "express".

if so, From node.js stack, The frameworks will pretty much be narrowed down a few of them: Hapi, Strongloop, or even not popular one Mojito from Yahoo

For Those frameworks at least you can tell - "They are really production or enterprise ready" - cos' they have been using form Walmart, from Yahoo, from other big giants from some time, some even for couple of years.

Can this explain why Ruby on rails and Django still dominate the full stack framework markets? even you see lots of "cool" frameworks keeping on coming to the market, Meteor, Sails.js, Go's Revel, Java's Play Spark whatever you can name - It doesn't mean these frameworks worse than the two, just mean they need time to win the market.

Another problems lots of the current frameworks are a kind of all-in-one, clone of "Ror"; From the end user's prospect, They just need a thing to help them get things done, need productive, need something to work from the begin to the end, like Ruby on Rails, like MacOS, like windows, like anything itself which has been tested by the time, has been daily used by people.

elawcn
  • 51
  • 2
0

I've been using Express to build my RESTful APIs on Node.js and with the addition of Router in Express 4, it's even easier to structure it. It's detailed here http://www.codekitchen.ca/guide-to-structuring-and-building-a-restful-api-using-express-4/

Tolga E
  • 12,188
  • 15
  • 49
  • 61
0

Try https://hivepod.io/ and generate your example in a full MEAN stack. Hivepod builds on top of BaucisJS + ExpressJS + MongoDB + AngularJS.

Disclaimer: I work building Hivepod.

pjmolina
  • 302
  • 4
  • 15
0

Have a look into this link

This project is build using the same project archutecture which is followed by ASP.Net WebApi 2.0. That means it will be having controllers, authentication mechanism etc. to start with. All you need to do is create your own controllers.

Rahul Pal
  • 96
  • 7
0

I'm surprised no-one mentioned Nodal.

From the website:

Nodal is a web server for Node.js, optimized for building API services quickly and efficiently.

Boasting its own opinionated, explicit, idiomatic and highly-extensible framework, Nodal takes care of all of the hard decisions for you and your team. This allows you to focus on creating an effective product in a short timespan while minimizing technical debt

It's actively maintained, has 3800+ stars on Github (at the time of writing), has a command-line tool built-in to generate boilerplate code, and overall gets the job done quickly.

Community
  • 1
  • 1
jeanpaul62
  • 9,451
  • 13
  • 54
  • 94
0

This is a sample to perform CRUD operations in a library system

var schema=require('../dbSchema');
var bookmodel=schema.model('book');

exports.getBooks = function (req,res) {
    bookmodel.find().exec().then((data)=>{
        res.send(data)
    }).catch((err)=>{
        console.log(err);
    });
};

exports.getBook = function (req,res) {
var bkName=req.params.Author;
bookmodel.find({Name:bkName}).exec().then((data)=>{
    res.send(data)
}).catch((err)=>{
    console.log(err);
});
};

exports.getAutBooks = function (req,res) {
    bookmodel.find({},'Author').then((data)=>{
        res.send(data);
    }).catch((err)=>{
    console.log(err);
    });
};

exports.deleteBooks=function(req,res){
    var bkName=req.params.name;
    bookmodel.remove({Name:bkName}).exec().then((data)=>{
        res.status(200);
        console.log(bkName);
    }).catch((err)=>{
        console.log(err);
    });
    };

exports.addBooks=function(req,res){
    var newBook=new bookmodel({
        Name:req.body.Name,
       ISBN:req.body.ISBN,
       Author:req.body.Author,
       Price:req.body.Price,
       Year:req.body.Year,
       Publisher:req.body.Publisher
    });
    newBook.save().then(()=>{
        res.status(201);
    }).catch((err)=>{
        console.log(err);
    });
};
Ashane.E
  • 109
  • 10
0

I am a big fan of express and I've been using it to build RESTful APIs on Node.js which are easier to build. However, when our application started growing, we ended in a situation where express structure did not scale well and with more code splitting around, it was harder for us to maintain.

I am from C#/Java background where SOLID principles are heavily used. Frameworks like Java Spring / C# WebAPI are proven to create enterprise level applications. I wanted to have a framework where I can reuse my existing C#/Java skills (reuse I mean MVC architecture, OOPS, SOLID, DI, Mocks ... yeah, strong typing). Unfortunately, I did not find a framework which meets my requirements (It should have less learning curve, minimalist codebase size, completely express compatible).

What do I mean by completely express compatible? Whatever express does, I must be able to do it even If I use a framework on top of it, when I have looked into Strongloop Loopback it was pretty good to use but It had a lot of documentation to go through and the frameworks are coupled, not really what I was looking for.

So I have created Dinoloop powered by Typescript (has interfaces, classes, abstract classes and strong oops). The package is pretty stable now.

With Dinoloop you can build enterprise level applications in a scalable architecture. It uses Dependency Injection framework but you can configure any DI frameworks available in typescript. Dinoloop enables typescript to use as a Nodejs REST framework that helped me to maintain common typescript codebase for both angular and node projects.

So, Dinoloop is a perfect fit for typescript lovers and angular developers.

user3205479
  • 1,443
  • 1
  • 17
  • 41
-1
    var mongoose        = require('../DBSchema/SchemaMapper');
    var UserSchema      = mongoose.model('User');

    var UserController = function(){
        this.insert = (data) => {
            return new Promise((resolve, reject) => {
                var user = new UserSchema({
                    userName: data.userName,
                    password: data.password
                });
                user.save().then(() => {
                    resolve({status: 200, message: "Added new user"});
                }).catch(err => {
                    reject({status: 500, message: "Error:- "+err});
                })
            })

        }

        this.update = (id, data) => {
            return new Promise((resolve, reject) => {
                UserSchema.update({_id: id}, data).then(() => {
                    resolve({status: 200, message: "update user"});
                }).catch(err => {
                    reject({status: 500, message: "Error:- " + err});
                })
            })
        }

        this.searchAll = () => {
            return new Promise((resolve, reject) => {
                UserSchema.find().exec().then((data) => {
                    resolve({status: 200, data: data});
                }).catch(err => {
                    reject({status: 500, message: "Error:- " + err});
                })
            })
        }

        this.search = (id) => {
            return new Promise((resolve, reject) => {
                UserSchema.find({_id:id}).exec().then(user => {
                    resolve({status: 200, data: user});
                }).catch(err => {
                    reject({status: 500, message: "Error:- " + err});
                })
            })
        }

        this.delete = (id) => {
            return new Promise((resolve, reject) => {
                UserSchema.remove({_id:id}).then(() => {
                    resolve({status: 200, message: "remove user"});
                }).catch(err => {
                    reject({status: 500, message:"Error:- " + err});
                })
            })
        }
    }

    module.exports = new UserController();

    ///Route
    var express     = require('express');
    var router      = express.Router();
    var Controller  = require('./User.Controller');



    router.post('/', (req, res) => {
        Controller.insert(req.body).then(data => {
            res.status(data.status).send({message: data.message});
        }).catch(err => {
            res.status(err.status).send({message: err.message});
        })
    });

    router.put('/:id', (req, res) => {
        Controller.update(req.params.id, req.body).then(data => {
            res.status(data.status).send({message: data.message});
        }).catch(err => {
            res.status(err.status).send({message: err.message});
        })
    });

    router.get('/', (req, res) => {
        Controller.searchAll().then(data => {
            res.status(data.status).send({data: data.data});
        }).catch(err => {
            res.status(err.status).send({message: err.message});
        });
    });

    router.get('/:id', (req, res) => {
        Controller.search(req.params.id).then(data => {
            res.status(data.status).send({data: data.data});
        }).catch(err => {
            res.status(err.status).send({message: err.message});
        });
    });

    router.delete('/:id', (req, res) => {
        Controller.delete(req.params.id).then(data => {
            res.status(data.status).send({message: data.message});
        }).catch(err => {
            res.status(err.status).send({message: err.message});
        })
    })

    module.exports = router;

//db`enter code here`schema

var mongoose = require('mongoose');
const Schema = mongoose.Schema;

var Supplier =new Schema({

    itemId:{
        type:String,
        required:true
    },
    brand:{
        type:String,
        required:true
    },
    pno:{
        type:String,
        required:true
    },   
    email:{
        type:String,
        required:true
    }    

});

mongoose.model('Inventory',Inventory);
mongoose.model('Supplier',Supplier);

mongoose.connect('mongodb://127.0.0.1:27017/LAB', function (err) {
    if (err) {
        console.log(err);
        process.exit(-1);
    }
    console.log("Connected to the db");
});
module.exports = mongoose;