Welcome to a series on learning how to build web applications with Node and Express! đ
Express is a micro-framework built to run on a Node.js server. Originally, JavaScript was made to run in web browsers. As the language matured, people explored opportunities to use JavaScript to also write web backends. The result was Node.js. Essentially, it runs the same JavaScript engine that is used in the Chrome Browser (called v8) but does that without the graphical interface of the web browser. Using Node.js you can execute JavaScript files on your computer like any other scripting language by typing node filname.js
in the command line.
Prerequisites
Iâll assume you have some previous knowledge to keep this course focused. If any of the following topics seem foreign to you, make sure to catch up on them before continuing with this tutorial.
- Using the command line (on your operating system)
- Using git
- How the internet works (client-server relationship)
- HTML (CSS is optional but helpful)
- JavaScript (particularly functions, for-loops, object-oriented programming with classes)
How this tutorial works
This series has several lessons, each covering a larger topic. Itâs best to go through the tutorial in the given order. Some concepts only make sense after understanding some pre-required topics.
The approach of this tutorial is very project-focused. Throughout this tutorial, weâll be working on an example project: an e-commerce website to sell cookies. Follow along the instructions and try to immediately put into practice what you see.
đĄ By following along with the tutorial you will end up with a complete application in the end. In addition, you should come up with a separate project (e.g. a blog or a todo list application are great starter projects). Try to apply what you have learned in each lesson to that second project of yours. Only if you practice transferring your knowledge to separate projects over and over again, youâll actually learn and memorize these concepts.
Install Node.js
To get started, go to nodejs.org and follow the link to install the âLTSâ version of Node. Thatâs not the newest version but the one thatâs currently most widely used.
After youâve installed Node, open the command line and confirm that you have installed it by running node -v
. This should output the version number of Node that you have installed.
npm
Part of Node is also a software called npm. When installing Node, you automatically installed npm as well. Npm stands for âNode Package Managerâ. It gives you access to an online library of numerous packages. Packages are pre-written pieces of JavaScript uploaded by other developers all over the world. Express is one of those packages, and weâll install that later.
First, however, we have to initialize a project. Create a new project folder and give it any name you like - for example, cookieshop
. In your command line, navigate into that folder. From within the folder, run the following command:
npm init
You will be asked to fill out some information about your project. Donât worry too much about those details. You can always change them later. If you donât know what to write simply hit the âEnterâ key on your keyboard to autocomplete. The final step asks you if youâre OK with the configuration defined. Just hit âEnterâ again. Now, you should see a new file in that folder generated for you: package.json. This file is the heart of any JavaScript application. It lists some general information about your app, but itâll also include a list of all the packages installed and used in your application.
Install Express
Now that our project is prepared, we can install Express. Express is a micro-framework. That means it behaves much like a library and not like a full-featured framework. If youâre familiar with other frameworks such as Angular, Next.js, Ruby on Rails, or Django, you may be used to frameworks that generate a whole folder structure for you. Thatâs one of the key differences to micro-frameworks. Micro-frameworks have no opinion about the folder structure. You can set up your project in any way you like. Youâll generally start with nothing and create every single file in your application by yourself.
The word âlibraryâ means that Express is a library of JavaScript functions pre-written by the developers of Express. They are functions to make building web applications easier and more convenient for you. This way, you donât have to reinvent the wheel over and over again but simply use the functions predefined by the Express team.
To install Express, run the following command:
npm install express
This may take a moment. When the installation is done, youâll see a couple of new things pop up in your project folder. You see a new file called package-lock.json and a folder called node_modules. The folder contains all the packages installed for your project. It contains the actual code. Have a look around. Youâll see that Express comes with a whole load of packages, and you can see the code of each of them.
The package-lock.json file is an auto-generated file. You should never touch it manually. It specifies which exact versions of which modules you have currently installed. This is very useful when working together with other developers and wanting to make sure everyone uses the exact same versions.
Finally, if you take a look at the package.json file, youâll also notice that express
was added as one of the dependencies
. The version number is automatically set to the newest one if you donât specify it when installing a package.
đĄ If you ever want to uninstall a package, all you need to do is run
npm uninstall package-name
.
.gitignore
If you inspect the node_modules folder youâll notice that itâs quite the huge folder. Since the package-lock.json file includes already all the relevant information about which versions to install, you donât need to make the entire node_modules folder part of your code repository.
Youâll most likely work with git to manage the versions of your application. If you havenât already, create a file called .gitignore and write the following line in it:
node_modules
If you are using macOS, you should also add .DS_Store
on a new line. Thatâs just a mac-specific file that sometimes gets auto-generated and shouldnât be part of your repository either.
Creating an Express application
First, weâll need a code file that serves as the starting point of our application. Executing that file will start our application server so that users can interact with it.
In your project folder, create a new file called app.js.
In the package.json file, I suggest to also change the
"main"
value from"index.js"
to"app.js"
. While itâs strictly speaking not necessary right now, itâll keep your code consistent and may avoid silly issues in the future.
Importing vs Requiring modules
Inside the newly created app.js file, we need to import the express
package we just installed in order to use it. Importing and exporting (also known as require
and module.export
) are extremely important concepts when working with larger JavaScript projects. As your project grows, you donât want to keep all your code inside a single file. Instead, youâll have multiple files. But since, in the end, youâll just start a single file (in our case, the app.js), you need to specify inside that file which other files (or packages) you want to open.
For the longest time, Node used a module called CommonJS for importing different files into each other. Using that syntax, youâd use the function require()
to import either a package or another file from your project folder. Most Node projects use this way of importing modules (âmodulesâ are any type of packages or JavaScript files youâd like to import).
In newer versions of Node, itâs also possible to use the recent JavaScript standard called ECMAScript modules to import modules. Instead of writing require()
, youâd be writing import
.
In this tutorial, weâll be using the newer import
syntax throughout the project. Just be aware that many references on the internet (such as the official Express documentation) may still use CommonJS instead. Often, when you see this:
const express = require('express')
You can write the same thing with the new syntax like this:
import express from 'express'
However, sometimes this wonât work because the particular package youâre using may not support the new syntax exactly how youâd like to use it. In that case, you can just use require()
instead.
To be able to use the new syntax, you have to add the following property to your package.json file:
"type": "module",
It doesnât matter where you add it as long as itâs part of the main project. I added it right below the line defining the "main"
file of my project.
The Node developer decided to keep require()
as the standard for now in order to be backwards compatible. Thatâs why you have to explicitly enable the new syntax with the line we just added.
Letâs finally import express
and initialize express in our app.js file:
import express from 'express'
const app = express()
All of the Express functionality is built into the single express()
function. Once you run it, you start Express and gain access to all its features and functions.
Starting an Express server
Under the hood, Express comes with a built-in web server software. Thatâs very convenient because it means you donât have to run any additional server software. You can just start the Express server, and your website is live.
To start the server, you use the listen()
function available through express()
.
import express from 'express'
const app = express()
const PORT = 3000
app.listen(PORT, () => {
console.log(`đ Started server on port ${PORT}`)
})
Thatâs it. If you now run node app.js
and execute the app.js file, itâll start the server, and the server will be running in the Terminal window.
The first parameter of the listen()
function represents the port your server is running on. Explaining ports would go beyond the scope of this tutorial. If youâre not familiar with them, all you need to know is that ports are a way to open up your computer (or a server) to the network (like the internet). You could technically have multiple web servers run on a single device. By using different ports for each one of them, you can allow other devices to access all those different web servers on the same computer.
There is much more to it, and if youâre interested in learning more, I suggest reading up on networking. For now, however, just know that all web servers need to run on a port that could be any number specified by you. Some ports like 3000
, 4000
, or 8080
have become standard ports among web developers.
The second argument of the listen()
function is another function. That function will be executed the moment the server is started. In our example, this function just contains a single console.log()
. But in a real-world application, we could run any code here that needs to be run once whenever the server starts.
Alright, our server is running. You can access it by going to localhost:3000 in your web browser. But since we didnât add any page or response, opening that link will just throw an error.
Adding your first route
Letâs render some text when someone accesses the URL above. In app.js, above the app.listen()
function, add the following function:
app.get('/', (request, response) => {
response.send('Welcome to my đȘ Cookieshop!')
})
You can stop the express server in the terminal by hitting the keys ctrl
+ c
on your keyboard. Then, start the server again by running again node app.js
.
If you now go to localhost:3000 in the web browser, you should see our welcome message.
get()
is another function provided by Express that allows us to add routes to our web backend that can be accessed by web browsers or other clients. Youâll learn in more detail about routes in the next lesson.
npm run and nodemon
Before we wrap up this lesson, letâs do one more thing in our package.json. In there you find a section called "scripts"
. This section letâs you define shorthand command line commands relevant to your application. Right now, it only contains a "test"
command that responds with an error that no tests are defined. We may change that in a later lesson.
Scripts from the package.json file can be executed with the command npm run
followed by the name of the script. Try running npm run test
. Do you see the error message defined in the package.json?
Itâs common practice to define a script called start
with the command to start up the server. Right now, the start command is very short. Itâs node app.js
. But in the future, it might get a little longer. So itâs best to just set it up already.
Inside the "scripts"
object and above the line with the "test"
command, add a new line:
"start": "node app.js",
Now, you can start your application by running npm run start
.
However, whenever you make any changes in the code, you still need to stop the server with Ctrl
+ C
and restart it again with npm run start
. Wouldnât it be nice if changes are automatically detected and the server reloads on its own? Thatâs what a package called nodemon
does for you. Install it by running:
npm install nodemon --save-dev
Notice the additional --save-dev
in the command. Thatâs a so-called âflagâ. You can add predefined âflagsâ to some command line commands to slightly alter or specify what should happen. After running the command, take a look at the package.json of your project. You can see that "nodemon"
was added under a new category called devDependencies
instead of dependencies
. --save-dev
automatically moved nodemon
into that second category. As the name suggests, this category is meant for any packages that you install that are only relevant for the development process but not relevant for running the server. When you run the application on a server later, you donât need it to automatically restart when you make changes to the code because whenever you make changes, you make them on your local computer and then redeploy and restart the entire application.
Now, letâs add one more script
to the package.json.
Underneath the line "start": "node app.js",
add the following new line:
"dev": "nodemon app.js",
This is what the package.json could look like now (your version will most certainly have different version numbers.)
{
"name": "cookieshop-express",
"version": "1.0.0",
"description": "",
"main": "app.js",
"type": "module",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.1"
},
"devDependencies": {
"nodemon": "^2.0.19"
}
}
From now on, whenever you start working on your application, you just run npm run dev
to start up the server. And as soon as you make updates to the code, the server is automatically restarted.
A word on JavaScript Syntax
JavaScript is highly opinionated and has changed over time. Many developers have strong opinions about the right way to write JavaScript.
In this tutorial, I will follow the newest standards whenever possible or reasonable. Sometimes, you may read things that could be more efficient, or other developers may even strongly disagree with my approach. Generally, I will favor readability and understandability over everything else.
Here are a few important highlights you should know:
- I will use arrow functions whenever possible for better readability.
- I will avoid using
var
and instead useconst
whenever possible. Whenever a variable value were to change, in most cases, Iâll try to assign a new variable withconst
instead of mutating an existing variable. I believe that makes code much easier to read and debug. Iâm not 100% strict on this, though. In some contained situations, I may uselet
for variables that are meant to change their value. - The most controversial one is my use of semicolons - or the lack thereof. JavaScript has a feature called Automatic Semicolon Insertion (ASI). Itâs a built-in part of JavaScript and wonât be removed because of JavaScriptâs backward compatibility. Because of that, itâs almost never necessary to use semicolons at the end of a line. There are very few instances in which semicolons are really necessary. Thatâs when you start a line with parentheses or brackets
(
,[
,{
. A semicolon is only necessary if one of those shows up at the beginning of a line. In those cases, I place a semicolon right in front of them like so:;(
,;[
,;{
. I do this because I favor readable code over convention and rules. And in most cases, the semicolon is simply not necessary. Removing it removes noise from the code and makes it more readable.
đ How to practice
The last part of each lesson will always include a short section on how you can practice what you have learned. You will only learn the contents of each lesson if you practice it.
As you go through the tutorial, you should have at least one more separate project to work on. So at any given time, there should be at least two projects youâre working on: the cookie shop and your own project.
In order to practice, create a separate, brand new project. Install express
and set up a main starter file for the project. Try to get it to run in the browser.