+ - 0:00:00
Notes for current slide
Notes for next slide

Building a RESTful API with Koa

next generation web framework for node.js




Presented by Michael Herman

koa tree logo

1 / 77

Agenda

2 / 77

Agenda

(1) Hello
  1. About Me
  2. Objectives
3 / 77

Agenda

(1) Hello
  1. About Me
  2. Objectives
(2) Theory
  1. What is Koa?
  2. Test Driven Development
  3. What are we building?
4 / 77

Agenda

(1) Hello
  1. About Me
  2. Objectives
(2) Theory
  1. What is Koa?
  2. Test Driven Development
  3. What are we building?
(3) Practice
  1. Project Setup
  2. Hello, World!
  3. Routes Setup
  4. CRUD Routes - GET, POST, PUT, DELETE
5 / 77

Agenda

(1) Hello
  1. About Me
  2. Objectives
(2) Theory
  1. What is Koa?
  2. Test Driven Development
  3. What are we building?
(3) Practice
  1. Project Setup
  2. Hello, World!
  3. Routes Setup
  4. CRUD Routes - GET, POST, PUT, DELETE
(4) Goodbye
  1. Next Steps
  2. Questions
6 / 77

About Michael

$ whoami
michael.herman
7 / 77

About Michael

$ whoami
michael.herman

Day Job:

Galvanize (since May 2015)...

  1. Lead Instructor Full Stack
  2. Curriculum Developer
  3. Senior Software Engineer
8 / 77

About Michael

$ whoami
michael.herman

Day Job:

Galvanize (since May 2015)...

  1. Lead Instructor Full Stack
  2. Curriculum Developer
  3. Senior Software Engineer

Also:

  1. Co-founder/author of Real Python
  2. 😍 - tech writing, open source, financial models, radiohead, chilling
9 / 77

Objectives

By the end of this tutorial, you will be able to:

10 / 77

Objectives

By the end of this tutorial, you will be able to:

  1. Set up a project with Koa using test driven development
11 / 77

Objectives

By the end of this tutorial, you will be able to:

  1. Set up a project with Koa using test driven development

  2. Set up the testing structure with Mocha and Chai

12 / 77

Objectives

By the end of this tutorial, you will be able to:

  1. Set up a project with Koa using test driven development

  2. Set up the testing structure with Mocha and Chai

  3. Create a CRUD app, following RESTful best practices

13 / 77

Objectives

By the end of this tutorial, you will be able to:

  1. Set up a project with Koa using test driven development

  2. Set up the testing structure with Mocha and Chai

  3. Create a CRUD app, following RESTful best practices

  4. Write integration tests

14 / 77

Objectives

By the end of this tutorial, you will be able to:

  1. Set up a project with Koa using test driven development

  2. Set up the testing structure with Mocha and Chai

  3. Create a CRUD app, following RESTful best practices

  4. Write integration tests

  5. Write tests, and then write just enough code to pass the tests

15 / 77

Objectives

By the end of this tutorial, you will be able to:

  1. Set up a project with Koa using test driven development

  2. Set up the testing structure with Mocha and Chai

  3. Create a CRUD app, following RESTful best practices

  4. Write integration tests

  5. Write tests, and then write just enough code to pass the tests

  6. Create routes with Koa Router

16 / 77

Objectives

By the end of this tutorial, you will be able to:

  1. Set up a project with Koa using test driven development

  2. Set up the testing structure with Mocha and Chai

  3. Create a CRUD app, following RESTful best practices

  4. Write integration tests

  5. Write tests, and then write just enough code to pass the tests

  6. Create routes with Koa Router

  7. Parse the request body with koa-bodyparser

17 / 77

What is Koa?

18 / 77

What is Koa?

Koa is a web framework for Node.js.

node logo

19 / 77

What is Koa?

Koa is a web framework for Node.js.

node logo

  • Lightweight - Although, it's developed by the same team that created Express, it’s much lighter than Express though, so it comes with very little out of the box. It’s really just a tiny wrapper on top of Node's HTTP module.
20 / 77

What is Koa?

Koa is a web framework for Node.js.

node logo

  • Lightweight - Although, it's developed by the same team that created Express, it’s much lighter than Express though, so it comes with very little out of the box. It’s really just a tiny wrapper on top of Node's HTTP module.

  • Async/Await - It has native support for async/await, which makes it easier and faster to develop an API since you don't have to deal with callbacks and callback hell.

21 / 77

What is Koa?

Koa is a web framework for Node.js.

node logo

  • Lightweight - Although, it's developed by the same team that created Express, it’s much lighter than Express though, so it comes with very little out of the box. It’s really just a tiny wrapper on top of Node's HTTP module.

  • Async/Await - It has native support for async/await, which makes it easier and faster to develop an API since you don't have to deal with callbacks and callback hell.

  • Express-like: Since Koa has similar patterns to Express, it's relatively easy to pick up if you've worked at all with Express.

22 / 77

What is Koa?

Koa is a web framework for Node.js.

node logo

  • Lightweight - Although, it's developed by the same team that created Express, it’s much lighter than Express though, so it comes with very little out of the box. It’s really just a tiny wrapper on top of Node's HTTP module.

  • Async/Await - It has native support for async/await, which makes it easier and faster to develop an API since you don't have to deal with callbacks and callback hell.

  • Express-like: Since Koa has similar patterns to Express, it's relatively easy to pick up if you've worked at all with Express.


For more on how Koa compares to Express, check out Koa vs Express.

23 / 77

Test Driven Development

24 / 77

Test Driven Development

Test Driven Development (TDD) is an iterative development cycle that emphasizes writing automated tests before writing the actual code.

25 / 77

Test Driven Development

Test Driven Development (TDD) is an iterative development cycle that emphasizes writing automated tests before writing the actual code.

Why?

26 / 77

Test Driven Development

Test Driven Development (TDD) is an iterative development cycle that emphasizes writing automated tests before writing the actual code.

Why?

  1. Helps break down problems into manageable pieces since you should have a better understanding of what you’re going to write
  2. Forces you to write cleaner code
  3. Prevents over coding
27 / 77

Test Driven Development

Test Driven Development (TDD) is an iterative development cycle that emphasizes writing automated tests before writing the actual code.

Why?

  1. Helps break down problems into manageable pieces since you should have a better understanding of what you’re going to write
  2. Forces you to write cleaner code
  3. Prevents over coding


Red-Green-Refactor

28 / 77

What are we building?

  1. We will be designing a RESTful API, using test driven development, for a single resource - movies.
29 / 77

What are we building?

  1. We will be designing a RESTful API, using test driven development, for a single resource - movies.

  2. The API itself will follow RESTful design principles, using the basic HTTP verbs - GET, POST, PUT, and DELETE.

30 / 77

What are we building?

  1. We will be designing a RESTful API, using test driven development, for a single resource - movies.

  2. The API itself will follow RESTful design principles, using the basic HTTP verbs - GET, POST, PUT, and DELETE.


URL HTTP Verb Action
/api/v1/movies GET Return ALL movies
/api/v1/movies/:id GET Return a SINGLE movie
/api/v1/movies POST Add a movie
/api/v1/movies/:id PUT Update a movie
/api/v1/movies/:id DELETE Delete a movie
31 / 77

What are we building?

  1. We will be designing a RESTful API, using test driven development, for a single resource - movies.

  2. The API itself will follow RESTful design principles, using the basic HTTP verbs - GET, POST, PUT, and DELETE.


URL HTTP Verb Action
/api/v1/movies GET Return ALL movies
/api/v1/movies/:id GET Return a SINGLE movie
/api/v1/movies POST Add a movie
/api/v1/movies/:id PUT Update a movie
/api/v1/movies/:id DELETE Delete a movie


tools and tech
32 / 77

Project Setup

33 / 77

Project Setup

Clone down the base project:

$ git clone https://github.com/mjhea0/node-koa-api \
--branch v1 --single-branch
$ cd node-koa-api
34 / 77

Project Setup

Clone down the base project:

$ git clone https://github.com/mjhea0/node-koa-api \
--branch v1 --single-branch
$ cd node-koa-api

Check out the v1 tag to the master branch and install the dependencies:

$ git checkout tags/v1 -b master
$ npm install
35 / 77

Project Setup

Clone down the base project:

$ git clone https://github.com/mjhea0/node-koa-api \
--branch v1 --single-branch
$ cd node-koa-api

Check out the v1 tag to the master branch and install the dependencies:

$ git checkout tags/v1 -b master
$ npm install

Run two quick sanity checks to make sure all is well:

$ npm start
It works!
$ npm test
Sample Test
✓ should pass
1 passing (8ms)
36 / 77

Project Setup

Clone down the base project:

$ git clone https://github.com/mjhea0/node-koa-api \
--branch v1 --single-branch
$ cd node-koa-api

Check out the v1 tag to the master branch and install the dependencies:

$ git checkout tags/v1 -b master
$ npm install

Run two quick sanity checks to make sure all is well:

$ npm start
It works!
$ npm test
Sample Test
✓ should pass
1 passing (8ms)

Take a quick look at the project structure before moving on.

37 / 77

Hello, World! (part 1)

RED

38 / 77

Hello, World! (part 1)

RED

Install Koa and Chai HTTP:

$ npm install koa@2.3.0 --save
$ npm install chai-http@3.0.0 --save-dev
39 / 77

Hello, World! (part 1)

RED

Install Koa and Chai HTTP:

$ npm install koa@2.3.0 --save
$ npm install chai-http@3.0.0 --save-dev


Create a new file in the "test" directory called routes.index.test.js...

40 / 77

Hello, World! (part 2)

RED

41 / 77

Hello, World! (part 2)

RED

Add the code to routes.index.test.js

process.env.NODE_ENV = 'test';
const chai = require('chai');
const should = chai.should();
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
const server = require('../src/server/index');
describe('routes : index', () => {
describe('GET /', () => {
it('should return json', (done) => {
chai.request(server)
.get('/')
.end((err, res) => {
res.status.should.eql(200);
res.type.should.eql('application/json');
res.body.status.should.eql('success');
res.body.message.should.eql('hello, world!');
done();
});
});
});
});
42 / 77

Hello, World! (part 3)

RED

43 / 77

Hello, World! (part 3)

RED

Run $ npm test. The tests should fail.

44 / 77

Hello, World! (part 4)

GREEN

45 / 77

Hello, World! (part 4)

GREEN

Update src/server/index.js like so:

const Koa = require('koa');
const app = new Koa();
const PORT = 1337;
app.use(async (ctx) => {
ctx.body = {
status: 'success',
message: 'hello, world!'
};
});
const server = app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}`);
});
module.exports = server;
46 / 77

Hello, World! (part 4)

GREEN

Update src/server/index.js like so:

const Koa = require('koa');
const app = new Koa();
const PORT = 1337;
app.use(async (ctx) => {
ctx.body = {
status: 'success',
message: 'hello, world!'
};
});
const server = app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}`);
});
module.exports = server;

Run the tests again. They should pass!

47 / 77

Hello, World! (part 5)

REFACTOR

48 / 77

Hello, World! (part 5)

REFACTOR

Unlike Express, Koa does not provide any routing middleware. There are a number of options available, but we'll use koa-router due to its simplicity.

$ npm install koa-router@7.2.1 --save
49 / 77

Hello, World! (part 5)

REFACTOR

Unlike Express, Koa does not provide any routing middleware. There are a number of options available, but we'll use koa-router due to its simplicity.

$ npm install koa-router@7.2.1 --save

Create a new folder called "routes" within "server", and then add an index.js file to it:

const Router = require('koa-router');
const router = new Router();
router.get('/', async (ctx) => {
ctx.body = {
status: 'success',
message: 'hello, world!'
};
})
module.exports = router;
50 / 77

Hello, World! (part 6)

REFACTOR

51 / 77

Hello, World! (part 6)

REFACTOR

Update src/server/index.js:

const Koa = require('koa');
const indexRoutes = require('./routes/index');
const app = new Koa();
const PORT = process.env.PORT || 1337;
app.use(indexRoutes.routes());
const server = app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}`);
});
module.exports = server;

Essentially, we moved the / route out of the main application file. Ensure the tests still pass before moving on.

52 / 77

Routes Setup (part 1)

53 / 77

Routes Setup (part 1)

Add a new route file called movies.js to "routes":

const Router = require('koa-router');
const data = require('../data.json');
const router = new Router();
const BASE_URL = `/api/v1/movies`;
module.exports = router;
54 / 77

Routes Setup (part 1)

Add a new route file called movies.js to "routes":

const Router = require('koa-router');
const data = require('../data.json');
const router = new Router();
const BASE_URL = `/api/v1/movies`;
module.exports = router;

Then, wire this file up to the main application in src/server/index.js:

const Koa = require('koa');
const indexRoutes = require('./routes/index');
const movieRoutes = require('./routes/movies');
const app = new Koa();
const PORT = process.env.PORT || 1337;
app.use(indexRoutes.routes());
app.use(movieRoutes.routes());
const server = app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}`);
});
module.exports = server;
55 / 77

Routes Setup (part 2)

56 / 77

Routes Setup (part 2)

Add a file called data.json to "src/server":

[
{
"name": "The Land Before Time",
"genre": "Fantasy",
"rating": 7,
"explicit": false
},
{
"name": "Jurassic Park",
"genre": "Science Fiction",
"rating": 9,
"explicit": true
},
{
"name": "Ice Age: Dawn of the Dinosaurs ",
"genre": "Action/Romance",
"rating": 5,
"explicit": false
}
]
57 / 77

Routes Setup (part 3)

58 / 77

Routes Setup (part 3)

Finally, add a new test file to "test" called routes.movies.test.js:

process.env.NODE_ENV = 'test';
const chai = require('chai');
const should = chai.should();
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
const server = require('../src/server/index');
describe('routes : movies', () => {
});
59 / 77

GET all movies (part 1)

RED

60 / 77

GET all movies (part 1)

RED

Start with a test:

describe('GET /api/v1/movies', () => {
it('should return all movies', (done) => {
chai.request(server)
.get('/api/v1/movies')
.end((err, res) => {
should.not.exist(err);
res.status.should.equal(200);
res.type.should.equal('application/json');
res.body.status.should.eql('success');
res.body.data.length.should.eql(3);
res.body.data[0].should.include.keys(
'id', 'name', 'genre', 'rating', 'explicit'
);
done();
});
});
});
61 / 77

GET all movies (part 2)

GREEN

62 / 77

GET all movies (part 2)

GREEN

To get the test to pass, add the route handler to src/server/routes/movies.js:

router.get(BASE_URL, async (ctx) => {
try {
ctx.body = {
status: 'success',
data: data
};
} catch (err) {
console.log(err)
}
})
63 / 77

GET all movies (part 2)

GREEN

To get the test to pass, add the route handler to src/server/routes/movies.js:

router.get(BASE_URL, async (ctx) => {
try {
ctx.body = {
status: 'success',
data: data
};
} catch (err) {
console.log(err)
}
})

Run the tests to ensure they pass.

64 / 77

GET a single movie

Your turn!

65 / 77

GET a single movie

Your turn!


Red-Green-Refactor

66 / 77

POST, PUT, and DELETE (part 1)

67 / 77

POST, PUT, and DELETE (part 1)

Before, we add the tests and routes, we need to add koa-bodyparser, since Koa does not parse the request body by default.

$ npm install koa-bodyparser@4.2.0 --save
68 / 77

POST, PUT, and DELETE (part 2)

69 / 77

POST, PUT, and DELETE (part 2)

Add the requirement to src/server/index.js, and then make sure to mount the middleware to the app before the routes:

const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const indexRoutes = require('./routes/index');
const movieRoutes = require('./routes/movies');
const app = new Koa();
const PORT = process.env.PORT || 1337;
app.use(bodyParser());
app.use(indexRoutes.routes());
app.use(movieRoutes.routes());
const server = app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}`);
});
module.exports = server;
70 / 77

POST, PUT, and DELETE (part 3)

Your turn!

71 / 77

POST, PUT, and DELETE (part 3)

Your turn!


Red-Green-Refactor

72 / 77

That's it!

73 / 77

That's it!

What's next?

74 / 77

Agenda

2 / 77
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow