Table of Contents for
Server Side development with Node.js and Koa.js Quick Start Guide

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition Server Side development with Node.js and Koa.js Quick Start Guide by Olayinka Omole Published by Packt Publishing, 2018
  1. Server Side Development with Node.js and Koa.js Quick Start Guide
  2. Title Page
  3. Copyright and Credits
  4. Server Side Development with Node.js and Koa.js Quick Start Guide
  5. About Packt
  6. Why subscribe?
  7. Packt.com
  8. Contributors
  9. About the author
  10. About the reviewer
  11. Packt is searching for authors like you
  12. Table of Contents
  13. Preface
  14. Who this book is for
  15. What this book covers
  16. To get the most out of this book
  17. Download the example code files
  18. Download the color images
  19. Code in action
  20. Conventions used
  21. Get in touch
  22. Reviews
  23. Introducing Koa
  24. Technical requirements
  25. What is Koa?
  26. What can you do with Koa?
  27. Why choose Koa?
  28. When you should not use Koa
  29. Koa versus Express
  30. How can this book help you understand Koa better?
  31. Summary
  32. Getting Started with Koa
  33. Technical requirements
  34. Modern JavaScript
  35. A primer on Node
  36. What is async… await?
  37. The promise class
  38. Introducing async
  39. Introducing await
  40. Installing Koa
  41. Using Babel
  42. Starting a server in Koa
  43. Summary
  44. Koa Core Concepts
  45. Technical requirements
  46. The application object
  47. Useful application methods
  48. Settings
  49. The context object
  50. Context object API
  51. Aliases
  52. The request object
  53. Content negotiation
  54. The response object
  55. Middleware
  56. Cascading in Koa
  57. Defining middleware
  58. Registering middleware
  59. Common middleware
  60. Summary
  61. Handling Errors in Koa
  62. Technical requirements
  63. Catching errors in Koa
  64. Koa's default error handler
  65. Emitting errors
  66. Error event listener
  67. Throwing HTTP errors
  68. Writing error handlers
  69. Summary
  70. Building an API in Koa
  71. Technical requirements
  72. Project setup
  73. Initialization
  74. Installing dependencies
  75. Structure
  76. Building the application
  77. Starting the server
  78. Using Nodemon
  79. Connecting to a database
  80. Creating data models
  81. Setting up the router
  82. Setting up a logger
  83. Creating contact endpoints and controller actions
  84. Retrieving all contacts
  85. Storing new contacts
  86. Retrieving a single contact
  87. Updating a contact
  88. Deleting a contact
  89. Validating requests
  90. Useful notes
  91. Summary
  92. Building an Application in Koa
  93. Technical requirements
  94. About the application
  95. Setting up a project
  96. Installing dependencies
  97. Project structure
  98. Building the application
  99. Starting the server
  100. Connecting to the database
  101. Creating data models
  102. The user model
  103. The post model
  104. Setting up the router
  105. Setting up the views
  106. Using partials
  107. Setting up sessions
  108. Handling authentication
  109. User registration and login
  110. Authentication middleware
  111. Creating controller functions
  112. Summary
  113. Other Books You May Enjoy
  114. Leave a review - let other readers know what you think

Creating controller functions

Finally, we will create controller methods for the various actions we want our application to be able to carry out. These include the following:

  • Index: View all blog posts
  • Create: View a form to create a new blog post
  • Store: Save a new blog post to the database
  • Show: View a single blog post
  • Edit: View a form to edit a blog post
  • Update: Update a blog post in the database

Let's create a controller file for our post actions name PostController in the controllers folder, and insert the following contents into it:

// ./controllers/PostController.js

const Post = require('../models/Post');

module.exports = {
async index(ctx) {
const posts = await Post.find()
.populate('author');
ctx.state.posts = posts;
ctx.state.title = 'Home';
await ctx.render('index');
},

async create(ctx) {
ctx.state.title = 'Create Post';
await ctx.render('post/create');
},

async store(ctx) {
const { body } = ctx.request;
const postData = {
...body,
author: ctx.session.user,
image: 'https://picsum.photos/300/?random'
};
const post = await new Post(postData).save();
ctx.redirect(`/post/${post.id}`);
},

async show(ctx) {
const { id } = ctx.params;
try {
const post = await Post.findById(id).populate('author');
ctx.state.post = post;
ctx.state.title = post.title;
} catch(e) {
ctx.throw(404, "Post not found");
}
await ctx.render('post/show');
},

async edit(ctx) {
const { id } = ctx.params;
try {
const post = await Post.findById(id).populate('author');
ctx.state.post = post;
ctx.state.title = `Edit Post - ${post.title}`;
} catch(e) {
ctx.throw(404, "Post not found");
}
await ctx.render('post/edit');
},

async update(ctx) {
const { id } = ctx.params;
const { body } = ctx.request;
try {
const postData = { ...body, createdAt: new Date() }
const post = await Post.findByIdAndUpdate(id, postData);
ctx.redirect(`/post/${post.id}`);
} catch(e) {
ctx.throw(e);
}
}
};

We also need to create the corresponding template files for the needed actions. Let's create a post folder in the views folder to house the post specific views needed. This will contain the following files:

  • create.ejs:
      <%- include( '../partials/header.ejs' ) -%> 
<div class="columns">
<div class="column is-three-quarters">
<h1 class="title">Create Post</h1>

<form action="/post/" method="POST">
<div class="field">
<label class="label">Title</label>
<div class="control">
<input class="input" type="text" placeholder="e.g Koa is
awesome" name="title">
</div>
</div>

<div class="field">
<label class="label">Content</label>
<div class="control">
<textarea rows="12" class="textarea" placeholder="e.g.
Hello world" name="content"></textarea>
</div>
</div>

<div class="control">
<button class="button is-primary">Submit</button>
</div>
</form>
</div>

<div class="column">
</div>
</div>
<%- include( '../partials/footer.ejs' ) -%>
  • edit.ejs:
      <%- include( '../partials/header.ejs' ) -%> 
<div class="columns">
<div class="column is-three-quarters">
<h1 class="title">Create Post</h1>

<form action="/post/<%= post.id %>/" method="POST">
<input type="hidden" name="method" value="PUT">
<div class="field">
<label class="label">Title</label>
<div class="control">
<input value="<%= post.title %>" class="input"
type="text" placeholder="e.g Koa is awesome"
name="title">
</div>
</div>

<div class="field">
<label class="label">Content</label>
<div class="control">
<textarea rows="12" class="textarea" placeholder="e.g.
Hello world" name="content"><%= post.content %>
</textarea>
</div>
</div>

<div class="control">
<button class="button is-primary">Submit</button>
</div>
</form>
</div>

<div class="column">
</div>
</div>
<%- include( '../partials/footer.ejs' ) -%>
  • show.ejs:
      <%- include( '../partials/header.ejs' ) -%> 
<h2 class="title"><%= post.title %></h2>

<div class="columns">
<div class="column is-2">
<img src="<%= post.image %>" alt="<%= post.title %>">
<h5 class="subtitle">
<small>by <%= post.author.fullName %></small>
</h5>
<% if (locals.user && (post.author.id == user._id)) { -%>
<p><small><a href="/post/<%= post.id %>/edit">Edit Post</a>
</small></p>
<% } -%>
</div>

<div class="column">
<p><%= post.content %></p>
</div>
</div>
<%- include( '../partials/footer.ejs' ) -%>

We also need to update our index.ejs file, as shown, to show the blog posts:

<!-- ./views/index.ejs -->

<%- include( 'partials/header.ejs' ) -%>
<div class="columns">
<% posts.forEach(post => { %>

<div class="column is-one-third">
<div class="card">
<div class="card-content">
<p class="title">
<%= post.title %>
</p>
<p class="subtitle">
<small>by <%= post.author.fullName %></small>
</p>
</div>
<footer class="card-footer">
<p class="card-footer-item">
<span>
<a href="/post/<%= post.id %>">View Post</a>
</span>
</p>
</footer>
</div>
</div>

<% }); %>
</div>
<%- include( 'partials/footer.ejs' ) -%>

Next, let's register these actions in our router file. We will also make the index route of our application point to the index method defined previously. Update the router as shown:

// ./middleware/router.js

const KoaRouter = require('koa-router');
const authenticated = require('./authenticated');
const guest = require('./guest');
const user = require('./user');
const authController = require('../controllers/AuthController');
const postController = require('../controllers/PostController');

const router = new KoaRouter();
router.use(user());

// base routes.
// authentication not required
router
.get('/', postController.index)
.get('/post/:id', postController.show);

// auth routes
const auth = new KoaRouter()
.get('/', guest(), authController.index)
.post('/login', authController.login)
.post('/register', authController.register)
.get('/logout', authController.logout);
router.use('/auth', auth.routes());

// blog post routes
const posts = new KoaRouter();
posts
.use(authenticated())
.post('/', postController.store)
.get('/create', postController.create)
.put('/:id', postController.update)
.get('/:id/edit', postController.edit);
router.use('/post', posts.routes());

module.exports = router;

To make put requests work, we need to implement a method overriding middleware. This will help us change the request method for the post request to update a post to be converted to a put request. We do this by creating a method-override.js file and putting the following contents as shown:

// ./middleware/method-override.js

module.exports = () => {
return async (ctx, next) => {
const { method } = ctx.request.body;
if (method) ctx.method = method;
await next();
};
};

Then, we register the middleware in our index.js as given:

// ./index.js

// ...

const methodOverride = require('./middleware/method-override');

app.use(methodOverride());

// ...

And that's it! Our blog post built from scratch in Koa is complete.