Cheat Sheet

SQL + Postgres

SQL + Postgres Basics

  • Fullstack - refers to the combination of frontend (client-side) and backend (server-side) technologies.

  • PERN - an acronym for a specific set of technologies used to build a fullstack web application: Postgres, Express, React, and Node. This acronym is useful when asked "what stack do you use?"

  • Database - a structured collection of data that is organized in a manner for easy retrieval.

  • Database Management System (DBMS) - a piece of software used to create and maintain a database.

  • Postgres - a popular "relational" database management system that stores data in a table-like manner

  • SQL (Structured Query Language) - a language used by relational database management systems to create, read, update, or delete data from a database.

One-To-Many Table

  • Schema Design - the process of planning the structure and relationships of the tables in a database.

  • Primary Key - a column in a table that uniquely identifies each row in the table.

  • Foreign Key - a column in a table that references the primary key of another table.

  • One-to-Many - a relationship between two tables in which instances in one table can be referenced by many instances in another table.

  • JOIN - A SQL statement that combines the columns of two tables.

people Table:

id
name

1

Ann Duong

2

Reuben Ogbonna

3

Carmen Salas

4

Ben Spector

pets Table:

id
name
type
owner_id

1

Khalo

dog

3

2

Juan Pablo

dog

2

3

Bora

bird

1

4

Frida

cat

3

5

Tora

dog

1

6

Pon Juablo

cat

2

7

Kora

dog

1

What are the names and ids of all the pets owned by Ann?

  • The order that you select FROM and JOIN does not matter

  • The ON clause indicates the relationship between the two tables (the pets.owner_id column references the people.id column)

  • The WHERE clause filters down the result.

  • When dealing with multiple tables, always specify the table that a value comes from.

Many To Many Table

  • Entity Relationship Diagram - a diagram that illustrates the relationships between tables.

  • Many-to-Many - a relationship between two tables in which the instances of each table can be referenced by many instances in the other table.

  • Association/Junction Table - a table used to create a many-to-many relationship using two foreign keys to reference two tables.

created using https://dbdiagram.io/

Q: Give me the names and ids of the customers that ordered product #2

pg

pg Basics

  • pg — the official Node.js client library for PostgreSQL. It lets JavaScript code send SQL queries to and receive results from a Postgres database.

  • Connection Pool — a cache of database connections reused across queries, rather than opening and closing a new connection for each query.

  • pool.query() — the primary method for sending a SQL query to the database. Returns a Promise that resolves to a result object.

  • Result Object — the object resolved by pool.query(). Its .rows property is an array of matching records, each as a plain JavaScript object.

  • Parameterized Query — a query where user-supplied values are passed separately from the SQL string using $1, $2, etc. placeholders, preventing SQL injection.

  • SQL Injection — a security attack where malicious SQL is embedded in user input and executed by the database. Parameterized queries prevent this.

  • .env file — a file that stores environment variables (like database credentials) that should not be committed to version control.

Setting Up pool.js

  1. Install pg and dotenv:

  2. Create a .env file in your project root:

    Never commit .env to GitHub. Add it to .gitignore.

  3. Create db/pool.js that sets up the pool once and exports it:

Running Queries with pool.query()

  • pool.query() returns a Promise — always await it inside an async function.

  • result.rows is an array of records. Each row is a plain JavaScript object.

  • result.rowCount is the number of rows returned or affected.

  • When no row matches, result.rows is [] — not an error.

Parameterized Queries

Never interpolate user input directly into a SQL string — it opens your app to SQL injection:

Use $1, $2, etc. placeholders and pass values in a separate array:

  • $1 maps to the first element of the values array, $2 to the second, and so on.

  • Postgres treats the values as data only, never as SQL commands — even if they contain SQL syntax.

CRUD Operations with pg

  • RETURNING * makes INSERT, UPDATE, and DELETE return the affected row(s). Without it, result.rows is empty.

  • Use result.rows[0] when you expect exactly one result (find, create, update, delete).

  • Use result.rows when you expect multiple results (list).

Async Model Methods

All pg queries are asynchronous. Every model method that calls pool.query() must be async and use await:

Model methods can wrap queries in try/catch to provide context on failure:

Async Controllers & Error Handling Middleware

Controllers that call async model methods must also be async. Use try/catch and pass errors to next(err):

Add an error handling middleware after all routes in index.js. Express identifies it by its four-parameter signature:

Authentication and Authorization

Authentication and Authorization Basics

  • Hashing - a mathematical algorithm that transforms a string of characters into a fixed-length string of characters.

  • Password Salt - A salt is a random string of data that is added to the input data before the hash function is applied. This changes the hash value that is produced, even for the same input data.

  • Salt Rounds - the number of times a password has been salted before being hashed

  • Plaintext password - the password as it was entered by the user, before it is hashed.

  • Bcrypt - a Node module that provides functions for hashing strings and verifying hashed strings.

  • Authentication - confirming who you are — verifying identity through login. Failure returns 401 Unauthorized.

  • Authorization - confirming what you're allowed to do — checking permissions after identity is established. Failure returns 403 Forbidden.

  • Session - a way for the server to persist information (like a logged-in user's ID) across multiple requests from the same client.

  • Cookie - a small piece of data set by a server and automatically sent by the browser with every subsequent request to that domain.

Bcrypt

The bcrypt module makes handling hashing, salting, and verification straightforward with two key methods:

  • bcrypt.hash(password, saltRounds)

  • bcrypt.compare(password, hashedPassword)

We can create these helper functions to gracefully handle errors that can occur when using these asynchronous operations.

And when users want to create a new account or login to their existing account, we can use these functions for authentication:

HTTP is stateless — the server has no memory of previous requests. Sessions solve this by storing data (like the logged-in user's ID) that persists across requests.

cookie-session stores session data directly in an encrypted, signed cookie sent to the browser.

Install and configure it before your routes:

Add SESSION_SECRET to your .env file:

Once the middleware is in place, req.session is available in every controller:

Auth Endpoints

A complete authentication system has four endpoints:

Method
Endpoint
What it does

POST

/api/register

Create a new user (hash password, store in DB)

POST

/api/login

Verify credentials, set session cookie

GET

/api/me

Return current user from session (or 401)

DELETE

/api/logout

Clear the session cookie

Login controller — find user, verify password, set session:

Use the same generic message for both "user not found" and "wrong password" — telling an attacker which one is true gives them information.

/api/me controller — return the current user from the session:

Logout controller — clear the session:

Authorization Middleware

checkAuthentication middleware protects routes that require login. It checks for a valid session and either continues the request or returns a 401:

Apply it to individual routes:

Or apply it to an entire router with router.use():

Ownership-Based Authorization

Being logged in is the first check. Some actions also require that the logged-in user owns the resource. Compare req.session.userId against the resource's owner field, returning 403 Forbidden if they don't match:

  • 401 Unauthorized — "I don't know who you are. Log in."

  • 403 Forbidden — "I know who you are, but you can't do this."

Last updated