Code Self Study Forum

[Tutorial] An Introduction to Node.js Servers (and Express.js)

Nice write up Josh. :slightly_smiling_face:

1 Like
npm install -g express-generator
cd ~/sites # ...or wherever you collect your website projects
express --ejs test # Scaffold an Express app called test
cd test
npm install
DEBUG test:* npm start # Bring it up with debug logging
# Ctl-C
code . # Assuming that you have Visual Studio Code to start editing
1 Like

I didn’t include that because it was already getting long. :slight_smile:

I usually pass in Handlebars like this…

$ express --view=hbs appname

…and then restructure everything except for parts of the app.js and bin/www files something like this:

.
├── bin
├── controllers
├── db
├── helpers
├── models
├── public
│   ├── fonts
│   ├── images
│   ├── js
│   └── styles
├── routes
└── views

I’ve been meaning to write a custom Express generator, but haven’t done it yet.

I know that I’ll be using this tomorrow to scaffold yet another project. It’s such a time saver.

2 Likes

OK, I just went through this, and coming from PHP this is making sense now. I also see the power of handlebars!

Basically to me, the advantage of node over PHP is all the dynamic async things you can do. AJAX helps with that of course, but is more cumbersome. Hmmm

I am going to push through with this.

As for feedback on the tutorial I had to run all the installs using sudo . So it might be good to put a note in about that.
I.E. sudo npm install

Also is require in node like include in PHP? It seems that way to me.

Thanks again for this, I am going to try port a few pages of my site! :slight_smile:

Oh, and another fun thing (for me) I was able to edit and all the files and folders in terminal. I did use nano though. :wink:

1 Like

AJAX

AJAX works with node in a similar way as PHP. In PHP the steps would look almost the same, but just with different syntax. I’ll write an example below.

If you start the node server and visit localhost:3333/api (after making sure the API route from the example repo has been added) it should return some JSON. You can fetch that JSON with AJAX by opening the browser console while on a localhost:3333 page and typing:

fetch("/api")
    .then(res => res.json())
    .then(json => console.log("here is the JSON response", json));

and it should print out the JSON response from the server.

sudo

If the terminal asks for sudo when installing npm packages, Node probably isn’t set up correctly. I’m guessing that Node wasn’t installed with nvm. If you want to fix it (possible security risk), send me a chat message and I could walk through the steps on how to uninstall and reinstall node using nvm.

require

I think in PHP it would be more like use, but I haven’t used much PHP in years and don’t remember the syntax. I think include injects the entire other PHP file into the current file, while use or require just imports specific functions that are allowed to be exported from the other file.

PHP server

You can compare the Express code with modern PHP code by looking at a similar framework for PHP called Slim.

I added some comments to their sample code from the homepage:

<?php
// This is like `require` in Node. Each line imports some functionality
// from another PHP library. In Node, the file that lists the
// dependencies is called `package.json`. In PHP, it's called
// `composer.json`. Here, it's importing `Request` and `Response` as
// well as `AppFactory`, which is like the Node.js line that requires
// `express`.
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

// This is PHP code to load the dependencies.
require __DIR__ . '/../vendor/autoload.php';

// This is like `const app = express()` in Node/Express
$app = AppFactory::create();

// This is like the `app.get("/hello", function(request, response) ...` lines in Express.
$app->get('/hello/{name}', function (Request $request, Response $response, array $args) {
    $name = $args['name'];
    $response->getBody()->write("Hello, $name");
    return $response;
});

// This is like `app.listen(...)` in Express code
$app->run();

Nearly all major languages let you create servers with similar steps: Python/Flask, Ruby/Sinatra, etc.

1 Like

OK, got it, thanks! I am working my way through handlebars, but will try the API and re-installing node after that, and check back in. :slight_smile:

1 Like

I just wrote the rest of the post. I think I accidentally hit ctrl-enter, which submits the post. :thinking:

Handlebars is for returning HTML content. If you want to return JSON, have the function return res.json instead of res.render.

// To send HTML from a template
function aboutPage(request, response) {
    const someData = { /* get data from the DB here */ }; 
    res.render("templateName.hbs", someData);
}

// To send JSON from a JS object
function apiPage(request, response) {
    const someJavaScriptObject = { /* get data from the database here */ }; 
    res.json(someJavaScriptObject);
}
1 Like

Nice! Can I inline CSS in handlebars? Just searching how to do that.
Usually with PHP I have separate CSS files and then use include to inline them for faster loading.

Edit: I guess I can just include plain CSS with partials? :man_shrugging:

Question on handlebars.
I want to build my pages using partials like header, css, and footer.
I read the docs, but I am not sure where I register the partial

https://handlebarsjs.com/guide/partials.html#basic-partials

Handlebars.registerPartial("myPartial", "{{name}}");

Also, is there a way to reference the partial in the folder?

1 Like

I haven’t tried it, but I think that would work.

Are you using Express.js or just Handlebars by itself?

As far as I know I am using express per the tutorial above.

I actually have not made any changes to the server.js code yet. So just porting to the home page…

1 Like

I think you can put this in the server.js file:

// this goes at the top
const hbs = require('hbs');

Look for the line that sets the view engine and code after it:

// look for this line
app.set("view engine", "hbs");

// add this below that one
hbs.registerPartials(__dirname + '/views/partials');

Then create a directory at views/partials/ and put your partials there.

An example partial file at views/partials/footer.hbs might look like this:

<footer class="container" id="mainFooter">
    <hr />
    Copyright &copy; 2020 {{siteName}}
</footer>

Then your layout.hbs file could include any partial files like this:

{{> navbar}}
{{{body}}}
{{> footer}}

I haven’t tested that recently but am copying from some old code I wrote in 2017. If it doesn’t work, let me know.

1 Like

Thanks for that! It was close to working, looks like in addition to the line where you define the path to the partials you also need a line to define each partial like so:
hbs.registerPartial("myPartial", "{{footer}}");

It is working now, I will report back if I can inline css! :slight_smile:

1 Like

So question: Does the server.js have to contain every route for every page?
I do have template pages in my tools subdomain, but I also have a lot of static pages that are “articles”. Do I have to route to all those articles?

Also, if I have to route to every server-generated page, I feel like the server.js is going to be a very large file. Is that right?

Is there one separate file for each page on the server, or does the page content come from a database?

I’ll post an example of how you can split things up into multiple files below.

1 Like

I separate my routes. For example I separate my stressors routes from the rest of server.js.

const express = require("express");
const morgan = require("morgan");
const mongoose = require("mongoose");

const { PORT, DATABASE_URL } = require("./config");
const { Stressor } = require("./models");
const stressorsRouter = require("./stressorsRouter");

const app = express();

app.use(morgan("common"));
app.use(express.static("public"));

/* Route requests that come to the `/stressors` endpoint
to the Express router instance in the stressorsRouter module. */
app.use("/stressors", stressorsRouter);

mongoose.Promise = global.Promise;
2 Likes

I made a longer example of one way to do it and posted it on Github: https://github.com/j127/express_structure

The app.js (same idea as server.js above) file imports the routes on these lines (the way that flcpge mentioned).

Then the routers get mounted here at three different paths:

  • /
  • /things
  • /api

Then the router mounts controller functions on the routes. Example, the thing router sends requests for localhost:3000/things to this function in the thingsController.js file.

Another example:

I used the words “page”, “thing”, and “api” in the example code — you could do similar things with any content type (“page”, “article”, “food”, etc.).

1 Like

At this point, I have another question actually. If I render the handlebars page using Node will the {{variables}} still be there for subsequent client sideloading? Or put another way, will the template still be there for client side loading?

How does it then work for subsequent loads on the client side?
Do I have to render a handlebars template that then gets populated on the client side? With a handlebars library on the client side?
Or do I still use document.getElementById?

This is great content, @Josh!

1 Like