Time to begin using a backend server with Express.
Firstly, we must install Node.js if we have not already which we can do so here. The Node.js installation will also install npm
, which will be our package manager, allowing us to install Javascript packages such as Express. To confirm that both Node.js and npm have been installed correctly, run these two commands
node --version
npm --version
and if they print the version numbers, we are good to go.
When we create our first application, first create a new folder where the files will be stored. Then, inside a terminal run the following command:
npm init
Make sure you are in the right directory in your terminal! You can navigate to the folder you want to create by using the cd
command.
npm i [PACKAGE_NAME]
command. So, to install Express, we will run
npm i express
Then, we will make a file in the folder called index.js
which will be where most of our code for our server will be written. In that file, add the following code:
const express = require('express')
const app = express()
app.get('/', function(req, res) {
res.send('Hello World!')
})
app.listen(3000, () => {
console.log(`Example server started`)
})
Save the file, and then in terminal run the command
node index.js
Then, you can visit the page at http://localhost:3000/. You should see a page that just says Hello World!
. To stop the server, do Ctrl+C.
In Director, press the "Create Site" button again and fill out the form. However, this time choose "Dynamic" in the Type dropdown. After creating the site, select "Customize Docker Image" button on the site settings page, and on there select the "Node.js (Alpine)" option and click on the "Write run.sh file?" checkbox. Save the image, and then in the online terminal create a new file index.js
in the public
folder and copy paste the file above, with one exception. Replace the last three lines with
app.listen(process.env.PORT, => {
console.log(`Example server started`)
})
Note that the port 3000
has been replaced with process.env.PORT
.
Save the file, and then either click on the "Restart Process" button on the site settings page or press Alt+Enter to restart the server. After the server has finished running, visit the site URL and you should see "Hello World!"
Okay, so what just happened in those 10ish lines of code? Well, on the first line the require()
methods loads the Express module, which we then initialize on the second line. Let's skip the middle few lines for right now and discuss the last three lines. The last three lines cause the server to listen on port 3000 for connections, which is why when you visit localhost:3000 you get the webpage. Lastly, the middle few lines manage the endpoints of the server.
An endpoint is a route that the server listens for. In this case, the server listens for a request to the root of the server, which is /
. When the server receives a request to the root, it will send the message Hello World!
.
There are many functions that define these endpoints, but the most common ones are get()
and post()
. The first one is a GET request, and the second one is a POST request. When visiting a site, you will be sending a GET request, so most of the time you will be using the get()
method. POST methods usually are used when submitting forms, whcih we will discuss in a few sections below.
We won't go over the details of GET vs POST here, but you can find more info about that here.
app.get('/test', function(req, res) {
res.send('This is a test.')
})
then the server will listen for a request to /test
and respond with the message This is a test.
if you visit localhost:3000/test. Note that when making changes to the server, you will need to restart the server to see the changes. However, if you are making changes to the .html
(or the .hbs
files in the next lesson), a restart is not necessary—a simple refresh will suffice.
Inside the get()
function, we have the function send()
, which just sends a message to the client. If we wanted to have the website display an HTML file, we would use the sendFile()
method. For example, if we had a file name index.html
and wanted it to be displayed at the route /test
, we would add
app.get('/test', function(req, res) {
res.sendFile('index.html', { root: __dirname }) // the root is necessary for the application to locate the file
})
When we want to render HTML templates (like we will be doing in the next lesson with handlebars), we will use the render()
function. We will talk about that more in the next lesson.
Finally, when we want to redirect the user to another route, we will use the redirect()
method. So, for example, let's say after a user has successfully logged in and we want to redirect them to the home page, we would do useredirect(/)
method in the same endpoint with the logging in function.
You don't have to specify a specific endpoint for every request. Instead, we can use wildcards for more general routes using :
. Let's give an example.
app.get('/test/:id', function(req, res) {
res.send(`This is a test with id ${req.params.id}`)
})
req.params.id
gets the id
parameter from the route. So, when we visit localhost:3000/test/1, we will get the message This is a test with id 1
.
We can use ?
to make a parameter optional. For example, if we wanted to make the id
parameter optional, we would add
app.get('/test/:id?', function(req, res) {
if (req.params.id) {
res.send(`This is a test with id ${req.params.id}`)
} else {
res.send('This is a test without an id')
}
})
Then, if we visit localhost:3000/test/1, we still get the message This is a test with id 1
. However, if we visit localhost:3000/test, we will get the message This is a test without an id
.
Finally, *
allows us to use a wildcard within a route path. So, if we wanted to make a route that matches any route, we would add
app.get('/*', function (req, res) {
res.send('This is a wildcard route')
})
Then, any route to localhost:3000 will get the message This is a wildcard route
.
If there are duplicate possible endpoints for a certain route, the first one is picked. So, if we wanted to make an error page for routes that don't exist, we would add the *
wildcard at the bottom.
req
and res
are both objects defined by express and are passed to the endpoint function. The req
object contains information about the request, such as the URL and request method. The res
object will contain information about the response. We can add our own variables to these objects, and those will persist throughout the request. This is useful when you want to store data that will be sent back to the user during a request cycle.
We'll take a short break from Express and talk about HTML forms real quick. Forms are defined with the form
tag, and most inputs are defined with the input
tag, with the type attribute setting the type of input. Addtionally, inputs have the name
attribute, which is the name of the input and is used for the server to get the information from the submitted form.
Labels are an HTML element that help label an input. It's benefit is that touching the label of an input selects the input as well, helping with clicking small inputs such as radio inputs. When using the <label>
tag, the for
attribute must match the id
attribute of the input it is binding with.
The table below shows some common input types and their results.
HTML | Output |
---|---|
<input type="text"> | |
<input type="radio"> | |
<input type="checkbox"> | |
<input type="button" value="text"> | |
<input type="submit"> |
Radio inputs only allow one selection for each set of radio inputs, while checkboxes allow multiple. They both have a value
attribute which represent the value of the option. Radio and checkbox inputs must share the same name
attribute to link them together. So, for example,
<input type="radio" id="cat" name="animal" value="Cats" />
<label for="cat">Cats</label><br>
<input type="radio" id="dog" name="animal" value="Dogs" />
<label for="dog">Dogs</label><br>
<input type="radio" id="both" name="animal" value="Both" />
<label for="both">Both</label>
results in
Notice that only one of the buttons can be selected at a time.
Dropdown lists are made through the <select>
tag, and all the options for the list have the <option>
tag and are located inside the <select>
tag. The <option>
tag has the value
attribute, which is the value of the option. So, for example
<select id='animal'>
<option value="Cats">Cats</option>
<option value="Dogs">Dogs</option>
<option value="Both">Both</option>
</select>
results in
Also, large text area is defined by the <textarea>
tag. Again, this has the name
attribute. Additionally, it has row
and col
attributes that define the size of the text area.
<textarea name="message">
Default text here
</textarea>
results in
Finally, the submit button submits the form. It sends the data of the form to the route found in the action
attribute found in the <form>
tag, and the method that the data is being sent to is defined by the method
attribute in the <form>
tag.
Alright, that's the gist of HTML forms. Let's use Express to obtain the form data. First, we will install the body-parser
package by doing npm i body-parser
in your terminal. Then, add the following lines at the top of your application file:
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: true }))
Then, we will create a new route, and depending on the method of the request, we'll handle the request differently: if the method is GET, we will use the get()
method, and if the method is POST, we will use the post()
method. To get the data from the form, we'll use the req.body
object. So, if in the HTML form we have
<form action="./.ubmit_form" method="POST">
<input type="text" name="name" />
<input type="submit" value="Submit" />
</form>
and our index.js
file was
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: true }))
app.get('/', function(req, res) {
res.sendFile('index.html', { root: __dirname })
})
app.post('/submit_form', function(req, res) {
res.send(`Hello ${req.body.name}`)
})
app.listen(3000, () => {
console.log(`Example server started`)
})
When running the server, after filling out and submitting the form, you'll see the message Hello <NAME>
on the webpage, where NAME
should be the name you inputted.
Middleware are functions that are called during a request to the server. They have access to the req
and res
objects and run sequentially. To call the next function in a middleware function, we use the next()
method. To make a request call middleware, we will just need to add the functions in an array in the second argument of the get
method.
function myMiddleware(req, res, next) {
console.log('This is my middleware')
next() // we need this to call the next function in the chain
}
function mySecondMiddleware(req, res, next) {
console.log('request url: ' + req.originalUrl) // prints the request url using the req object
next() // calls the next function
}
app.get('/', [myMiddleware, mySecondMiddleware], function (req, res) { // we have the two middleware functions in an array
res.send('Hello World!') // prints 'This is my middleware' first and then 'request url: /'
})
Now that we got the basics of Express down, let's start using handlebars and generating HTML.