How to create ToDo App with Node.js + Express (For beginners)

Tomohiro Meo
7 min readJun 8, 2021

Notes

Before trying to creating ToDo App, you need to get a Node.js development environment ready!

1. Generate Express template apps

The first step is to install Express.
Type the following in the console and run it.

  • npm install express-generator -g
tomohiro@meos-MacBook-Pro todo_app % npm install express-generator -g
npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
/Users/tomohiro/.anyenv/envs/nodenv/versions/14.15.1/bin/express -> /Users/tomohiro/.anyenv/envs/nodenv/versions/14.15.1/lib/node_modules/express-generator/bin/express-cli.js
+ express-generator@4.16.1
added 10 packages from 13 contributors in 2.979s
╭────────────────────────────────────────────────────────────────╮
│ │
│ New major version of npm available! 6.14.8 → 7.16.0 │
│ Changelog: https://github.com/npm/cli/releases/tag/v7.16.0
│ Run npm install -g npm to update! │
│ │
╰────────────────────────────────────────────────────────────────╯

You can check that it has been installed without any problems.

  • express -h
tomohiro@meos-MacBook-Pro todo_app % express -hUsage: express [options] [dir]Options:--version        output the version number
-e, --ejs add ejs engine support
--pug add pug engine support
--hbs add handlebars engine support
-H, --hogan add hogan.js engine support
-v, --view <engine> add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
--no-view use static html instead of view engine
-c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
--git add .gitignore
-f, --force force on non-empty directory
-h, --help output usage information

Next, you create a template application for Express with the following command.

  • express — view=ejs todoapp
```
tomohiro@meos-MacBook-Pro todo_app % express --view=ejs todoapp
create : todoapp/
create : todoapp/public/
create : todoapp/public/javascripts/
create : todoapp/public/images/
create : todoapp/public/stylesheets/
create : todoapp/public/stylesheets/style.css
create : todoapp/routes/
create : todoapp/routes/index.js
create : todoapp/routes/users.js
create : todoapp/views/
create : todoapp/views/error.ejs
create : todoapp/views/index.ejs
create : todoapp/app.js
create : todoapp/package.json
create : todoapp/bin/
create : todoapp/bin/www
change directory:
$ cd todoapp
install dependencies:
$ npm install
run the app:
$ DEBUG=todoapp:* npm start
```

Install the npm package.

  • npm install

2. Start the application and check the process flow.

The next step is starting the server with the following command.

  • npm start

You should edit the app.js file.

1. Since /users is not used, remove the following code related to /users

  • const usersRouter = require(‘. /routes/users’);
  • app.use(‘/users’, usersRouter);

2. Replace the variable declarations with var with const

  • all of them are assumed to be constants since no assignment is made after the declaration

3. Remove the following two lines and add app.use(“/”, require(“. /routes”));

  • const indexRouter = require(‘. /routes/index’);
    app.use(‘/’, indexRouter);

For the third point, in order not to bloat app.js, we have combined the two lines of code into one line.

Also, instead of . /routes/index because the index file is loaded when the folder name is specified. /routes.

The modified app.js is as follows.

const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const app = express();// view engine setupapp.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));// router
app.use('/', require('./routes'));
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));});// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;

Check the code above

Check out the code above.

Use app.use(‘/’, require(‘. /routes’)); to handle access to the route path (‘/’) in . /routes/index.

Now, open routes/index.js.

As you can imagine from router.get
In Express, we do routing with const router = express.Router(); and router.get or router.post.

In the source code, var router = express.Router();, but since var declarations are deprecated and there is no possibility of making assignments, it is better to declare it as a constant in const.

This source code makes heavy use of var and contains unnecessary declarations and sources.

Next, let’s look at rendering.

As you can see in index.js, res.render(‘index’, { title: ‘Express’ });, this is done in res.render(), and is tied to the .ejs file that matches the first argument.

The second argument is an object with the values we want to pass to the .ejs file.

You should change it to { title: ‘ToDo App’ }.

Now that we have made the changes on the server side, stop the server with ctrl + c and start it again with npm start.

The following is a ToDo App this point.

3. Implement input and save processes for ToDo tasks.

First, you should create a ToDo task input form in the index.ejs file.

Leave the <body></body> in index.ejs empty, and create the input form as a next step.

The <body> element should look like this


<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel="stylesheet" href="/stylesheets/style.css" />
</head>
<body>
<form action="/" method="post">
<label>Add Task</label>
<input required type="text" name="add" placeholder="What do you do" />
<input type="submit" value="add" />
</form>
</body>
</html>

In <form action=”/” method=”post”>, the action attribute specifies that the request should be sent to “/” by setting action=”/”.

In addition, method=”post” specifies that the request should be sent as a POST request.

Requests to the root path (“/”) are made in routes/index.js, but since routing for POST requests is not defined, we need to do it ourselves.

Try pressing the “Add” button or “Enter”.

An error message is displayed.

This is because the routing for the POST request is not defined, so the destination cannot be found and an error occurs.

Next, handle the POST request in index.js.
Open index.js.

First of all, you can write router.post as we did for router.get.
And also, change the var declaration to a const declaration.

Let’s prepare an array and write a process to add the acquired input data to the array.

Declare global variable todos with an empty array.

You should get the input data and store it in the constant todo.
The input data can be obtained from the name value of the req.body.<input> element.

const express = require('express');
const router = express.Router();
let todos = [];router.get('/', function(req, res, next) {
res.render('index', { title: 'ToDo App' });
});
router.post('/', function(req, res, next) {
const todo = req.body.add;
todos.push(todo);
res.render('index', { title: 'ToDo App' });
});
module.exports = router;

4. Display the ToDo tasks

To display the to-do tasks, you have to pass the todos that contain all the todo to the index.ejs.

The index.ejs will look like this

const express = require('express');
const router = express.Router();
let todos = [];router.get('/', function (req, res, next) {
res.render('index', {
title: 'ToDo App',
todos: todos,
});
});
router.post('/', function (req, res, next) {
const todo = req.body.add;
todos.push(todo);
res.redirect('/');
});
module.exports = router;

Do not render in router.post(), but pass todos when rendering GET requests.
In addition, when making a POST request, use res.redirect(‘/’) to send the GET request to the root path.

This way, even if you don’t render in router.post(), the redirect will execute router.get() and eventually render without any problem.

Redirect generally refers to forwarding to another web page, but even when forwarding to the same page as in this case, changing the request method will change the process to be executed, hence the term redirect.

res.redirect() is a method that redirects to the path or URL specified in the argument.

Now, you can try display the todo in the index.ejs.

Open index.ejs, and place a <ui> element under the <form> element.

This time, let’s display all todo by calling elements from the todos list one by one on the ejs side as well.

In ejs, you can do this as follows

<% for(let todo of todos){ %>   
<li><span><%= todo %></span></li>
<% } %>

The final index.ejs is like this.

<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<form action="/" method="post">
<label>Add Task</label>
<input required type="text" name="add" placeholder="What do you do?"/>
<input type="submit" value="add"/>
</form>
<ul>
<% for(let todo of todos){ %>
<li><span><%= todo %></span></li>
<% } %>
</ul>
</body>
</html>

This articles will contine to next articles.
It’s about storing the data with mysql.

Look forward to your next post!

--

--