Understanding Express + Node
5 min read

Understanding Express + Node

The purpose of this article is to get a basic understanding of what makes Node.Js different from running browser-side Javascript and why Express is helpful.

This is not a step by step beginner tutorial - I suggest reading the official documentation for a proper beginner's guide.

Running Javascript in Node.Js vs Browser

Node.Js is a Javascript runtime environment that executes Javascript code. It runs on top of Google's V8 engine (a C++ engine that executes Javascript) which was created for Google Chrome. There are other engines that exist, but Node.JS chose V8 for it's performance.

Sometimes when people think of Javascript, they think of the UI and running Javascript in a browser. The V8 engine that powers Chrome has no knowledge of the DOM and views the Javascript as code with objects and doesn't have any special knowledge around the DOM API. This works perfectly for Node.Js's needs which includes being a way to power server side applications.

Wrapping my head around the idea of Node.Js running Javascript without access to any of the DOM APIs took me a little while to understand. Part of my confusion may have come from the fact that once I had a Node.Js server running and hit the API endpoint in a browser, I didn't quite understand why I couldn't access the browser's local storage for example (which btw, I was hoping the local storage could be my hacky way of having a DB). If I used a tool like Postman from the beginning, maybe I wouldn't have made this wrong assumption.

Node.Js does have global objects that we take advantage of, just to juxtapose this next to the DOM's global objects like document or window. The best way to see that is with Node's command line environment, also known as REPL.

By typing the following command, we can run single lines of Javascript in a temporary session.

node // type this into a terminal/command line once node is installed

> global. //type this and after the dot, press tab
command line

By viewing what the global object contains, we notice quite a few objects and functions that are available in Node.Js

> global.
global.__proto__             global.hasOwnProperty        global.isPrototypeOf
global.propertyIsEnumerable  global.toLocaleString        global.toString
global.valueOf

global.constructor

global.AbortController       global.AbortSignal           global.AggregateError
global.Array                 global.ArrayBuffer           global.Atomics
global.BigInt                global.BigInt64Array         global.BigUint64Array
global.Boolean               global.Buffer                global.DOMException
global.DataView              global.Date                  global.Error
global.EvalError             global.Event                 global.EventTarget
global.FinalizationRegistry  global.Float32Array          global.Float64Array
global.Function              global.Infinity              global.Int16Array
global.Int32Array            global.Int8Array             global.Intl
global.JSON                  global.Map                   global.Math
global.MessageChannel        global.MessageEvent          global.MessagePort
... //Shortening this list - but run it for yourself to see it in its entirety!

Express

With it being 2022, it's hard to hear of Node.Js without Express being brought into the conversation. In fact, there's quite a few different frameworks alongside Express that sound new and cool like Remix, Gatsby, and a ton of others.

Express is a "fast, unopinionated, minimalist web framework" for Node.Js. As a framework, it wraps around Node.Js to allow setting up an API be easier, but doesn't obscure the core Node.Js features you might need.

Simple Node.Js Server

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Simple Node.Js with Express Server

const express = require('express')
const app = express() // We don't need to specify a hostname 
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!') // We don't need to set a status code or header
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

From comparing the 2 simple servers above, we can see how Express removes a few lines of code from a simple setup. We can get to specifying HTTP methods for our API faster.

A standout feature of Express is adding middleware aka using the method app.use().

Middleware in Express allows you to pass a request through a chain of functions and decide when to skip logic and go to the next function in the chain.

If you don't want to write your own middleware, you might find a third party package that has what you need, like in this example:  create user sessions with cookies .

Bonus: A Note on Typescript and Javascript Compilation Versions

To run the example above, I setup the following package.json and tsconfig.json (I'm a fan of Typescript so this includes the Typescript config)

{
  "name": "example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon src/index.ts", 
    "build": "tsc --project tsconfig.json",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.3"
  },
  "devDependencies": {
    "@types/express": "^4.17.13",
    "@types/node": "^17.0.21",
    "nodemon": "^2.0.15",
    "ts-node": "^10.7.0",
    "typescript": "^4.6.2"
  }
}
{
    "compilerOptions": {
      "target": "es6",
      "module": "commonjs",
      "rootDir": "./",
      "outDir": "./build",
      "esModuleInterop": true,
      "strict": true
    },
    "include": ["src"]
  }

I'm also calling out Typescript here because with a Node Express project, part of your setup may or may not need Babel/Webpack tools to build and compile. This has repeatedly been a pain point for myself - I constantly have to remind myself what these tools mean.

So I'd like to point out that Node.Js is able to run ES6 (or EcmaScript 2015). Node.Js does not need Babel. Babel is needed at times for front-end Javascript to be compiled to a version that is compatible with browsers, since that is the front-end Javascript's intention.

If you notice, it might seem odd that we can run pass our Typescript code "directly" to nodemon. This is a newer feature of nodemon in which we are able to quickly run our Typescript code without seemingly needing to compile it first.

Nodemon requires that you have the package ts-node installed so that it can run the nodemon process with the help of this package.

Conclusion

I hope that simplified how Node and Express work together, and how we can also add Typescript to a project and get that running quickly.

I wanted to start here and make these ideas a bit clearer, since the main topics I want to simplify next are Babel and Webpack, when and why we need them, and what compiling vs bundling really mean.

My ultimate goal with these findings is to create a minimal backend project setup since I'm trying to setup a production ready project for an API that I need. And since I'm using Javascript, I'm digging through tutorials and trying to scrape off the UI/front-end portion.

References

Introduction to Node.js
Getting started guide to Node.js, the server-side JavaScript runtime environment. Node.js is built on top of the Google Chrome V8 JavaScript engine, and it’s mainly used to create web servers - but it’s not limited to just that.
Express - Node.js web application framework