Node.js + Express - Baby Steps 🍼

July 6, 2020 | 10 minute read

With Express we can easily build an stateless API…and follow patterns such as REST to build routes with GET, POST, PUT, you name it!

Node.js + Express - Baby Steps 🍼

Today we’ll be taking some baby steps into the Node.js world 👶🏽. You can check the latest incarnation of the Yijing Ball-Z with Node/Express as you follow along.

Node.js: Pros vs Cons

Screen Shot 2020-06-29 at 7 57 33 PM

In Node we have synchronous code, which can be called also blocking code. Async code is non-blocking that can run the background. So by running code in the background it seems faster and the app can do many things at the same time. Node usually has its sync vs async versions of methods e.g. readFile vs readFileSync. Unless it is top level code do not run sync methods. More on this to follow.

Asynchronous Nature of Node.js

Node is single threaded, so all users access the same single thread. If a user access the line with synchronous code it can be a problem (e.g. read very large file), with async code then multiple users can run queries at same time (e.g. login, read file, logout etc). So time-consuming data can run in background and once completed will finish the request.

Example of blocking vs non-blocking code:

// playing with fs module - SYNCHRONOUS / BLOCKING

const t1 = fs.readFileSync("./large-txt", "utf-8");
const t2 = `${t1} \n Done ${Date.now()}`;

fs.writeFileSync("./txt/output.txt", t2);

//  NON BLOCK - ASYNC EXAMPLE

fs.readFile(
  "./txt/start.txt",
  "utf-8",
  // be wary of callback hell nesting here
  (err, data) => {
    console.log(data);
  }
);

Node.js is built on callbacks. But be wary of callback hell, e.g.

Screen Shot 2020-06-29 at 8 46 45 PM

Node is well known for callbacks to start nesting many levels down. We can escape this callback hell with async await or promises (I’ll discuss this in a future article).

Creating a Simple Web Server

We can create a simple node server as this:

const http = require("http");

const server = http.createServer((req, res) => {
  res.end("Hellooo");
});

// saved variable then listen port
// localhost default is '127.0.0.1'
server.listen(8000, "127.0.0.1", () => {
  console.log("listening on port 8000");
});

By using createServer() we then pass the callback function executed each time the server is requested. Then we listen on port 8000. So if you go to http://localhost:8000 on your browser you’ll see: ‘Hellooo’. Can it get more easy?

Now if you console.log the req object you’ll see a bunch of stuff, so we get access to a bunch of stuff to handle requests.

Routing with Pure Node.js

Routing can get complicated in a big app so Express is recommended but we’ll talk about it next. For now we can modify our server as this:

const server = http.createServer((req, res) => {
const pathName = req.url

if (pathName === '/') {
    res.end('Hellooo Root')
} else if (pathName === '/product') {
    res.end('Hellooo Product')
} else  {
    res.writeHead(404, {
        'Content-type': 'text/html',
        'my-own-header': 'hello-world'
        });
    res.end('<h1>Page not found!</h1>');
}}

On the 404 response we send back a header, which is a piece of info about the response sent back. my-own-header is like custom metadata we can see on the console then Network tab on Chrome.

Sneak-peak of Express

Express is a Node.js framework with useful abstractions. It is the most popular framework. It has some characteristics.

  • Allows for easy organization into MVC structure
  • Rapid development of Node.js by implementing some robust features including server-side rendering, middleware, complex routing, better handling of response/request cycle.

So we can write apps so much faster, looks simpler as well.

Express Setup and Basic Routing

Note Express is fully written on Node but it abstracts away all the complexities.

To start make sure you npm init and npm i express. Then create your App.js file that will contain your express server. Example

const express = require("express");
const app = express();

// routing with GET

app.get("/", (req, res) => {
  res.status(200).json({
    message: "Hi from Express world!",
    app: "Museo API",
  });
});

const port = 4000;
app.listen(port, () => {
  console.log(`Listening on port ${port}`);
});

Here .get() similar to Node, which has access to the request and response objects, but in Express there are even more methods. Now if you navigate to localhost:4000 you get “Hello from Express World!” as JSON and thus we have an Express API!.

Another thing you can see is that Express automatically sends app headers for you. With Express we can easily build an stateless API (in which the client will ask for specific info) and follow patterns such as REST to build routes with GET, POST, PUT, you name it!

Wanna checkout a quick demo of simple API implementation? Check this repo.

Express Request / Response Cycle

When Express receives a request, it creates a request and response object. Data will be used to process the data. To do this you can use some middleware, which intercepts and does something with the data on different stages. So here middleware is between request and response. There’s the saying that everything is middleware, so router, setting headers, logging, parsing body.

All middleware used in Express is called middleware stack. The req/res objects go into these middlewares and they call the next() function to move to the next middleware. This is called the pipeline until it reaches the final middleware that usually uses the res.send() method or similar (like res.json()). You can define your own middlewares:

app.use((req, res, next) => {
  // current time of request e.g.
  req.requestTime = new Date().toISOString;
  next();
});

Now you can use req.requestTime in any body of any request! There are many middlewares out there you don’t have to manually create them yourself. It will make your life and development much easier.

So as you grow your application, you wanna separate concerns. App.js then focuses mostly on middleware declarations using .use(). Also there should be a server.js file that deals with just starting the server and server configurations. Checkout such file in the YBZ repo.

The Importance of Middlewares in Express

So the importance of express middlewares is that you can implement validations as middleware then it will apply every time it applies. For example here’s a function that will use the params middleware to check for ‘id’ and whether it exists. This will only fire if ‘id’ is on the params of the request.

// tour controller
exports.checkId = (req, res, next, value) => {
  console.log("id is ", value);
  if (req.params.id * 1 > tours.length)
    return res.status(404).json({
      status: "fail",
      message: "Invalid ID",
    });
  // remember to call next() on middlewares
  next();
};

// tour routes
const { checkId } = require("./tourController");

router.param("id", checkId);

With this simple check we no longer have to include such validations on each route, it will hit anytime the id matches the middleware. This is the magic of Node.js enhanced by Express!

BONUS - Node Environment Variables

Node can run on development and production environments among others. You can run in a particular environment by doing for example on the terminal and starting your server as: NODE_ENV=development node server.js. This could be helpful if you use different db for development/production. You can also check environment variables with process.env.

Now the easiest way to define these variables and environments is to add them to a config.env file. To do this use a dotenv NPM package you can install as npm i dotenv. After you define your variables you can use them on server.js as this:

// top level code server.js
const dotenv = require("dotenv");
// read config.env and save as env variables
dotenv.config({ path: "./config.env" });

BONUS 2 - ESLint and Prettier Cheatsheet

Install this to use ESLint and prettier with cool config to make your Node life easier.

npm i eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-jsx-a11y eslint-plugin-react-hooks eslint-plugin-node eslint-plugin-import  eslint-plugin-react eslint-config-airbnb --save-dev

In the repo you can check the .eslintrc.json and .prettierrc file configurations used.

So although we took some baby steps on this exploration, I think they were enough to prepare us to fly with express in no time 🧙🏾‍♂️💫.

Continue Your Journey