History: Node.js and Heroku

Revision made 7 years ago by Francisco Presencia. Go to the last revision.

Let's learn how to use javascript to program a server based on Node.js and upload the website to Heroku. First, the main file for our simple server; write this in a file called app.js:

let server = require('server');
let { get } = server.router;

server({},
  get('/', (req, res) => res.send('Hello world'))
);

Then in a file called .gitignore we will write this. This is to avoid uploading all the dependencies to the host (Heroku) later on and only upload our code:

node_modules

Lastly, let's install server:

npm install server --save

We will also install nodemon as a global package since it makes things easier:

npm install nodemon -g

Now we can see that message on the server. Run nodemon and open http://localhost:3000/ in your browser:

nodemon

Routing

Now let's tell our server what files and how it should display. First let's just display index.html. In app.js, write this instead of our previous 'Hello world':

let server = require('server');
let { get, post } = server.router;

// Route
let home = get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html'));
}

server({}, home);

If we reopen http://localhost:3000/ and refresh it we can see our index.html without any style or javascript.

The server will automatically know where our css and (front-end) javascript is, which is in a folder named public by default. We could change this and the server port in the options:

server({ port: 8080, public: './static' }, ...);

Note: only the Route part is shown from now on for brevity, but you should write the whole code as shown before.

Now let's create a route to get the file requested dynamically. We will get the url fragment as the variable name and then use it:

let page = get('/:name', (req, res) => {
  console.log(req.params);
  res.send(req.params);
});

The previous one only shows us in the terminal what file we are reading and on the browser the file name. Let's actually display the file in the browser:

let page = get('/:name', (req, res) => {
  var file = __dirname + '/' + req.params.name + '.html';
  res.sendFile(file);
});

Forms

Let's try to create a form and handle it with Node.js. First we create the form in contact.html:

<!DOCTYPE html>
<html>
  <head>
    <title>Página Web</title>
    <meta charset="utf-8">
    <meta name='viewport' content='width=device-width, initial-scale=1'>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/picnicss/6.1.1/picnic.min.css">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>

    <form action="/contacto" method="POST" class="contact">
      <h1>Contacto</h1>
      <input name="name" placeholder="Nombre">
      <input name="lastname" placeholder="Apellidos">
      <input name="age" placeholder="Edad">
      <input type="submit" value="Enviar">
    </form>

  </body>
</html>

And in style.css:

.contact {
  width: 500px;
  margin: 100px auto;
}

.contact input {
  margin-top: 0;
  margin-bottom: 10px;
}
Now we can read the data on the server. So far we won't be storing it, just display it on the terminal:
app.post('/contact', function(req, res){
  console.log(req.body);
  res.redirect('/');
});

If everything worked fine, this should display our data from the contact form and then redirect us home. We could change this for a /thankyou.html page or just handle it with javascript on the front-end and not redirect anywhere.

REST API

A REST API is a group of URLs and actions that can be done to them (GET, POST, PUT, DELETE). Let's say we have a user system. This would be a basic REST API for it:

GET /users            # Get all of the users data
GET /users/456546     # Get one specific user data
POST /users           # Create a new user
PUT /users/435465     # Edit an existing user
DELETE /users         # Remove all the users
DELETE /users/354643  # Remove a single user

This is useful on its own, but specially useful with mobile apps as well as webapps (React, Angular, etc). It separates our logic at some specific points and makes it easier to handle the complexity.

Let's make a small API that handles a Like button. In our app.js:

let server = require('server');
let { get, post, del } = server.router;

// An array with the ids of the things we liked
let likes = [];

// Retrieve all of our likes
let show = get('/likes', (req, res) => {
  res.json(likes);
});

// Create a new like
let add = post('/likes/:id', (req, res) => {
  let id = req.params.id;
  likes.push(id);
  res.json({ added: true });
});

// Remove a like
let remove = del('/likes/:id', (req, res) => {
  let id = req.params.id;
  let index = likes.indexOf(id);
  likes.splice(index, 1)
  res.json({ deleted: true });
});

server({}, show, add, remove);

Now we can have a list of items that can be liked:

<main>
  <ul>
    <li>First <button data-id="1">Like</button></li>
    <li>Second <button data-id="2">Like</button></li>
    <li>Third <button data-id="3">Like</button></li>
    <li>Fourth <button data-id="4">Like</button></li>
  </ul>
</main>

<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script>
  // Load all the likes and add the class 'active' to the ones we liked
  fetch('/likes/').then(res => res.json()).then(likes => {
    likes.forEach(id => {
      $(`button[data-id='${id}']`).addClass('active');
    });
  });

  // Handle the button clicks
  $('button').on('click', function(e){
    var id = $(e.target).attr('data-id');
    var method = $(e.target).hasClass('button');
    fetch('/likes/' + id, { method: method });
  });
</script>

Exercise: make a dislike button that calls the route /dislike through POST and DELETE and shows on the browser console what the server is doing.