Skip to content

Instantly share code, notes, and snippets.

@greggnakamura
Last active August 29, 2015 14:18
Show Gist options
  • Save greggnakamura/c236fc64b6b66a3de943 to your computer and use it in GitHub Desktop.
Save greggnakamura/c236fc64b6b66a3de943 to your computer and use it in GitHub Desktop.
Javascript: REST Level 3: Hypermedia-Driven Relationships (Nettuts: How to Build a Hypermedia-Driven REST API)
var express = require('express');
var app = express();
var Datastore = require('nedb');
var db = {};
var wrapper = require('./lib/wrapper.js');
var port = process.argv[2] || 3050;
var root = "http://localhost:" + port;
// Connect to an NeDB database
db.movies = new Datastore({ filename: 'db/movies', autoload: true });
// Add an index
db.movies.ensureIndex({ fieldName: 'title', unique: true });
// Necessary for accessing POST data via req.body object
app.use(express.bodyParser());
// Catch-all route to set global values
app.use(function (req, res, next) {
res.type('application/json');
res.locals.wrap = wrapper.create({
start: new Date()
});
next();
});
// Routes
app.get('/', function (req, res) {
res.send('The API is working.');
});
app.get('/movies', function (req, res) {
db.movies.find({}, function (err, results) {
if (err) {
res.json(500, { error: err });
return;
}
// option 1 of returning a collection result
// res.json(200, res.locals.wrap(results.map(function (movie) {
// movie.links = { self: root + '/movies/' + movie._id };
// return movie;
// }), {
// // fake pagination 'link' url
// next: root + '/movies?page=2'
// }));
// option 2 of returning a collection result
// alternate version passing back results collection, of above
// each item is a link with a relation
res.json(200, res.locals.wrap({}, { items: results.map(function (movie) {
return root + '/movies/' + movie._id;
})}));
});
});
app.post('/movies', function (req, res) {
if (!req.body.title) {
res.json(400, { error: { message: "A title is required to create a new movie." }});
return;
}
db.movies.insert({ title: req.body.title }, function (err, created) {
if (err) {
res.json(500, { error: err });
return;
}
res.set('Location', root + '/movies/' + created._id);
res.json(201, created);
});
});
app.get('/movies/:id', function (req, res) {
db.movies.findOne({ _id: req.params.id }, function (err, result) {
if (err) {
res.json(500, { error: err });
return;
}
if (!result) {
res.json(404, { error: { message: "We did not find a movie with id: " + req.params.id }});
return;
}
res.json(200, res.locals.wrap(result, { self: root + '/movies/' + req.params.id }));
});
});
app.put('/movies/:id', function (req, res) {
db.movies.update({ _id: req.params.id }, req.body, { upsert: false }, function (err, num, upsert) {
if (err) {
res.json(500, { error: err });
return;
}
if (num === 0) {
res.json(400, { error: { message: "No records were updated." }});
return;
}
res.send(204);
res.json(200, { success: { message: "Sucessfully updated movie with ID " + req.params.id }});
});
});
app.delete('/movies/:id', function (req, res) {
db.movies.remove({ _id: req.params.id }, function (err, num) {
if (err) {
res.json(500, { error: err });
return;
}
if (num === 0) {
res.json(404, { error: { message: "We did not find a movie with id: " + req.params.id }});
return;
}
res.set('Link', root + '/movies; rel="collection"');
res.send(204);
});
});
app.listen(port);
// helper
// for meta objects (i.e. - links, items)
var _ = require('underscore');
module.exports = {
// create wrapper function
create: function (data) {
return function (result, links) {
var envelope = {};
// check on type of result
// returns [object Object] or [object Array]
var resultType = Object.prototype.toString.call(result);
envelope.links = links || {};
envelope.request_info = {
seconds: (new Date() - data.start) / 1000,
cached: false
};
if (resultType === '[object Object]') {
envelope = _.extend({}, envelope, result);
}
else if (resultType === '[object Array]') {
envelope.request_info.result_count = result.length;
envelope.items = result;
}
return envelope;
};
}
};
{
"links": {
"next": "http://localhost:3050/movies?page=2"
},
"request_info": {
"seconds": 0.002,
"cached": false,
"result_count": 4
},
"items": [
{
"title": "Die Hard",
"_id": "91rGJj7o1EbHnhsv",
"rating": "5",
"links": {
"self": "http://localhost:3050/movies/91rGJj7o1EbHnhsv"
}
},
{
"title": "Star Wars: Episode IV - A New Hope",
"rating": "4.5",
"_id": "hjXeCBOjYfktF1gl",
"links": {
"self": "http://localhost:3050/movies/hjXeCBOjYfktF1gl"
}
},
{
"title": "The Life Aquatic with Steve Zissou",
"_id": "nemADCddUHCxWjNY",
"links": {
"self": "http://localhost:3050/movies/nemADCddUHCxWjNY"
}
},
{
"title": "Indiana Jones and the Temple of Doom",
"_id": "uMcqVcvvyU4bQvY2",
"links": {
"self": "http://localhost:3050/movies/uMcqVcvvyU4bQvY2"
}
}
]
}
{
"links": {
"items": [
"http://localhost:3050/movies/91rGJj7o1EbHnhsv",
"http://localhost:3050/movies/hjXeCBOjYfktF1gl",
"http://localhost:3050/movies/nemADCddUHCxWjNY",
"http://localhost:3050/movies/uMcqVcvvyU4bQvY2"
]
},
"request_info": {
"seconds": 0.001,
"cached": false
}
}
@greggnakamura
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment