API Design in Node.js
& Secure Authentication using JWTs
27-03-2019
About me
- João Marques
- Project Manager at KPMG
- I've worked with Node.js for about 3 years
What is an API
- Application Programming Interface
- Abstraction of the underlying implementation
- In the Web context is defined as a set of HTTP request and response messages, usually in JSON
- The API dictates how other applications must interact with data
- Usually allows basic data operations like CRUD
What is REST
- The most popular API design pattern
- Combines paths with verbs to describe the intended action
- REST is not a protocol, but rather an architectural style
- Hard to scale for very complex data models without careful planning
What is NodeJS
- The most popular Javascript runtime engine outside the browser
- Async, single threaded and event driven.
- Optimal for handling a high amount of concurrent requests
- Not great for CPU intensive work
- Huge community support with lots of existing libraries
What is Express
- The standard API framework for Node.js
- Very simple to use
- Abstracts all the sockets for matching routes, managing sockets, error handling, etc
- Huge community support with lots of existing libraries
Getting started with Express
- The express library is very simple to use
- Just import the library and start it on any available port
import express from 'express'
const app = express()
app.get('/', (req, res) => res.send('My response'))
app.listen(3000, () => console.log('Example app listening on port 3000!'))
Middleware
- Code that executes in order before reaching the end controller that handles the request
- Great for common behavior such as authentication, error handling, logging, gzip compression, CORS, etc etc
- All middlewares follow the same pattern
import express from 'express';
const myMiddleware = function(req, res, next) {
// ... do whatever I need with req and res
next() // ensure the next middleware in the chain is invoked
}
const app = express();
app.use(myMiddleware);
Exercise 1
In this exercise you'll be creating a simple Express based API in node that returns "Hello World" and logs the requests.
git clone https://gitlab.com/jmarques/api-design-nodejs.git
git checkout exercise-1
- install dependencies with npm
- create a route named 'api' that send an 'Hello World' response
- log each call to the console using the morgan logger (remember to install it with npm too!)
- start the server and test it
Complex Routing
- Express supports very complex pattern matching with exact, regex, glob and parameter matching
- Also supports HTTP verbs at route level (GET, POST, ...)
- Routes are match in the order they are defined
app.get('/route', (req, res) => { ... })
app.get(/.*fly$/, (req, res) => { ... })
app.get('/route/:routeId', (req, res) => {
// req.params: { routeId: '<value>' }
})
Exercise 2
In this exercise you'll be creating two different routes to access two different sets of data: posts and users
git checkout exercise-2
- create a route named 'api/users' that returns a list of users, and a route named 'api/users/:username' to get a specific user. Make use of the
resources/users
file to access the list of users - create a route named 'api/posts' that returns a list of posts, and a route named 'api/posts/:id' to get a specific post. Make use of the
resources/posts
file to access the list of posts - start the server and test it
Controllers and Routes
- Controllers are just the final middleware in the stack, that will actually handle the request and return a response
- Controllers handle a route + verb combination
- You've been implementing controllers to handle the GET verb
app.get('/route', (req, res) => { ... })
app.post('/route', (req, res) => { ... })
app.patch('/route', (req, res) => { ... })
// OR
app.route('/route')
.get((req, res) => { ... })
.post((req, res) => { ... })
.patch((req, res) => { ... })
Routers
- Routers allow to create modular, mountable route handlers.
- They have a complete middleware and routing system
- Allows to encapsulate all logic that relates to a route
- Can be nested, you can have Routers mounting other Routers
Routers
// in users.js
const router = express.Router()
router.get('/', (req, res) => { ... })
router.get('/:username', (req, res) => { ... })
router.post('/', (req, res) => { ... })
export default router
// in server.js
import userRouter from './users'
app.use('/users', userRouter')
Exercise 3
In this exercise you'll be expanding on the previous exercise to implement a full CRUD on two different sets of data: posts and users
git checkout exercise-3
npm i -S body-parser
- create a router named 'api/users' that handles all the methods available in
resources/users
- create a router named 'api/posts' that handles all the methods available in
resources/posts
- abstract the 'api' in its own router, using the routers created above
- start the server and test it
- BONUS: In POST responses, return the Location header in the response to indicate where the new resource is available
Authentication basics
- Authentication is about controlling if an incoming request can have a response or not
- Authorization is controlling if an authenticated request has the correct level of permissions to access a resource
- Identification is determining who the requester is
- You can never protect your API 100%
Authentication with JWTs
- Based on passing a token (with every request) to check authentication on the server
- Stateless authentication
- Involves encoding a payload using a shared secret
- After verification, the payload is available to the server. Payload can be used to store anything
Authentication with JWTs
- jsonwebtoken is a package that handles both the creation and verification of JWTs
- express-jwt is a package that simplifies the protection of routes with JWT
import jwt from 'jsonwebtoken'
import expressJwt from 'express-jwt'
const claims = {
username: 'myUser',
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1 hour
}
// to generate a token
const token = jwt.sign(claims, 'my precious')
// to verify a token
const decoded = jwf.verify(token, 'my precious')
app.use('/myPath', expressJwt({ secret: 'my precious' }).unless({ path: <unprotected paths here> })
Exercise 4
In this exercise you'll be securing the application with JWT
git checkout exercise-4
npm i -S jsonwebtoken express-jwt bcrypt
- create a new route named '/login' that allows someone to login and obtain a token
- lock down all routes with the exception of creating a new user (signup)
- start the server and test it
- BONUS: make sure that editing or removing a user is done only if it is the user itself performing the request
Questions
Thank you
API Design in Node.js & Secure Authentication using JWTs
By João Marques
API Design in Node.js & Secure Authentication using JWTs
- 148