📝
marcyannotes
  • Welcome
  • Student Guidelines & Policies
    • Student Handbook
    • AI Policy
    • Academic Calendar
  • Environment Setup
    • Local Environment Setup - Mac
    • Local Environment Setup - Windows
    • GitHub Setup
    • Postgres Setup
  • Fullstack Software Engineering Curriculum
    • Overview
    • How-Tos
      • How To Code at Marcy: Code Style Guide
      • How to Do Short Response and Coding Assignments
      • How to Debug
      • How to PEDAC
      • How to Create Projects with Vite
      • How to Deploy on GitHub Pages
      • How to Deploy on Render
    • Mod 0 - Command Line Interfaces, Git, and GitHub
      • Mod 0 Overview
      • Command Line Interfaces
      • Git & GitHub
      • Git Pulling & Merging
      • Git Branching & PRs
      • Pair Programming: BONUS
    • Mod 1 - JavaScriptFundamentals
      • Mod 1 Overview
      • Intro to Programming
      • Errors
      • Node & Node Modules
      • Variables, Functions & String Methods
      • Control Flow, typeof, and Math
      • Loops
      • Arrays
      • Objects
      • Higher Order Functions: Callbacks
      • Higher Order Functions: Array Methods
      • Regex
    • Mod 2 - HTML, CSS & the DOM
      • Mod 2 Overview
      • HTML
      • CSS
      • Accessibility (a11y)
      • The DOM
      • Events
      • Forms
      • The Box Model and Positioning
      • Flexbox
      • Grid & Media Queries
      • ESModules
      • LocalStorage
    • Mod 3 - Async & APIs
      • Mod 3 Overview
      • Promises
      • Fetch
      • Building a Fetching App
      • Async & Await
    • Mod 4 - Project Week!
      • Project Week Overview
    • Mod 5 - Object-Oriented Programming
      • Mod 5 Overview
      • Intro to OOP, Encapsulation, Factory Functions, and Closure
      • Classes
      • Private & Static
      • Has Many/Belongs To
      • Polymorphism
    • Mod 6 - Data Structures & Algorithms
      • Mod 6 Overview
      • Stacks & Queues
      • Nodes & Linked Lists
      • Singly & Doubly Linked Lists
      • Recursion
      • Trees
    • Mod 7 - React
      • Mod 7 Overview
      • Intro to React
      • Events, State, and Forms
      • Fetching with useEffect
      • Building a Flashcards App
      • React Context
      • Global Context Pattern
      • React Router
    • Mod 8 - Backend
      • Mod 8 Overview
      • Intro to Express
      • Building a Static Web Server with Middleware
      • Securing API Keys with Environment Variables
      • Building a RESTful API with MVC
      • SQL and Databases
      • JOIN (Association) SQL Queries
      • Knex
      • Your First Fullstack App!
      • Migrations & Seeds
      • Schema Design & Normalization
      • Hashing Passwords with Bcrypt
  • Code Challenge Curriculum
    • Unit 0
      • Lecture: Functions in JS
      • CC-00: Functions and Console Logs
      • CC-01: Conditionals
      • CC-02: Conditionals 2
    • Unit 1
      • CC-03: For Loops
      • CC-04: For Loops and Conditionals
      • CC-05: For Loops and Conditionals 2
    • Unit 2
      • CC-06: String Mutations
      • CC-07: Array Iteration
      • CC-08: String Mutation and Array Iteration
      • CC-09: Array Mutations
      • CC-10: Reading Objects
      • CC-11: Objects
      • CC-12: Objects
      • Unit 2 Diagnostic
    • Unit 3
      • Intro to PEDAC (and Algorithms)
      • validTime
      • fizzBuzz (array)
      • digitSumDifference
      • firstNotRepeating
      • compareEvenAndOddSum
      • countVowelConsonants
      • finalHP
      • canMakeTriangle
    • Unit 4
    • Unit 5
    • Unit 6
    • Unit 7
    • Unit 8
    • Sorting
Powered by GitBook
On this page
  • Terms
  • Controllers Review
  • Middleware
  • Serving Static Assets
  • Summary
  1. Fullstack Software Engineering Curriculum
  2. Mod 8 - Backend

Building a Static Web Server with Middleware

PreviousIntro to ExpressNextSecuring API Keys with Environment Variables

Last updated 8 months ago

Follow along with code examples !

In the last lecture, we created a server that could send back HTML in a single file. But when we build our React projects, they are more than just a single file. React projects typically have HTML, CSS, and JS, and potentially images or other files. In order to serve all of these static assets, we need Express Middleware.

Let's make a static web server!

Table of Contents:

Terms

  • Middleware - a function in express that intercepts and processes incoming HTTP requests. It can perform server-side actions such as parsing the request, modifying the response, or executing additional logic before passing control to the next middleware in the chain."

  • path module - a module for creating absolute paths to static assets

  • Environment Variable - a variable defined outside of the JavaScript execution context.

  • __dirname — an environment variable that returns the path to the parent directory of the current file.

  • Static Assets - unchanging files delivered to the client exactly as they are stored on a server. These include HTML, CSS, JavaScript files, images, videos, fonts, and documents. For React projects, we need to "build" our project to generate static assets (convert .jsx files to .js files).

Controllers Review

Remember how the Express app works?

  1. A client sends a request to the server.

  2. The server receives the request and routes it to the proper controller based on the specific endpoint.

  3. The controller processes the request, interacts with any necessary data or services, and generates a response.

  4. The server sends the response back to the client.

  5. The client receives the response and renders the data or take further actions based on it.

// controller
const serveHello = (req, res, next) => {
  const name = req.query.name || "stranger"
  res.send(`hello ${name}`);
}

// endpoint
app.get('/api/hello', serveHello);

// A GET request to /api/hello?name=ben will send the response "hello ben"
  • A controller is a callback function that parses a request and sends a response. It will be invoked by the Express app when the associated endpoint is sent a request.

    • The controller receives a req object from the Express app which holds data about the request, including query parameters.

    • It also receives a res object which has methods to send a response.

What about next?

Middleware

A "middleware" is a type of controller that can also parse requests and perform server-side actions.

Unlike normal controllers, middleware functions pass the request to the next function in the chain without sending a response to the client. They sit in the "middle" of the chain of middleware/controllers.

For example, this middleware prints out information about the incoming request in a nice format:

// Middleware function for logging route requests
const logRoutes = (req, res, next) => {
  const time = new Date().toLocaleString();
  console.log(`${req.method}: ${req.originalUrl} - ${time}`);
  next(); // Passes the request to the next middleware/controller
};

// Controller function for serving a hello message
const serveHello = (req, res, next) => {
  const name = req.query.name || "stranger";
  res.send(`Hello, ${name}!`);
};

// Register the logRoutes middleware globally to log all requests
app.use(logRoutes);

// Register the serveHello controller for the /api/hello route
app.get('/api/hello', serveHello);
  • app.use is like app.get but for middleware

  • When app.use is invoked with just the middleware function, it executes that middleware for ALL routes

  • Notice that the logRoutes middleware controller doesn't use the res object at all and then invokes next() to pass control to the next controller in the chain.

Our diagram now looks like this:

Q: So, if a user sends a request to http://localhost:8080/api/hello, which controllers are invoked and in what order?

First the logRoutes middleware is invoked. The next() function is called which passes the request to the next controller, serveHello.

Q: What would happen if the logRoutes controller DID send a response to the client? What would happen if it didn't invoke next()?

If logRoutes did invoke res.send(), the serveHello controller would NOT be invoked as a response has already been sent. If we simply didn't invoke next(), our server would "hang" — the response would never be completed and the client would likely receive a timeout error because the request took too long.

Middleware can be custom-made like this logRoutes. However, we can also utilize some of the out-of-the-box middleware controllers provided by Express.

Serving Static Assets

One of the most important roles of a full stack web server is to provide a client with a frontend. Whenever you visit a website, that's what happens — go to https://google.com and the Google server sends back HTML, CSS, and JavaScript to render the user interface.

That's what static web servers like GitHub Pages do — they store static assets (HTML, CSS, and JS files) and then provide a URL where users can access them.

With Express, it is really quick to build your own static web server using the express.static(filepath) middleware function. You only need 4 lines!

Suppose we had a React project in a directory called frontend and it has its static assets built into a directory called dist:

// Import the path module to construct the absolute path to the static assets folder
const path = require('path');

// Construct the absolute path to the static assets folder using the `path.join()` method
// Use '../' to navigate to a parent directory, similar to when you are using `cd`.
const pathToDistFolder = path.join(__dirname, '../../path/to/frontend/dist');

// Create the middleware function for serving static assets
const serveStatic = express.static(pathToDistFolder);

// Use the middleware function to serve static assets
app.use(serveStatic);

Explanation:

  • The path.join() method constructs an absolute file path to the static assets folder, ensuring compatibility across different operating systems.

  • __dirname provides the absolute path of the current module's parent directory.

  • The express.static() middleware function makes static assets (such as HTML, CSS, and JS files) from the specified directory publicly available.

  • The middleware function serveStatic is used with app.use() to enable serving static assets to clients.

Now, if you run the server and visit the http://host:port/index.html, the server will send you the index.html file! (You can also just visit http://host:port/ and it will automatically send you the index file).

Any other files in the provided folder can also be accessed on your server. Assuming the /dist directory contains 2 images, foo.jpg and bar.jpg then you can simply access them at:

http://host:port/foo.jpg
http://host:port/bar.jpg

Summary

  • Controllers: Callback functions that handle requests by parsing them and sending responses.

  • Middleware Functions: Functions similar to controllers but pass requests to the next middleware without sending a response. They can also be executed for all requests while controllers typically handle a single endpoint.

  • Static Assets: Unchanging files (e.g., HTML, CSS, JS) served by a web server. For React projects, we need to "build" the project to convert "dynamic" .jsx files to "static" .js files

  • Serving Static Assets:

    1. Construct an absolute file path to the static assets folder using path.join() and __dirname.

    2. Use express.static(filepath) middleware to make static assets publicly available.

    3. Register the middleware with app.use()

here
Terms
Controllers Review
Middleware
Serving Static Assets
Summary