Skip to content

Instantly share code, notes, and snippets.

@AlexMercedCoder
Last active April 19, 2022 13:25
Show Gist options
  • Save AlexMercedCoder/2e48ba5a2bf2f0aeb341c1ce29457844 to your computer and use it in GitHub Desktop.
Save AlexMercedCoder/2e48ba5a2bf2f0aeb341c1ce29457844 to your computer and use it in GitHub Desktop.
Blog Drafts
title date description author category bannerImage tags
ExpressJS Cheatsheet 2022
2022-04-09
Easy reference for using express
Alex Merced
javascript
/images/postbanner/2022/batch-streaming.png
backend
javascript
node

This article assumes basic knowledge of ExpressJS, if your new to Express I recommend starting with the following Video Playlist:

Creating a New Project

Assuming you have NodeJS installed just open up your IDE to an empty folder and do the following in terminal:

  • Create a new node project npm init -y

  • install dependencies npm install express morgan dotenv

  • if you don't have nodemon installed globally, do so npm install -g nodemon

  • create your initial server file, gitignore and env file touch server.js .env .gitignore

  • put the following in the .gitignore file

.env
/node_modules
  • put the following in the .env file, this file is for defining variables you don't want in your code and not in public view (api keys, database credentials, sensitive stuff) or variables that should change depending on the context.
PORT=5000

package.json

Could of useful things we can do in the package.json:

  • We can add scripts, you can add as many as you like but here is basics I'd recommend.
"scripts: {
    "start": "node server.js",
    "dev": "nodemon server.js"
}

scripts can be run using the commands npm run <command name> for example we can run the dev script with npm run dev.

  • We can also choose whether we will use commonjs or module syntax, commonjs will be the default if we do nothing, but if we add "type":"module" to the package.json then we can use module syntax. This effects how we import and export things.
Task CommonJS ES Module
Importing Dependency/File const something = require("something") import something from "something"
Exporting from a file module.exports = something export default something

Which you prefer is your own preference, just be aware you may not be able to import JSON files with ES Modules in older versions (or use ES Modules at all in really old versions) of node and use an experimental flag to do so in newer versions.

Basic Server Setup

CommonJS Version

// bring in environment variables from a .env file
require("dotenv").config()

// import express and morgan
const express = require("express")
const express = require("morgan")

// create an application object
const app = express()

// define a PORT variable from the environment with a default value
const PORT = process.env.PORT || 4000

/////////////////////////////////////
// ALL YOUR MIDDLEWARE AND ROUTES GO HERE
app.use(morgan("tiny")) // middleware for logging
app.use(express.urlencoded({extended: true})) //middleware for parsing urlencoded data
app.use(express.json()) // middleware for parsing incoming json
app.use("/static", express.static("static")) // to set a folder for static file serving
/////////////////////////////////////

// Server Listener
app.listen(PORT, () => console.log(`Listening on port ${PORT}`))

ES Module Version

// Bring in environmental variables
import dotenv from "dotenv"
dotenv.config()

// import express and morgan
import express from "express"
import morgan from "morgan"

// create an application object
const app = express()

// define a PORT variable from the environment with a default value
const PORT = process.env.PORT || 4000

/////////////////////////////////////
// ALL YOUR MIDDLEWARE AND ROUTES GO HERE
app.use(morgan("tiny")) // middleware for logging
app.use(express.urlencoded({extended: true})) //middleware for parsing urlencoded data
app.use(express.json()) // middleware for parsing incoming json
app.use("/static", express.static("static")) // to set a folder for static file serving
/////////////////////////////////////

// Server Listener
app.listen(PORT, () => console.log(`Listening on port ${PORT}`))

Middleware

Middleware are just functions that receive three arguments:

  • req the request object, more on this later
  • res the response object, more on this later
  • next a function that passes the req/res objects to the next middleware or route.

Here is an example of the simplest middleware

const middlewareFunction = (req, res, next) => {
 console.log("This is middleware")
}

The middleware functions can be registered using the .use method of the application object or routers.

// using the middleware on all requests
app.use(middlewareFunction)
// using the middleware on certain urls
app.use("/endpoint", middlewareFunction)

Other popular middleware not in the previous code snippets include:

And many others...

Routes

Routes determine what is the servers response to in an incoming request. A route is created by using one of the following methods on the application object or a router:

  • .all for requests of any method
  • .get for GET requests
  • .post for POST requests
  • .put for PUT requests
  • .delete for DELETE requests

All of these functions take two arguments:

  • the endpoint
  • a "action", "Controller" or "Route Handler" function that takes req and res as arguments

Here is an example:

// writing pass an anonymous function
app.get("/endpoint", (req, res) =>  {
  res.send("The Response")
})

// using a named function
function routeHandler(req, res){
  res.send("the response")
}
app.get("/endpoint", routeHandler)

The Request Object (res)

The request object represents the data from the incoming request and is passed to all middleware and route handlers.

-req.headers object with the headers of the incoming request -req.params object with any route params -req.query object with any key/values from a url query string -req.body object key/values of the request body (parsed by the express.urlencoded or express.json middleware) -req.method the method of the request as string

plus much more

The Response Object (res)

The response object is an object that is used to help author the response to the request. Primarily made up of helper functions for different types of responses.

-res.send will send a text, html or json request depending on what is passed to it -res.json send a javascript object or array as a json response -res.renderrenders an html response from a template

Rendering Templates

Templates allow you to generate html responses dynamically, there are several templating engines that can be used, here are few articles to see how to use them.

To render a template we use the res.render function which takes two arguments:

  • the name of the the file to locate in the views folder
  • javascript object with data that can be used in the rendering of the template (each templating language should have its own way of using the data from the object in the template)

Router Objects

You can group routes together using routers which can be used for organization and to apply middleware to a specific group of routes.

Creating a Router

// create the router object
const router = express.Router()
// register it with the application for routes with a certain prefix
app.use("/prefex", router)

Just like the application object you can register middlewares routes with the router

// router specific middleware
router.use(middlewareFunction)

// registering routes
router.get("/endpoint", routerHandler) // url is /prefix/endpoint

Connecting to Databases

The following libraries can help you connect to different databases.

Making API Calls

Keep in mind you can't use fetch natively in node, and jQuery is only a frontend library. But you have some options.

  • node-fetch A library that replicates the browsers fetch function
  • Axios A library for making API Calls
  • GOT a library for making api calls
  • needle another http client library

What is Mongoose?

Mongoose is an ODM (Object Document Manager) that like relational ORMs (Object Relationship Managers) maps the data in our data base to objects for more familiar programming patterns. Mongoose specifically is a MongoDB ODM for Javascript applications, and this article aims to be a resources for working with MongoDB in JS using Mongoose.

  • Install mongoose npm install mongoose
  • We will share many examples in the context of ExpressJS application although the logic and flow should be the same with any other Node web framework like Koa or Fastify.

[MongoDB Video Playlist](Working with MongoDB)

The Mongoose URI

URI stands for Universal Resource Identifier, a string that allows applications to send messages to other applications. We use HTTP URIs all the time in the form of the URLs to our favorite website. The pattern is as follows:

protocol://username:password@host:port/resource?query=string&key=value

  • protocol:// the type of message being sent such as https://, mongodb://, postgresql://

  • username:password@ the username and password if one is needed for the target, typically needed for databases since most web pages are open to the public.

  • host this would be the domain and subdomains with it that are an alias for an IP address of server that hosts the destination application (localhost for example is an alias for 127.0.0.1 which is the machine your currently using.

  • port every server can receive message on different ports numbered up to 65535 (the highest unsigned 16-bit integer). You generally don't type a port for URLs because browsers know http traffic goes to port 80 and https traffic goes to port 443. Most databases have a default port they run on mongodb -> 27017 | postgresql -> 5432 | mysql -> 3306

  • /resource tell the receiving application what resources to access at the destination. For web applications this usually a particular web page, file, or JSON data. For database application this usually refers to the particular database being accessed.

  • ?query=string&key=value this is the query string which can be used to pass additional info like data from a form or database configurations.

A MongoDB URI for a mongo server running on my pc would be:

mongodb://username:[email protected]:27017/database_name

Read here for options that can be passed in the query string

The mongodb uri should NEVER be hardcoded in your application but delivered via environmental variables or via some file that is included in your .gitignore so you don't export your URL in any remote public get repositories

Importing Mongoose

By default node using commonJS so importing a library would be done using require. const mongoose = require("mongoose")

If you add "type":"module" to your package.json then your project will be treated like an ES Module and you can then do the following instead. import mongoose from "mongoose"

Establising a Connection

For our purposes we will assume the mongo uri is stored in a variable called DATABASE_URL it's up to you to make sure this variable exists and holds the URI string. The options variable we are assuming holds a javascript object with any database configuration which can see listed here.

//establish the connection
mongoose.connect(DATABASE_URL, OPTIONS)

// saving the connection in a variable
const cxn = mongoose.connection

//creating messages when connection changes state
cxn
.on("open", () => console.log("mongoose is connected"))
.on("close", () => console.log("mongoose is disconnected"))
.on("error", (error) => console.log(error))

Models

Models all us save in and retrieve from our mongoose database. To create a model we must first create a schema which defines the shape of the data in the database (the individual properties and their data types). Defining a basic model can be done like so:

// destructure model function and Schema constructor from mongoose
const {model, Schema} = mongoose

// create a schema
const UserSchema = new Schema({
username: {type: String, unique: true, required: true},
password: {type: String, required: true},
age: Number,
email: String
}, {timestamps: true})

// create a model
const User = model("User", UserSchema)

details on the different schema data types and options

details on working with models

Using Model Methods

Model objects have many methods for creating, deleting, updating and finding data in the database. Methods can be used in three different ways. We will write these in the context of an express route but would be pretty much the same in any node web framework.

Methods you models have

// callback syntax
app.get("/users", (req, res) => {
  // use the find method to find all users with the specified username
  User.find({username: req.query.username}, (error, results) => {
    // check if there is an error
    if(error){
      res.status(400).json({error})
      return false
    }
    // send results as json
    res.json(results)
  })
})

// .then syntax
app.get("/users", (req, res) => {
  // use the find method and catch errors
  User.find({username: req.query.username})
  .then((results) => res.json(results))
  .catch((error) => res.status(400).json({error})
})

// async/await syntax
app.get("/users", async (req, res) => {
  // use the find method, catch errors
  const results = await User.find({username: req.query.username}).catch((error) => res.status(400).json({error})
  // send results as response
  res.json(results)
})

Handling Related Documents

MongoDB is a document database so it is optimal for handling unrelated data, but mongoose does build in several tools to make implementing related data a lot easier.

First Step: The ObjectID Type

To express a relationship we specify a related field as an ObjectID type meaning it expects to strings that represent object ID of data documents in a specified collection.

// destructure model function and Schema constructor from mongoose
const {model, Schema} = mongoose

// create a schema
const UserSchema = new Schema({
username: {type: String, unique: true, required: true},
password: {type: String, required: true},
age: Number,
email: String,
posts: [{type: mongoose.Types.ObjectId, ref: "Post"]
}, {timestamps: true})

const PostSchema = new Schema({
title: String,
body: String,
author: {type: mongoose.types.ObjectId, ref: "User"}
}, {timestamps: true})

// create a model
const User = model("User", UserSchema)
const Post = model("Post", PostSchema)

Second Step: Associate Records with Each Other

// grab two existing documents from both collection

app.get("/createPost/:userId", async (req, res) => {
  const post = await Post.create(req.body) // create new post
  const user = await User.findById(req.params.userId) // get existing user
  
  // associate post with user
  post.author = user
  post.save()
  
  // associate user with post
  user.posts.push(post)
  user.save()
  
  // send back post as response with author populated
  res.json(await post.populate("author"))

})

The populate method allows us to take a field marked as related to another document (the ObjectID type) and fill in the data from that related document that you don't have to manually do another query to get the additional data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment