Skip to content

Commit

Permalink
async/await (kylealwyn#1)
Browse files Browse the repository at this point in the history
* use dotenv to load environment variables from .env file

* add async await calls

* add errorhandler middleware

* async/await all the things!

* add travis.yml

* use latest node v6 version; specify node and npm engines in package.json

* specify g++ compiler in travis.yml to solve bcrypt error

* fix travis.yml format due to linter barfing

* dont import error handler in server

* add mongodb service to travis
  • Loading branch information
Kyle Alwyn authored Jan 26, 2017
1 parent 605f74d commit 5fde37a
Show file tree
Hide file tree
Showing 17 changed files with 212 additions and 144 deletions.
8 changes: 7 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
{
"presets": ["es2015", "stage-0"]
"presets": ["es2015", "stage-0"],
"plugins": [
["transform-runtime", {
"polyfill": false,
"regenerator": true
}]
]
}
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
tests
node_modules
build
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": ["eslint:recommended", "google"],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 6,
"ecmaVersion": '2017',
"sourceType": "module"
},
"plugins": [
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ npm-debug.log
build
coverage
node_modules
.env
16 changes: 16 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
language: node_js
sudo: true
node_js:
- "6"
before_install:
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
- sudo apt-get update -q
- sudo apt-get install gcc-4.8 g++-4.8 -y
env:
- TRAVIS=travis CXX=g++-4.8
services:
- mongodb
install:
- npm install
script:
- npm test
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Express & ES6 API Boilerplate

[![Build Status](https://travis-ci.org/kylealwyn/node-rest-api-boilerplate.svg?branch=master)](https://travis-ci.org/kylealwyn/node-rest-api-boilerplate)
> Tested on Node v6 and above
## Features
- [x] ES6 for javascript awesomeness
Expand Down
2 changes: 1 addition & 1 deletion app/config/constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import path from 'path';
import { merge } from 'lodash';
import merge from 'lodash/merge';

// Default configuations applied to all environments
const defaultConfig = {
Expand Down
30 changes: 15 additions & 15 deletions app/controllers/auth.controller.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import UserModel from '../models/user';
import BaseController from './base.controller';
import User from '../models/user';

class AuthController extends BaseController {
login = (req, res) => {
login = async (req, res, next) => {
const { username, password } = req.body;

UserModel.findOne({ username })
.then((user) => {
if (!user || !user.authenticate(password)) {
return res.status(401).json({
message: 'Please verify your credentials.',
});
}
try {
const user = await User.findOne({ username });

const token = user.generateToken();
return res.status(200).json({ token });
})
.catch((err) => {
res.status(500).json(this.formatApiError(err));
});
if (!user || !user.authenticate(password)) {
const err = new Error('Please verify your credentials.');
err.status = 401;
return next(err);
}

const token = user.generateToken();
return res.status(200).json({ token });
} catch (err) {
next(err);
}
}
}

Expand Down
92 changes: 48 additions & 44 deletions app/controllers/posts.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,41 @@ import Post from '../models/post';

class PostController extends BaseController {

whitelist = ['text']
whitelist = [
'text',
];

// Middleware to populate post based on url param
_populate = (req, res, next) => {
Post.findById(req.params.postId)
.then((post) => {
if (!post) {
return res.status(404).json({ message: 'Post not found.' });
}

req.post = post;
next();
})
.catch((err) => {
// CastError means we could not cast the param id to an objectId
const status = err.name === 'CastError' ? 404 : 500;
res.sendStatus(status);
});
_populate = async (req, res, next) => {
const { id } = req.params;

try {
const post = await Post.findById(id);

if (!post) {
const err = new Error('Post not found.');
err.status = 404;
return next(err);
}

req.post = post;
next();
} catch(err) {
err.status = err.name ==='CastError' ? 404 : 500;
next(err);
}
}

search = (req, res) => {
Post
.find({})
.populate({ path: '_user', select: '-posts -role' })
.then((posts) => {
res.status(200).json(posts);
})
.catch((err) => {
res.status(500).json(this.formatApiError(err));
});
search = async (req, res, next) => {
try {
const posts =
await Post.find({})
.populate({ path: '_user', select: '-posts -role' });

res.json(posts);
} catch(err) {
next(err);
}
}

/**
Expand All @@ -47,35 +52,34 @@ class PostController extends BaseController {
* req.user is populated by middleware in routes.js
*/

create = (req, res) => {
create = async (req, res, next) => {
const params = this.filterParams(req.body, this.whitelist);

const post = new Post(params);
post._user = req.currentUser._id;
const post = new Post({
...params,
_user: req.currentUser._id,
});

post.save()
.then((p) => {
res.status(201).json(p);
})
.catch((err) => {
res.status(400).json(this.formatApiError(err));
});
try {
res.status(201).json(await post.save());
} catch(err) {
next(err);
}
}

delete = (req, res) => {
delete = async (req, res, next) => {
/**
* Ensure the user attempting to delete the post owns the post
*
* ~~ toString() converts objectIds to normal strings
*/
if (req.post._user.toString() === req.currentUser._id.toString()) {
req.post.remove()
.then(() => {
res.sendStatus(204);
})
.catch(() => {
res.sendStatus(500);
});
try {
await req.post.remove();
res.sendStatus(204);
} catch(err) {
next(err);
}
} else {
res.sendStatus(403);
}
Expand Down
117 changes: 60 additions & 57 deletions app/controllers/users.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,44 @@ import User from '../models/user';

class UsersController extends BaseController {

whitelist = ['firstname', 'lastname', 'email', 'username', 'password']

_populate = (req, res, next) => {
whitelist = [
'firstname',
'lastname',
'email',
'username',
'password',
];

_populate = async (req, res, next) => {
const { username } = req.params;

User.findOne({ username })
.then((user) => {
if (!user) {
return res.status(404).json({ message: 'User not found.' });
}

req.user = user;
next();
})
.catch((err) => {
res.status(400).json(this.formatApiError(err));
});
try {
const user = await User.findOne({ username });

if (!user) {
const err = new Error('User not found.');
err.status = 404;
return next(err);
}

req.user = user;
next();
} catch(err) {
next(err);
}
}

search = (req, res) => {
User.find({})
.then((users) => {
res.json(users);
})
.catch((err) => {
res.status(400).json(this.formatApiError(err));
});
search = async (req, res, next) => {
try {
// @TODO Add pagination
res.json(await User.find());
} catch(err) {
next(err);
}
}

fetch = (req, res) => {
let user = req.user || req.currentUser;
const user = req.user || req.currentUser;

if (!user) {
return res.sendStatus(404);
Expand All @@ -42,50 +49,46 @@ class UsersController extends BaseController {
res.json(user);
}

create = (req, res) => {
create = async (req, res, next) => {
const params = this.filterParams(req.body, this.whitelist);

const newUser = new User(params);
newUser.provider = 'local';
newUser.save()
.then((savedUser) => {
const token = savedUser.generateToken();
res.status(201).json({ token });
})
.catch((err) => {
res.status(400).json(this.formatApiError(err));
});
}

update = (req, res) => {
if (!req.currentUser) {
return res.sendStatus(403);
let newUser = new User({
...params,
provider: 'local',
});

try {
const savedUser = await newUser.save();
const token = savedUser.generateToken();
res.status(201).json({ token });
} catch(err) {
err.status = 400;
next(err);
}
}

const params = this.filterParams(req.body, this.whitelist);
update = async (req, res, next) => {
const newAttributes = this.filterParams(req.body, this.whitelist);
const updatedUser = Object.assign({}, req.currentUser, newAttributes);

const updated = Object.assign({}, req.currentUser, params);
updated.save()
.then(() => {
res.sendStatus(204);
})
.catch((err) => {
res.status(400).json(this.formatApiError(err));
});
try {
res.status(200).json(await updatedUser.save());
} catch (err) {
next(err);
}
}

delete = (req, res) => {
delete = async (req, res, next) => {
if (!req.currentUser) {
return res.sendStatus(403);
}

req.currentUser.remove()
.then(() => {
res.sendStatus(204);
})
.catch((err) => {
res.status(400).json(this.formatApiError(err));
});
try {
await req.currentUser.remove();
res.sendStatus(204);
} catch(err) {
next(err);
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Load environment variables
require('dotenv').config();

// Initialize Database
require('./database');

// Initialize Server
require('./server');
Loading

0 comments on commit 5fde37a

Please sign in to comment.