- expressjs - The server for handling and routing HTTP requests
- express-jwt - Middleware for validating JWTs for authentication
- jsonwebtoken - For generating JWTs used by authentication
- mongoose - For modeling and mapping MongoDB data to javascript
- mongoose-unique-validator - For handling unique validation errors in Mongoose. Mongoose only handles validation at the document level, so a unique index across a collection will throw an excpetion at the driver level. The
mongoose-unique-validator
plugin helps us by formatting the error like a normal mongooseValidationError
. - passport - For handling user authentication
- slug - For encoding titles into a URL-friendly format
app.js
- The entry point to our application. This file defines our express server and connects it to MongoDB using mongoose. It also requires the routes and models we'll be using in the application.config/
- This folder contains configuration for passport as well as a central location for configuration/environment variables.routes/
- This folder contains the route definitions for our API. They containmodels/
- This folder contains the schema definitions for our Mongoose models.
Please follow this link https://console.ng.bluemix.net/docs/starters/install_cli.html for deployment using Cloud FOundry CLI. IBM bluemix provides nodejs runtime, follow this link for more information https://console.ng.bluemix.net/docs/runtimes/nodejs/index.html#nodejs_runtime
Bluemix Dashboard after service deployment
In routes/api/index.js
, we define a error-handling middleware for handling Mongoose's ValidationError
. This middleware will respond with a 422 status code and format the response to have [error messages the clients can understand]
Requests are authenticated using the Authorization
header with a valid JWT. We define two express middlewares in routes/auth.js
that can be used to authenticate requests. The required
middleware configures the express-jwt
middleware using our application's secret and will return a 401 status code if the request cannot be authenticated. The payload of the JWT can then be accessed from req.payload
in the endpoint. The optional
middleware configures the express-jwt
in the same way as required
, but will not return a 401 status code if the request cannot be authenticated.
Authentication Header:
Authorization: Token jwt.token.here
JSON Objects returned by API:
Users (for authentication)
{ "user": { "email": "jake@jake.jake", "token": "jwt.token.here", "username": "jake", "bio": "I work at statefarm", "image": null } } Profile
{ "profile": { "username": "jake", "bio": "I work at statefarm", "image": "https://static.productionready.io/images/smiley-cyrus.jpg", "following": false } } Single Article
{ "article": { "slug": "how-to-train-your-dragon", "title": "How to train your dragon", "description": "Ever wonder how?", "body": "It takes a Jacobian", "createdAt": "2016-02-18T03:22:56.637Z", "updatedAt": "2016-02-18T03:48:35.824Z", "favorited": false, "favoritesCount": 0, "author": { "username": "jake", "bio": "I work at statefarm", "image": "https://i.stack.imgur.com/xHWG8.jpg", "following": false } } } Multiple Articles
{ "articles":[{ "description": "Ever wonder how?", "slug": "how-to-train-your-dragon", "title": "How to train your dragon", "tagList": ["dragons", "training"], "createdAt": "2016-02-18T03:22:56.637Z", "updatedAt": "2016-02-18T03:48:35.824Z", "favorited": false, "favoritesCount": 0, "author": { "username": "jake", "bio": "I work at statefarm", "image": "https://i.stack.imgur.com/xHWG8.jpg", "following": false } }, { "description": "So toothless", "slug": "how-to-train-your-dragon-2", "title": "How to train your dragon 2", "tagList": ["dragons", "training"], "createdAt": "2016-02-18T03:22:56.637Z", "updatedAt": "2016-02-18T03:48:35.824Z", "favorited": false, "favoritesCount": 0, "author": { "username": "jake", "bio": "I work at statefarm", "image": "https://i.stack.imgur.com/xHWG8.jpg", "following": false } }], "articlesCount": 2 } Single comment
{ "comment": { "id": 1, "body": "It takes a Jacobian", "createdAt": "2016-02-18T03:22:56.637Z", "author": { "username": "jake", "bio": "I work at statefarm", "image": "https://i.stack.imgur.com/xHWG8.jpg", "following": false } } } Multiple comments
{ "comments": [{ "id": 1, "body": "It takes a Jacobian", "createdAt": "2016-02-18T03:22:56.637Z", "author": { "username": "jake", "bio": "I work at statefarm", "image": "https://i.stack.imgur.com/xHWG8.jpg", "following": false } }] } List of Tags
{ "tags": [ "reactjs", "angularjs" ] } Errors and Status Codes
If a request fails any validations, expect a 422 and errors in the following format:
{ "errors":{ "body": [ "can't be empty" ] } } Other status codes:
401 for Unauthorized requests, when a request requires authentication but it isn't provided
403 for Forbidden requests, when a request may be valid but the user doesn't have permissions to perform the action
404 for Not found requests, when a resource can't be found to fulfill the request
Endpoints:
Authentication:
POST /api/users/login
Example request body:
{ "user":{ "email": "jake@jake.jake", "password": "jakejake" } } No authentication required, returns a User
Required fields: email, password
Registration:
POST /api/users
Example request body:
{ "user":{ "username": "Jacob" "email": "jake@jake.jake", "password": "jakejake" } } No authentication required, returns a User
Required fields: email, username, password
Get Current User
GET /api/user
Authentication required, returns a User that's the current user
Update User
PUT /api/user
Example request body:
{ "user":{ "email": "jake@jake.jake", "bio": "I like to skateboard", "image": "https://i.stack.imgur.com/xHWG8.jpg" } } Authentication required, returns the User
Accepted fields: email, username, password, image, bio
Get Profile
GET /api/profiles/:username
Authentication optional, returns a Profile
Follow user
POST /api/profiles/:username/follow
Authentication required, returns a Profile
No additional parameters required
Unfollow user
DELETE /api/profiles/:username/follow
Authentication required, returns a Profile
No additional parameters required
List Articles
GET /api/articles
Returns most recent articles globally be default, provide tag, author or favorited query parameter to filter results
Query Parameters:
Filter by tag:
?tag=AngularJS
Filter by author:
?author=jake
Favorited by user:
?favorited=jake
Limit number of articles (default is 20):
?limit=20
Offset/skip number of articles:
?offset=0
Authentication optional, will return multiple articles, ordered by most recent first
Feed articles
GET /api/articles/feed
Can also take limit and offset query parameters like List Articles
Authentication required, will return multiple articles created by followed users, ordered by most recent first.
Retrieve Article
GET /api/articles/:slug
No authentication required, will return single article
Create Article
POST /api/articles
Example request body:
{ "article": { "title": "How to train your dragon", "description": "Ever wonder how?", "body": "You have to believe", "tagList": ['reactjs', 'angularjs', 'dragons'] } } Authentication required, will return an Article
Required fields: title, description, body
Optional fields: tagList as an array of Strings
Update article
PUT /api/articles/:slug
Example request body:
{ "article": { "title": "Did you train your dragon?" } } Authentication required, returns the updated Article
Optional fields: title, description, body
The slug also gets updated when the title is changed
Adding comments to an article
POST /api/articles/:slug/comments
Example request body:
{ "comment": { "body": "His name was my name too." } } Authentication required, returns the created Comment
Required fields: body
Getting comments to an article
GET /api/articles/:slug/comments
Authentication optional, returns multiple comments
Deleting a comment
DELETE /api/articles/:slug/comments/:id
Authentication required
Favoriting an article
POST /api/articles/:slug/favorite
Authentication required, returns the Article
No additional parameters required
Unfavoriting an article
DELETE /api/articles/:slug/favorite
Authentication required, returns the Article
No additional parameters required