Build a Simple Beginner App with Node, Bootstrap and MongoDB — SitePoint


If you’re just getting started with Node.js and want to try your hand at building a web app, things can often get a little overwhelming. Once you get beyond the “Hello, World!” tutorials, much of the material out there has you copy-pasting code, with little or no explanation as to what you’re doing or why.

This means that, by the time you’ve finished, you’ve built something nice and shiny, but you also have relatively few takeaways that you can apply to your next project.

In this tutorial, I’m going to take a slightly different approach. Starting from the ground up, I’ll demonstrate how to build a no-frills web app using Node.js, but instead of focusing on the end result, I’ll focus on a range of things you’re likely to encounter when building a real-world app. These include routing, templating, dealing with forms, interacting with a database and even basic authentication.

This won’t be a JavaScript 101. If that’s the kind of thing you’re after, look here. It will, however, be suitable for those people who feel reasonably confident with the JavaScript language and who are looking to take their first steps in Node.js.

Table of Contents
  1. What We’ll Be Building
  2. Basic Setup
  3. Initializing the Application
  4. Adding a Templating Engine
  5. Dealing with Forms in Express
  6. Interacting with a Database
  7. Adding HTTP Authentication
  8. Serving Static Assets in Express
  9. Conclusion

What We’ll Be Building

We’ll be using Node.js and the Express framework to build a simple registration form with basic validation, which persists its data to a MongoDB database. We’ll add a view to list successful registrations, which we’ll protect with basic HTTP authentication, and we’ll use Bootstrap to add some styling. The tutorial is structured so that you can follow along step by step. However, if you’d like to jump ahead and see the end result, the code for this tutorial is also available on GitHub.

Basic Setup

Before we can start coding, we’ll need to get Node, npm and MongoDB installed on our machines. I won’t go into depth on the various installation instructions, but if you have any trouble getting set up, please visit our forums and ask for help there.

Node.js

Many websites will recommend that you head to the official Node download page and grab the Node binaries for your system. While that works, I would suggest that you use a version manager instead. This is a program which allows you to install multiple versions of Node and switch between them with ease. There are various advantages to using a version manager. For example, it negates potential permission issues which would otherwise see you installing packages with admin rights.

If you fancy going the version manager route, please consult our Install Multiple Versions of Node.js Using nvm quick tip. Otherwise, grab the correct binaries for your system from the link above and install those.

npm

npm is a JavaScript package manager which comes bundled with Node, so no extra installation is necessary here. We’ll be making quite extensive use of npm throughout this tutorial, so if you’re in need of a refresher, please consult A Beginner’s Guide to npm — the Node Package Manager.

MongoDB

MongoDB is a document database which stores data in flexible, JSON-like documents. If you’ve never worked with Mongo before, you might like to check out our beginner-friendly introduction to MongoDB.

The quickest way to get up and running with Mongo is to use a service such as MongoDB Atlas. It has a free plan which provides a single database with 512MB of storage running on a shared virtual machine. This is more than adequate for a simple app with a handful of users. If this sounds like the best option for you, please go to the Atlas home page and click the Try Free button to sign up for a free account. You’ll then be dropped into a wizard that will help you create and deploy a database. If you get stuck at any point, this video on getting your free MongoDB Atlas cluster is quite helpful.

You can also install Mongo locally. To do this, please visit the official download page and follow the instructions for your operating system. This will guide you through installing and configuring MongoDB on your machine.

A MongoDB GUI

Although not strictly necessary for following along with this tutorial, you might also like to install Compass, the official GUI for MongoDB. This tool helps you visualize and manipulate your data, allowing you to interact with documents with full CRUD functionality.

Note: if you’re using Windows and installing Mongo locally, you can install Compass as part of the Mongo installation process. No additional download is required.

Checking that everything is installed correctly

To check that Node and npm are installed correctly, open your terminal and type this:

<code class="bash language-bash">node -v
</code>

Then follow it with this:

<code class="bash language-bash"><span class="token function">npm</span> -v
</code>

This will output the version number of each program (18.16.0 and 9.5.1 respectively at the time of writing).

If you installed Mongo locally you should be able to check the version number with this:

<code class="bash language-bash">mongod --version
</code>

This should output a bunch of information, including the version number (6.0.6 at the time of writing).

Note: if you’re on Windows and you haven’t added MongoDB to your path, you’ll need to use the full path to the MongoDB Server binaries, as described on the installation page.

Checking the database connection using Compass

Assuming that MongoDB is running, open Compass.

Note: depending on how you installed Mongo, you might first need to start it with the mongod command.

You should be able to accept the defaults (server: localhost, port: 27017), press the CONNECT button, and establish a connection to the database server.

MongoDB Compass connected to localhost

The databases admin, config and local are created automatically.

Using a cloud-hosted solution

If you’re using Mongo Atlas, create your database, set up user authentication, configure IP whitelisting, then make a note of the connection details.

Open Compass, click New connection, then paste your connection string into the text area. This should be similar to the following format:

<code class="bash language-bash">mongodb+srv://<span class="token operator"><</span>username<span class="token operator">></span>:<span class="token operator"><</span>password<span class="token operator">></span>@<span class="token operator"><</span>cluster-name<span class="token operator">></span>.<span class="token operator"><</span>uid<span class="token operator">></span>.mongodb.net/
</code>

Click Connect and you should be off to the races.

MongoDB Compass connected to Atlas

MongoDB Compass connected to Atlas

Please note: if you’re running a VPN, you’ll likely need to disable this before connecting.

In both cases, Mongo will name your first database test. This is fine for the purposes of testing the connection, but we’ll see how to change this later on.

Initializing the Application

With everything set up correctly, the first thing we need to do is initialize our new project. To do this, create a folder named demo-node-app, enter that directory, and type the following in a terminal:

<code class="bash language-bash"><span class="token function">npm</span> init -y
</code>

This will create and auto-populate a package.json file in the project root. We can use this file to specify our dependencies and to create various npm scripts, which will aid our development workflow.

Installing Express

Express is a lightweight web application framework for Node.js, which provides us with a robust set of features for writing web apps. These features include such things as route handling, template engine integration and a middleware framework, which allows us to perform additional tasks on request and response objects.

To install Express, run the following in your terminal:

<code class="bash language-bash"><span class="token function">npm</span> <span class="token function">install</span> express
</code>

This will see Express added to the dependencies section of the package.json file. This signals to anyone else running our code that Express is a package our app needs to function properly.

Installing nodemon

nodemon is a convenience tool. It will watch the files in the directory it was started in, and if it detects any changes, it will automatically restart your Node application (meaning you don’t have to). In contrast to Express, nodemon is not something the app requires to function properly (it just aids us with development), so install it with this:

<code class="bash language-bash"><span class="token function">npm</span> <span class="token function">install</span> --save-dev nodemon
</code>

This will add nodemon to the dev-dependencies section of the package.json file.

Creating some initial files

We’re almost through with the setup. All we need to do now is create a couple of initial files before kicking off the app.

In the demo-node-app folder, create an app.js file and a start.js file. Also create a routes folder, with an index.js file inside.

On Linux you can run this:

<code class="shell language-shell"><span class="token function">mkdir</span> routes <span class="token operator">&&</span> <span class="token function">touch</span> app.js start.js routes/index.js
</code>

After you’re done, things should look like this:

<code class="bash language-bash"><span class="token builtin class-name">.</span>
├── app.js
├── node_modules
│   └── <span class="token punctuation">..</span>.
├── package.json
├── package-lock.json
├── routes
│   └── index.js
└── start.js
</code>

Now, let’s add some code to those files.

In app.js:

<code class="javascript language-javascript"><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> routes <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./routes/index'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token method function property-access">use</span><span class="token punctuation">(</span><span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span> routes<span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span><span class="token property-access">exports</span> <span class="token operator">=</span> app<span class="token punctuation">;</span>
</code>

Here, we’re importing both the express module and our routes file into the application. The require function we’re using to do this is a built-in Node function which imports an object from another file or module. If you’d like a refresher on importing and exporting modules, read Understanding module.exports and exports in Node.js.

After that, we’re creating a new Express app using the express function and assigning it to an app variable. We then tell the app that, whenever it receives a request from forward slash anything, it should use the routes file.

Finally, we export our app variable so that it can be imported and used in other files.

In start.js:

<code class="javascript language-javascript"><span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./app'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> server <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token method function property-access">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Express is running on port </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>server<span class="token punctuation">.</span><span class="token method function property-access">address</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token property-access">port</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Here we’re importing the Express app we created in app.js (note that we can leave the .js off the file name in the require statement). We then tell our app to listen on port 3000 for incoming connections and output a message to the terminal to indicate that the server is running.

And in routes/index.js:

<code class="javascript language-javascript"><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> router <span class="token operator">=</span> express<span class="token punctuation">.</span><span class="token method function property-access"><span class="token maybe-class-name">Router</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

router<span class="token punctuation">.</span><span class="token method function property-access">get</span><span class="token punctuation">(</span><span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  res<span class="token punctuation">.</span><span class="token method function property-access">send</span><span class="token punctuation">(</span><span class="token string">'It works!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span><span class="token property-access">exports</span> <span class="token operator">=</span> router<span class="token punctuation">;</span>
</code>

Here, we’re importing Express into our routes file and then grabbing the router from it. We then use the router to respond to any requests to the root URL (in this case http://localhost:3000) with an “It works!” message.

Kicking off the app

Finally, let’s add an npm script to make nodemon start watching our app. Change the scripts section of the package.json file to look like this:

<code class="javascript language-javascript"><span class="token string">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  <span class="token string">"watch"</span><span class="token operator">:</span> <span class="token string">"nodemon ./start.js"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
</code>

The scripts property of the package.json file is extremely useful, as it lets us specify arbitrary scripts to run in different scenarios. This means that we don’t have to repeatedly type out long-winded commands with a difficult-to-remember syntax. If you’d like to find out more about what npm scripts can do, read Give Grunt the Boot! A Guide to Using npm as a Build Tool.

Now, type npm run watch from the terminal and visit http://localhost:3000.

You should see “It works!”

Adding a Templating Engine

Returning an inline response from within the route handler is all well and good, but it’s not very extensible, and this is where templating engines come in. As the Express docs state:

A template engine enables you to use static template files in your application. At runtime, the template engine replaces variables in a template file with actual values, and transforms the template into an HTML file sent to the client.

In practice, this means we can define template files and tell our routes to use them instead of writing everything inline.

The docs continue:

Some popular template engines that work with Express are Pug, Mustache, and EJS.

So which one to use?

Basic templating with Pug

In this article, I’m going to use Pug as a templating engine. Pug (formerly known as Jade) comes with its own indentation-sensitive syntax for writing dynamic and reusable HTML.

I should point out that there has been some criticism that this project has stagnated. However, the author claims it is still maintained. Personally, I find Pug stable, full featured and easy to work with, but be aware that there are lots of other options out there, if you wish to choose a different library.

That said, let’s create a folder named views and in that folder a file named form.pug. Add the following code to this new file:

<code class="pug language-pug"><span class="token tag">form<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">action</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"."</span> method<span class="token operator">=</span><span class="token string">"POST"</span></span><span class="token punctuation">)</span></span></span>
  <span class="token tag">label<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">for</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span><span class="token punctuation">)</span></span></span> <span class="token plain-text">Name:</span>
  <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
    <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"text"</span></span>
    <span class="token attr-name">id</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
    <span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
  <span class="token punctuation">)</span></span></span>

  <span class="token tag">label<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">for</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span><span class="token punctuation">)</span></span></span> <span class="token plain-text">Email:</span>
  <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
    <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
    <span class="token attr-name">id</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
    <span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
  <span class="token punctuation">)</span></span></span>

  <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"submit"</span> value<span class="token operator">=</span><span class="token string">"Submit"</span></span><span class="token punctuation">)</span></span></span>
</code>

Hopefully the above example is easy to follow, but if you have any difficulties understanding what it does, just wait until we view this in a browser, then inspect the page source to see the markup it produces.
If you’d like a to learn a bit more about Pug before continuing, read our tutorial A Beginner’s Guide to Pug.

Installing Pug and integrating it into the Express app

Next, we’ll need to install pug, saving it as a dependency:

<code class="bash language-bash"><span class="token function">npm</span> <span class="token function">install</span> pug
</code>

Then configure app.js to use Pug as a layout engine and to look for templates inside the views folder:

<code class="javascript language-javascript"><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'path'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> routes <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./routes/index'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token method function property-access">set</span><span class="token punctuation">(</span><span class="token string">'views'</span><span class="token punctuation">,</span> path<span class="token punctuation">.</span><span class="token method function property-access">join</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'views'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token method function property-access">set</span><span class="token punctuation">(</span><span class="token string">'view engine'</span><span class="token punctuation">,</span> <span class="token string">'pug'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token method function property-access">use</span><span class="token punctuation">(</span><span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span> routes<span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span><span class="token property-access">exports</span> <span class="token operator">=</span> app<span class="token punctuation">;</span>
</code>

You’ll notice that we’re also requiring Node’s native Path module, which provides utilities for working with file and directory paths. This module allows us to build the path to our views folder using its join method and __dirname (which returns the directory in which the currently executing script resides).

Altering the route to use our template

Finally, we need to tell our route to use our new template. In routes/index.js:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token method function property-access">get</span><span class="token punctuation">(</span><span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  res<span class="token punctuation">.</span><span class="token method function property-access">render</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

This uses the render method on Express’s response object to send the rendered view to the client.

So let’s see if it worked. As we’re using nodemon to watch our app for changes, we should simply be able to refresh our browser and see our brutalist masterpiece.

Defining a layout file for Pug

If you open your browser and inspect the page source, you’ll see that Express only sent the HTML for the form. Our page is missing a doctype declaration, as well as a head and body section. Let’s fix that by creating a master layout for all our templates to use.

To do this, create a layout.pug file in the views folder and add the following code:

<code class="pug language-pug"><span class="token doctype">doctype html</span>
<span class="token tag">html</span>
  <span class="token tag">head</span>
    <span class="token tag">title</span><span class="token punctuation">=</span><span class="token code"> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>title<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span></span>

  <span class="token tag">body</span>
    <span class="token tag">h1</span> <span class="token plain-text">Event Registration</span>

    <span class="token keyword">block content</span>
</code>

The first thing to notice here is the line starting title=. Appending an equals sign to an attribute is one of the methods that Pug uses for interpolation. You can read more about it here. We’ll use this to pass the title dynamically to each template.

The second thing to notice is the line that starts with the block keyword. In a template, a block is simply a “block” of Pug that a child template may replace. We’ll see how to use it shortly, but if you’re keen to find out more, read this page on the Pug website.

Using the layout file from the child template

All that remains to do is to inform our form.pug template that it should use the layout file. To do this, alter views/form.pug, like so:

<code class="pug language-pug"><span class="token keyword">extends layout</span>

<span class="token keyword">block content</span>
  <span class="token tag">form<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">action</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"."</span> method<span class="token operator">=</span><span class="token string">"POST"</span></span><span class="token punctuation">)</span></span></span>
    <span class="token tag">label<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">for</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span><span class="token punctuation">)</span></span></span> <span class="token plain-text">Name:</span>
    <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
      <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"text"</span></span>
      <span class="token attr-name">id</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
      <span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
    <span class="token punctuation">)</span></span></span>

    <span class="token tag">label<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">for</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span><span class="token punctuation">)</span></span></span> <span class="token plain-text">Email:</span>
    <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
      <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
      <span class="token attr-name">id</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
      <span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
    <span class="token punctuation">)</span></span></span>

    <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"submit"</span> value<span class="token operator">=</span><span class="token string">"Submit"</span></span><span class="token punctuation">)</span></span></span>
</code>

And in routes/index.js, we need to pass in an appropriate title for the template to display:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token method function property-access">get</span><span class="token punctuation">(</span><span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  res<span class="token punctuation">.</span><span class="token method function property-access">render</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> title<span class="token operator">:</span> <span class="token string">'Registration form'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Now if you refresh the page and inspect the source, things should look a lot better.

Dealing with Forms in Express

Currently, if we hit our form’s Submit button, we’ll be redirected to a page with a message: “Cannot POST /”. This is because, when submitted, our form POSTs its contents back to /, and we haven’t defined a route to handle that yet.

Let’s do that now. Add the following to routes/index.js:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token method function property-access">post</span><span class="token punctuation">(</span><span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  res<span class="token punctuation">.</span><span class="token method function property-access">render</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> title<span class="token operator">:</span> <span class="token string">'Registration form'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

This is the same as our GET route, except for the fact that we’re using router.post to respond to a different HTTP action.

Now when we submit the form, the error message will be gone and the form should just re-render.

Handling form input

The next task is to retrieve whatever data the user has submitted via the form. To do this, we’ll need to install a package named body-parser, which will make the form data available on the request body:

<code class="bash language-bash"><span class="token function">npm</span> <span class="token function">install</span> body-parser
</code>

We’ll also need to tell our app to use this package, so add the following to app.js:

<code class="javascript language-javascript"><span class="token keyword">const</span> bodyParser <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'body-parser'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token spread operator">...</span>
app<span class="token punctuation">.</span><span class="token method function property-access">use</span><span class="token punctuation">(</span>bodyParser<span class="token punctuation">.</span><span class="token method function property-access">urlencoded</span><span class="token punctuation">(</span><span class="token punctuation">{</span> extended<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token method function property-access">use</span><span class="token punctuation">(</span><span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span> routes<span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span><span class="token property-access">exports</span> <span class="token operator">=</span> app<span class="token punctuation">;</span>
</code>

Note that there are various ways to format the data we POST to the server, and using body-parser’s urlencoded method allows us to handle data sent as application/x-www-form-urlencoded.

Then we can try logging the submitted data to the terminal. Alter the route handler in routes/index.js like so:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token method function property-access">post</span><span class="token punctuation">(</span><span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>req<span class="token punctuation">.</span><span class="token property-access">body</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  res<span class="token punctuation">.</span><span class="token method function property-access">render</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> title<span class="token operator">:</span> <span class="token string">'Registration form'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Now when we submit the form, we should see something along the lines of this:

<code class="bash language-bash"><span class="token punctuation">{</span>name: <span class="token string">'Jim'</span>, email: <span class="token string">'jim@example.com'</span><span class="token punctuation">}</span>
</code>

Form output logged to terminal

Form output logged to terminal

A note about request and response objects

By now, you’ve hopefully noticed the pattern we’re using to handle routes in Express:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token constant">METHOD</span><span class="token punctuation">(</span>route<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

The callback function is executed whenever somebody visits a URL that matches the route it specifies. The callback receives req and res parameters, where req is an object full of information that’s coming in (such as form data or query parameters) and res is an object full of methods for sending data back to the user. There’s also an optional next parameter, which is useful if we don’t actually want to send any data back, or if we want to pass the request off for something else to handle.

Without getting too deep into the weeds, this is a concept known as middleware (specifically, router-level middleware) which is very important in Express. If you’re interested in finding out more about how Express uses middleware, I recommend you read the Express docs.

Validating form input

Now let’s check that the user has filled out both our fields. We can do this using express-validator module, a middleware that provides a number of useful methods for the sanitization and validation of user input.

We can install it like so:

<code class="bash language-bash"><span class="token function">npm</span> <span class="token function">install</span> express-validator
</code>

Then we need to require the functions we’ll need in routes/index.js:

<code class="javascript language-javascript"><span class="token keyword">const</span> <span class="token punctuation">{</span> check<span class="token punctuation">,</span> validationResult <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express-validator'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

We can include it in our route handler like so:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token method function property-access">post</span><span class="token punctuation">(</span><span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span>
  <span class="token punctuation">[</span>
    <span class="token function">check</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token method function property-access">isLength</span><span class="token punctuation">(</span><span class="token punctuation">{</span> min<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token method function property-access">withMessage</span><span class="token punctuation">(</span><span class="token string">'Please enter a name'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token function">check</span><span class="token punctuation">(</span><span class="token string">'email'</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token method function property-access">isLength</span><span class="token punctuation">(</span><span class="token punctuation">{</span> min<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token method function property-access">withMessage</span><span class="token punctuation">(</span><span class="token string">'Please enter an email'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
    <span class="token spread operator">...</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

We’re using the check method to validate two properties on req.body — namely, name and email. In our case, it’s sufficient to just check that these properties exist (that is, that they have a length greater than one), but if you’d like to do more you can check out the full range of validators here.

In a second step, we can call the validationResult method to see if validation passed or failed. If no errors are present, we can go ahead and render out a “Thanks for registering” message. Otherwise, we’ll need to pass these errors back to our template to inform the user that something’s wrong.

And if validation fails, we’ll also need to pass req.body back to the template, so that any valid form inputs aren’t reset:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token method function property-access">post</span><span class="token punctuation">(</span>
  <span class="token string">"https://www.sitepoint.com/"</span><span class="token punctuation">,</span>
  <span class="token punctuation">[</span>
    <span class="token spread operator">...</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> errors <span class="token operator">=</span> <span class="token function">validationResult</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>errors<span class="token punctuation">.</span><span class="token method function property-access">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      res<span class="token punctuation">.</span><span class="token method function property-access">send</span><span class="token punctuation">(</span><span class="token string">'Thank you for your registration!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword control-flow">else</span> <span class="token punctuation">{</span>
      res<span class="token punctuation">.</span><span class="token method function property-access">render</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
        title<span class="token operator">:</span> <span class="token string">'Registration form'</span><span class="token punctuation">,</span>
        errors<span class="token operator">:</span> errors<span class="token punctuation">.</span><span class="token method function property-access">array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        data<span class="token operator">:</span> req<span class="token punctuation">.</span><span class="token property-access">body</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Now we have to make a couple of changes to our form.pug template. We firstly need to check for an errors property, and if it’s present, loop over any errors and display them in a list:

<code class="pug language-pug"><span class="token keyword">extends layout</span>

<span class="token keyword">block content</span>
  <span class="token flow-control"><span class="token branch keyword">if</span> errors</span>
    <span class="token tag">ul</span>
      <span class="token tag">for</span> <span class="token plain-text">error in errors</span>
        <span class="token tag">li</span><span class="token punctuation">=</span><span class="token code"> error<span class="token punctuation">.</span><span class="token property-access">msg</span></span>
  <span class="token punctuation">...</span>
</code>

If the li= looks weird, remember that pug does interpolation by following the tag name with an equals sign.

Finally, we need to check if a data attribute exists, and if so, use it to set the values of the respective fields. If it doesn’t exist, we’ll initialize it to an empty object, so that the form will still render correctly when we load it for the first time. We can do this with some JavaScript, denoted in Pug by a minus sign:

<code class="pug language-pug"><span class="token punctuation">-</span><span class="token code">data <span class="token operator">=</span> data <span class="token operator">||</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span>
</code>

We then reference that attribute to set the field’s value:

<code class="pug language-pug"><span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
  <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"text"</span></span>
  <span class="token attr-name">id</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
  <span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
  <span class="token attr-name">value</span><span class="token punctuation">=</span><span class="token attr-value">data<span class="token punctuation">.</span><span class="token property-access">name</span></span>
<span class="token punctuation">)</span></span></span>
</code>

Note: in Pug, by default, all attributes are escaped. That is, special characters are replaced with escape sequences to prevent attacks (such as cross site scripting).

This gives us the following:

<code class="pug language-pug"><span class="token keyword">extends layout</span>

<span class="token keyword">block content</span>
  <span class="token punctuation">-</span><span class="token code">data <span class="token operator">=</span> data <span class="token operator">||</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span>

  <span class="token flow-control"><span class="token branch keyword">if</span> errors</span>
    <span class="token tag">ul</span>
      <span class="token tag">for</span> <span class="token plain-text">error in errors</span>
        <span class="token tag">li</span><span class="token punctuation">=</span><span class="token code"> error<span class="token punctuation">.</span><span class="token property-access">msg</span></span>

  <span class="token tag">form<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">action</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"."</span> method<span class="token operator">=</span><span class="token string">"POST"</span></span><span class="token punctuation">)</span></span></span>
    <span class="token tag">label<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">for</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span><span class="token punctuation">)</span></span></span> <span class="token plain-text">Name:</span>
    <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
      <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"text"</span></span>
      <span class="token attr-name">id</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
      <span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
      <span class="token attr-name">value</span><span class="token punctuation">=</span><span class="token attr-value">data<span class="token punctuation">.</span><span class="token property-access">name</span></span>
    <span class="token punctuation">)</span></span></span>

    <span class="token tag">label<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">for</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span><span class="token punctuation">)</span></span></span> <span class="token plain-text">Email:</span>
    <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
      <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
      <span class="token attr-name">id</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
      <span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
      <span class="token attr-name">value</span><span class="token punctuation">=</span><span class="token attr-value">data<span class="token punctuation">.</span><span class="token property-access">email</span></span>
    <span class="token punctuation">)</span></span></span>

    <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"submit"</span> value<span class="token operator">=</span><span class="token string">"Submit"</span></span><span class="token punctuation">)</span></span></span>
</code>

Now, when we submit a successful registration, we should see a thank you message, and when we submit the form without filling out both field, the template should be re-rendered with an error message.

Interacting with a Database

We now want to hook our form up to our database, so that we can save whatever data the user enters. If you’re working with Mongo locally, don’t forget to make sure the server is running (possibly with the command mongod).

Specifying connection details

We’ll need somewhere to specify our database connection details. For this, we’ll use a configuration file (which should not be checked into version control) and the dotenv package. Dotenv will load our connection details from the configuration file into Node’s process.env.

Install it like so:

<code class="bash language-bash"><span class="token function">npm</span> <span class="token function">install</span> dotenv
</code>

And require it at the top of start.js:

<code class="javascript language-javascript"><span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'dotenv'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">config</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Next, create a file named .env in the project root (note that starting a filename with a dot may cause it to be hidden on certain operating systems) and enter your Mongo connection details on the first line.

If you’re running Mongo locally:

<code class="bash language-bash"><span class="token assign-left variable">DATABASE</span><span class="token operator">=</span>mongodb://localhost:27017/<span class="token operator"><</span>dbname<span class="token operator">></span>
</code>

Make sure to replace with whatever you are calling your database.

If you’re using Mongo Atlas, use the connection string you noted down previously. It should be in this format:

<code class="bash language-bash">mongodb+srv://<span class="token operator"><</span>username<span class="token operator">></span>:<span class="token operator"><</span>password<span class="token operator">></span>@<span class="token operator"><</span>cluster-name<span class="token operator">></span>.<span class="token operator"><</span>uid<span class="token operator">></span>.mongodb.net/<span class="token operator"><</span>dbname<span class="token operator">></span>
</code>

We’re now specifying a database name, which Mongo will create if it doesn’t exist. You can name yours whatever you like and use Compass to delete the test database.

A word on security

There are two security matters to point out before we go any further. Neither should affect your ability to follow along with this tutorial, but they are definitely things you should be aware of.

  • Local installations of MongoDB don’t have a default user or password. This is definitely something you’ll want to change in production, as it’s otherwise a security risk. You can check the Mongo docs for more info on how to do this.
  • If you’re using Git to version your project, be sure to add the .env file to your .gitignore. The .env file should remain on your PC and not be shared with anyone.

That said, let’s carry on building the app …

Connecting to the database

To establish the connection to the database and to perform operations on it, we’ll be using Mongoose. Mongoose is an ODM (object-document mapper) for MongoDB, and as we can read on the project’s home page:

Mongoose provides a straight-forward, schema-based solution to model your application data. It includes built-in type casting, validation, query building, business logic hooks and more, out of the box.

This means that it creates various abstractions over Mongo, which make interacting with our database easier and reduces the amount of boilerplate we have to write. If you’d like to find out more about how Mongo works under the hood, be sure to read our Introduction to MongoDB.

Install Mongoose like so:

<code class="bash language-bash"><span class="token function">npm</span> <span class="token function">install</span> mongoose
</code>

Then, require it in start.js:

<code class="javascript language-javascript"><span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

The connection is made like this:

<code class="javascript language-javascript">mongoose<span class="token punctuation">.</span><span class="token method function property-access">connect</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span><span class="token property-access">env</span><span class="token punctuation">.</span><span class="token constant">DATABASE</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  useNewUrlParser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  useUnifiedTopology<span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

mongoose<span class="token punctuation">.</span><span class="token property-access">connection</span>
  <span class="token punctuation">.</span><span class="token method function property-access">on</span><span class="token punctuation">(</span><span class="token string">'open'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span><span class="token string">'Mongoose connection open'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token method function property-access">on</span><span class="token punctuation">(</span><span class="token string">'error'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Connection error: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>err<span class="token punctuation">.</span><span class="token property-access">message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Notice how we use the DATABASE variable we declared in the .env file to specify the database URL.

This is what start.js should now look like:

<code class="javascript language-javascript"><span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'dotenv'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">config</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

mongoose<span class="token punctuation">.</span><span class="token method function property-access">connect</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span><span class="token property-access">env</span><span class="token punctuation">.</span><span class="token constant">DATABASE</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  useNewUrlParser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  useUnifiedTopology<span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

mongoose<span class="token punctuation">.</span><span class="token property-access">connection</span>
  <span class="token punctuation">.</span><span class="token method function property-access">on</span><span class="token punctuation">(</span><span class="token string">'open'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span><span class="token string">'Mongoose connection open'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token method function property-access">on</span><span class="token punctuation">(</span><span class="token string">'error'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Connection error: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>err<span class="token punctuation">.</span><span class="token property-access">message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./app'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> server <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token method function property-access">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Express is running on port </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>server<span class="token punctuation">.</span><span class="token method function property-access">address</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token property-access">port</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

When we save the file, nodemon will restart the app and, if all’s gone well, we should see the message “Mongoose connection open”.

Defining a Mongoose Schema

MongoDB can be used as a loose database, meaning it’s not necessary to describe what data will look like ahead of time. However, we’re using Mongoose to interact with it, and everything in Mongoose starts with a schema. In Mongoose, each schema maps to a MongoDB collection and defines the shape of the documents within that collection.

To this end, create a models folder in the project root, and within that folder, a new file named Registration.js.

Add the following code to Registration.js:

<code class="javascript language-javascript"><span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> registrationSchema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">mongoose<span class="token punctuation">.</span>Schema</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token punctuation">{</span>
    type<span class="token operator">:</span> <span class="token known-class-name class-name">String</span><span class="token punctuation">,</span>
    trim<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  email<span class="token operator">:</span> <span class="token punctuation">{</span>
    type<span class="token operator">:</span> <span class="token known-class-name class-name">String</span><span class="token punctuation">,</span>
    trim<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span><span class="token property-access">exports</span> <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token method function property-access">model</span><span class="token punctuation">(</span><span class="token string">'Registration'</span><span class="token punctuation">,</span> registrationSchema<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Here, we’re just defining a type (as we already have validation in place) and are making use of the trim helper method to remove any superfluous white space from user input. We then compile a model from the Schema definition, and export it for use elsewhere in our app.

The final piece of boilerplate is to require the model in start.js:

<code class="javascript language-javascript"><span class="token spread operator">...</span>

<span class="token method function property-access">require</span><span class="token punctuation">(</span><span class="token string">'./models/Registration'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./app'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> server <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token method function property-access">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Express is running on port </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>server<span class="token punctuation">.</span><span class="token method function property-access">address</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token property-access">port</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Saving data to the database

Now we’re ready to save user data to our database. Let’s begin by requiring Mongoose and importing our model into our routes/index.js file:

<code class="javascript language-javascript"><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> check<span class="token punctuation">,</span> validationResult <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express-validator'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> router <span class="token operator">=</span> express<span class="token punctuation">.</span><span class="token method function property-access"><span class="token maybe-class-name">Router</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token maybe-class-name">Registration</span> <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token method function property-access">model</span><span class="token punctuation">(</span><span class="token string">'Registration'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token spread operator">...</span>
</code>

Now, when the user posts data to the server, if validation passes we can go ahead and create a new Registration object and attempt to save it. As the database operation is an asynchronous operation which returns a promise, we can chain a .then() onto the end of it to deal with a successful insert and a .catch() to deal with any errors:

<code class="diff language-diff">if (errors.isEmpty()) {
<span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line">  res.send('Thank you for your registration!');
</span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line">  const registration = new Registration(req.body);
</span><span class="token prefix inserted">+</span><span class="token line">  registration.save()
</span><span class="token prefix inserted">+</span><span class="token line">    .then(() => { res.send('Thank you for your registration!'); })
</span><span class="token prefix inserted">+</span><span class="token line">    .catch((err) => {
</span><span class="token prefix inserted">+</span><span class="token line">      console.log(err);
</span><span class="token prefix inserted">+</span><span class="token line">      res.send('Sorry! Something went wrong.');
</span><span class="token prefix inserted">+</span><span class="token line">    });
</span></span>} else {
<span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> ...
</span></span>}

...
</code>

Now, if we enter your details into the registration form, they should be persisted to the database. We can check this using Compass (hit Ctrl + R to refresh the data if our newly created records aren’t showing).

Using Compass to check that our data was saved to MongoDB

Using Compass to check that our data was saved to MongoDB

Retrieving data from the database

To round the app off, let’s create a final route, which lists out all of our registrations. Hopefully you should have a reasonable idea of the process by now.

Add a new route to routes/index.js, as follows:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token method function property-access">get</span><span class="token punctuation">(</span><span class="token string">'/registrations'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  res<span class="token punctuation">.</span><span class="token method function property-access">render</span><span class="token punctuation">(</span><span class="token string">'index'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> title<span class="token operator">:</span> <span class="token string">'Listing registrations'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

This means that we’ll also need a corresponding view template (views/index.pug):

<code class="pug language-pug"><span class="token keyword">extends layout</span>

<span class="token keyword">block content</span>
  <span class="token tag">p</span> <span class="token plain-text">No registrations yet :(</span>
</code>

Now when we visit http://localhost:3000/registrations, we should see a message telling us that there aren’t any registrations.

Let’s fix that by retrieving our registrations from the database and passing them to the view. We’ll still display the “No registrations yet” message, but only if there really aren’t any.

In routes/index.js:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token method function property-access">get</span><span class="token punctuation">(</span><span class="token string">'/registrations'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  <span class="token maybe-class-name">Registration</span><span class="token punctuation">.</span><span class="token method function property-access">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">registrations</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
      res<span class="token punctuation">.</span><span class="token method function property-access">render</span><span class="token punctuation">(</span><span class="token string">'index'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> title<span class="token operator">:</span> <span class="token string">'Listing registrations'</span><span class="token punctuation">,</span> registrations <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> res<span class="token punctuation">.</span><span class="token method function property-access">send</span><span class="token punctuation">(</span><span class="token string">'Sorry! Something went wrong.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Here, we’re using Mongo’s Collection.find method, which, if invoked without parameters, will return all of the records in the collection. Because the database lookup is asynchronous, we’re waiting for it to complete before rendering the view. If any records were returned, these will be passed to the view template in the registrations property. If no records were returned, registrations will be an empty array.

In views/index.pug, we can then check the length of whatever we’re handed and either loop over it and output the records to the screen, or display a “No registrations” message:

<code class="pug language-pug"><span class="token keyword">extends layout</span>

<span class="token keyword">block content</span>

<span class="token keyword">block content</span>
  <span class="token flow-control"><span class="token branch keyword">if</span> registrations<span class="token punctuation">.</span><span class="token property-access">length</span></span>
    <span class="token tag">table</span>
      <span class="token tag">thead</span>
        <span class="token tag">tr</span>
         <span class="token tag">th</span> <span class="token plain-text">Name</span>
         <span class="token tag">th</span> <span class="token plain-text">Email</span>
      <span class="token tag">tbody</span>
      <span class="token flow-control"><span class="token each"><span class="token keyword">each</span> registration <span class="token keyword">in</span></span> registrations</span>
        <span class="token tag">tr</span>
          <span class="token tag">td</span><span class="token punctuation">=</span><span class="token code"> registration<span class="token punctuation">.</span><span class="token property-access">name</span></span>
          <span class="token tag">td</span><span class="token punctuation">=</span><span class="token code"> registration<span class="token punctuation">.</span><span class="token property-access">email</span></span>
  <span class="token flow-control"><span class="token branch keyword">else</span></span>
    <span class="token tag">p</span> <span class="token plain-text">No registrations yet :(</span>
</code>

Adding HTTP Authentication

The final feature we’ll add to our app is HTTP authentication, locking down the list of successful registrations from prying eyes.

To do this, we’ll use the http-auth module, which we can install like so:

<code class="bash language-bash"><span class="token function">npm</span> <span class="token function">install</span> http-auth
</code>

Next we need to require it in routes/index.js, along with the Path module we met earlier:

<code class="javascript language-javascript"><span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'path'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> auth <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'http-auth'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Next, let it know where to find the file in which we’ll list the users and passwords (in this case users.htpasswd in the project root):

<code class="javascript language-javascript"><span class="token keyword">const</span> basic <span class="token operator">=</span> auth<span class="token punctuation">.</span><span class="token method function property-access">basic</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  file<span class="token operator">:</span> path<span class="token punctuation">.</span><span class="token method function property-access">join</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'../users.htpasswd'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Create this users.htpasswd file next and add a username and password separated by a colon. This can be in plain text, but the http-auth module also supports hashed passwords, so you could also run the password through a service such as Htpasswd Generator.

For me, the contents of users.htpasswd look like this:

<code class="bash language-bash">jim:<span class="token variable">$apr1</span><span class="token variable">$FhFmamtz</span><span class="token variable">$PgXfrNI95HFCuXIm30Q4V0</span>
</code>

This translates to user: jim, password: password.

Finally, add it to the route you wish to protect and you’re good to go:

<code class="javascript language-javascript">router<span class="token punctuation">.</span><span class="token method function property-access">get</span><span class="token punctuation">(</span><span class="token string">'/registrations'</span><span class="token punctuation">,</span> basic<span class="token punctuation">.</span><span class="token method function property-access">check</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  <span class="token spread operator">...</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Try accessing http://localhost:3000/registrations in your browser (refreshing the page, or restarting your browser if necessary). You should now be prompted for a password.

Note: you should also add users.htpasswd to your .gitignore file if you’re using Git.

Serving Static Assets in Express

Let’s give the app some polish and add some styling using Bootstrap. We can serve static files such as images, JavaScript files and CSS files in Express using the built-in express.static middleware function.

Setting it up is easy. Just add the following line to app.js:

<code class="javascript language-javascript">app<span class="token punctuation">.</span><span class="token method function property-access">use</span><span class="token punctuation">(</span>express<span class="token punctuation">.</span><span class="token method function property-access">static</span><span class="token punctuation">(</span><span class="token string">'public'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Now we can load files that are in the public directory.

Styling the app with Bootstrap

Create a public directory in the project root, and in the public directory create a css directory. Download Bootstrap v5.3 as a zip file and extract it, then find bootstrap.min.css and place it in our public/css directory.

Next, we’ll need to add some markup to our Pug templates.

In layout.pug:

<code class="pug language-pug"><span class="token doctype">doctype html</span>
<span class="token tag">html</span>
  <span class="token tag">head</span>
    <span class="token tag">title</span><span class="token punctuation">=</span><span class="token code"> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>title<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span></span>
    <span class="token tag">link<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">rel</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">'stylesheet'</span></span><span class="token punctuation">,</span> <span class="token attr-name">href</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">'/css/bootstrap.min.css'</span></span><span class="token punctuation">)</span></span></span>
    <span class="token tag">link<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">rel</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">'stylesheet'</span></span><span class="token punctuation">,</span> <span class="token attr-name">href</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">'/css/styles.css'</span></span><span class="token punctuation">)</span></span></span>

  <span class="token tag">body</span>
    <span class="token tag">div<span class="token attr-class">.container</span></span>
      <span class="token tag">h1<span class="token attr-class">.text-center</span><span class="token attr-class">.mb-4</span></span> <span class="token plain-text">Event Registration</span>

      <span class="token keyword">block content</span>
</code>

Here, we’re including two files from our previously created css folder and adding a couple of Bootstrap classes.

In form.pug we need to add a couple of wrapper divs, as well as some further class names to the error messages and the form elements:

<code class="pug language-pug"><span class="token keyword">extends layout</span>

<span class="token keyword">block content</span>
  <span class="token punctuation">-</span><span class="token code">data <span class="token operator">=</span> data <span class="token operator">||</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span>

  <span class="token tag">div<span class="token attr-class">.form-wrapper</span></span>
    <span class="token flow-control"><span class="token branch keyword">if</span> errors</span>
      <span class="token tag">ul<span class="token attr-class">.error-messages</span></span>
        <span class="token tag">for</span> <span class="token plain-text">error in errors</span>
          <span class="token tag">li</span><span class="token punctuation">=</span><span class="token code"> error<span class="token punctuation">.</span><span class="token property-access">msg</span></span>

    <span class="token tag">form<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">action</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"."</span> method<span class="token operator">=</span><span class="token string">"POST"</span></span><span class="token punctuation">)</span></span></span>
      <span class="token tag">div<span class="token attr-class">.mb-3</span></span>
        <span class="token tag">label<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">for</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"form-label"</span></span><span class="token punctuation">)</span></span></span> <span class="token plain-text">Name:</span>
        <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
          <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"text"</span></span>
          <span class="token attr-name">class</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"form-control"</span></span>
          <span class="token attr-name">id</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
          <span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"name"</span></span>
          <span class="token attr-name">placeholder</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"Enter your name"</span></span>
          <span class="token attr-name">value</span><span class="token punctuation">=</span><span class="token attr-value">data<span class="token punctuation">.</span><span class="token property-access">name</span></span>
        <span class="token punctuation">)</span></span></span>
      <span class="token tag">div<span class="token attr-class">.mb-3</span></span>
        <span class="token tag">label<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">for</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"form-label"</span></span><span class="token punctuation">)</span></span></span> <span class="token plain-text">Email:</span>
        <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
          <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
          <span class="token attr-name">class</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"form-control"</span></span>
          <span class="token attr-name">id</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
          <span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"email"</span></span>
          <span class="token attr-name">placeholder</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"Enter your email"</span></span>
          <span class="token attr-name">value</span><span class="token punctuation">=</span><span class="token attr-value">data<span class="token punctuation">.</span><span class="token property-access">email</span></span>
        <span class="token punctuation">)</span></span></span>

      <span class="token tag">input<span class="token attributes"><span class="token punctuation">(</span>
        <span class="token attr-name">type</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"submit"</span></span>
        <span class="token attr-name">class</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"btn btn-primary"</span></span>
        <span class="token attr-name">value</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"Submit"</span></span>
      <span class="token punctuation">)</span></span></span>
</code>

And in index.pug, more of the same:

<code class="pug language-pug"><span class="token keyword">extends layout</span>

<span class="token keyword">block content</span>

  <span class="token flow-control"><span class="token branch keyword">if</span> registrations<span class="token punctuation">.</span><span class="token property-access">length</span></span>
    <span class="token tag">table<span class="token attr-class">.table</span><span class="token attr-class">.table-bordered</span><span class="token attr-class">.table-striped</span><span class="token attr-class">.mt-5</span></span>
      <span class="token tag">thead<span class="token attr-class">.table-dark</span></span>
        <span class="token tag">tr</span>
         <span class="token tag">th</span> <span class="token plain-text">Name</span>
         <span class="token tag">th</span> <span class="token plain-text">Email</span>
      <span class="token tag">tbody</span>
      <span class="token flow-control"><span class="token each"><span class="token keyword">each</span> registration <span class="token keyword">in</span></span> registrations</span>
        <span class="token tag">tr</span>
          <span class="token tag">td</span><span class="token punctuation">=</span><span class="token code"> registration<span class="token punctuation">.</span><span class="token property-access">name</span></span>
          <span class="token tag">td</span><span class="token punctuation">=</span><span class="token code"> registration<span class="token punctuation">.</span><span class="token property-access">email</span></span>
  <span class="token flow-control"><span class="token branch keyword">else</span></span>
    <span class="token tag">p</span> <span class="token plain-text">No registrations yet :(</span>
</code>

Finally, create a file called styles.css in the css folder and add the following:

<code class="css language-css"><span class="token selector">body</span> <span class="token punctuation">{</span>
  <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
  <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
  <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">100</span><span class="token unit">vh</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.form-wrapper</span></span> <span class="token punctuation">{</span>
  <span class="token property">max-width</span><span class="token punctuation">:</span> <span class="token number">350</span><span class="token unit">px</span><span class="token punctuation">;</span>
  <span class="token property">margin</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.form-wrapper</span> <span class="token class">.form-control</span><span class="token punctuation">,</span>
<span class="token class">.form-wrapper</span> <span class="token class">.btn</span></span> <span class="token punctuation">{</span>
  <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100</span><span class="token unit">%</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.error-messages</span></span> <span class="token punctuation">{</span>
  <span class="token property">color</span><span class="token punctuation">:</span> <span class="token color">red</span><span class="token punctuation">;</span>
  <span class="token property">list-style</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
  <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">;</span>
  <span class="token property">margin-bottom</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token unit">px</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.error-messages</span> li<span class="token pseudo-element">::before</span></span> <span class="token punctuation">{</span>
  <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"•"</span><span class="token punctuation">;</span>
  <span class="token property">margin-right</span><span class="token punctuation">:</span> <span class="token number">5</span><span class="token unit">px</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code>

Now when you refresh the page, you should see all of the Bootstrap glory!

The finished form

Conclusion

I hope you’ve enjoyed this tutorial. While we didn’t build the next Facebook, I hope that I was nonetheless able to help you make a start in the world of Node-based web apps and offer you some solid takeaways for your next project in the process. As noted above, the code for this tutorial is available on GitHub.

And while this tutorial has opened doors to fundamental concepts, mastering Node.js involves diving deeper and exploring its more intricate layers. A great way to further your knowledge and skills is through one of our books, Node.js: Novice to Ninja, over on SitePoint Premium.

If you have any questions or comments, please reach out on Twitter.

Happy coding!





Source link

Leave a Reply

Your email address will not be published. Required fields are marked *