SQL for Web Developers: Database Basics You Can't Skip
β‘ Quick Answer
SQL tutorial for web developers β learn SELECT, INSERT, UPDATE, DELETE, JOINs, indexes, and how databases fit into full-stack web applications with real examples.
Get more content like this on Telegram!
Daily AI tips, notes & resources β free
Advertisement
SQL for Web Developers: Database Basics You Can't Skip
SQL (Structured Query Language) is how you ask a database for data β select rows, filter them, join them across tables, and write them back. Skip it and you end up reinventing filtering and sorting in JavaScript, badly.
That's exactly what happened to me. I stored everything in JSON files and local storage for a year, until I needed to search, sort, and relate data. One query that takes three lines of SQL took 80 lines of JavaScript gymnastics β and the JavaScript version was slower and broke under concurrent access.
A database is a filing cabinet with a very fast, very literal clerk: tell it precisely what you want and it hands back exactly that, no matter how many millions of files are inside. Application code that tries to do the clerk's job by loading everything into memory first will always lose.
This guide covers the SQL you'll actually use as a web developer: selecting and filtering data, inserting and updating records, joining related tables, and structuring a real schema.
What Is a Relational Database?
A relational database stores data in tables β rows and columns, like a structured spreadsheet where every sheet can reference rows on another sheet. Each table has a defined schema (column names and types), and tables relate to each other through foreign keys.
| Database | Best for | Notes |
|---|---|---|
| PostgreSQL | Most web apps | Free, open source, scales from side project to millions of users |
| SQLite | Development, small apps | File-based, zero setup, not built for concurrent writes at scale |
| MySQL | Legacy/managed stacks | Widely hosted, slightly fewer advanced features than PostgreSQL |
For a new project in 2026, PostgreSQL is the default choice unless you have a specific reason to pick something else.
Core SQL: SELECT
SELECT is the read command β it pulls rows out of a table, the same way WHERE in a spreadsheet filter narrows down which rows you see:
-- All columns from the users table
SELECT * FROM users;
-- Specific columns
SELECT name, email FROM users;
-- With filtering
SELECT name, email FROM users
WHERE active = true;
-- Multiple conditions
SELECT * FROM posts
WHERE published = true
AND created_at > '2025-01-01';
-- With ordering
SELECT * FROM posts
ORDER BY created_at DESC
LIMIT 10; -- First 10 results
-- Pattern matching
SELECT * FROM users
WHERE email LIKE '%@gmail.com'; -- All Gmail usersAggregate Functions
-- Count all users
SELECT COUNT(*) FROM users;
-- Count active users
SELECT COUNT(*) FROM users WHERE active = true;
-- Average, min, max
SELECT AVG(price), MIN(price), MAX(price) FROM products;
-- Sum
SELECT SUM(amount) FROM orders WHERE status = 'completed';
-- Group by
SELECT category, COUNT(*) as product_count
FROM products
GROUP BY category
ORDER BY product_count DESC;INSERT, UPDATE, DELETE
INSERT β Add new rows
-- Insert one row
INSERT INTO users (name, email, created_at)
VALUES ('Alice Johnson', 'alice@example.com', NOW());
-- Insert and get the created record back (PostgreSQL)
INSERT INTO posts (title, body, user_id, published)
VALUES ('My First Post', 'Hello world!', 42, true)
RETURNING id, created_at;
-- Insert multiple rows
INSERT INTO tags (name)
VALUES ('javascript'), ('react'), ('css');UPDATE β Modify existing rows
-- Update one user's email
UPDATE users
SET email = 'alice.new@example.com'
WHERE id = 42;
-- Update multiple columns
UPDATE posts
SET published = true,
published_at = NOW()
WHERE id = 7;
-- Update with a WHERE clause β always include this!
-- Omitting WHERE updates EVERY row in the table
UPDATE users SET active = false; -- DANGEROUS β deactivates everyoneDELETE β Remove rows
-- Delete one user
DELETE FROM users WHERE id = 42;
-- Delete old data
DELETE FROM sessions
WHERE expires_at < NOW();
-- Delete with RETURNING (PostgreSQL)
DELETE FROM notifications
WHERE user_id = 42 AND read = true
RETURNING id;JOINs: Relating Tables
A JOIN stitches rows from two tables together based on a shared column β like matching invoice line items to customer names by customer ID. Real applications have related tables: a posts table has a user_id that references the users table, and a JOIN combines them into one result.
Schema Example
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
body TEXT,
user_id INTEGER REFERENCES users(id), -- foreign key
published BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
body TEXT NOT NULL,
post_id INTEGER REFERENCES posts(id),
user_id INTEGER REFERENCES users(id)
);INNER JOIN
Returns rows that match in both tables:
-- Get posts with author name
SELECT
posts.title,
posts.created_at,
users.name AS author_name
FROM posts
INNER JOIN users ON posts.user_id = users.id
WHERE posts.published = true
ORDER BY posts.created_at DESC;Result includes only posts that have a matching user.
LEFT JOIN
Returns all rows from the left table, with NULLs for the right table where no match:
-- All users, including those with no posts
SELECT
users.name,
COUNT(posts.id) AS post_count
FROM users
LEFT JOIN posts ON users.id = posts.user_id
GROUP BY users.id, users.name
ORDER BY post_count DESC;Multiple JOINs
-- Comments with post title and author name
SELECT
comments.body,
posts.title AS post_title,
u_comment.name AS commenter,
u_post.name AS post_author
FROM comments
INNER JOIN posts ON comments.post_id = posts.id
INNER JOIN users u_comment ON comments.user_id = u_comment.id
INNER JOIN users u_post ON posts.user_id = u_post.id;Indexes: Making Queries Fast
An index is a sorted lookup structure on a column β the same idea as a book's index letting you jump straight to a page instead of reading cover to cover. Without one, every query scans every row; on a table with 1 million rows, that's slow:
-- Without index: scans 1,000,000 rows
SELECT * FROM users WHERE email = 'alice@example.com';
-- With index: finds the row in microseconds
CREATE INDEX idx_users_email ON users (email);When to Add Indexes
-- Primary keys are indexed automatically
CREATE TABLE users (id SERIAL PRIMARY KEY); -- id is already indexed
-- Add index on columns used in WHERE clauses
CREATE INDEX idx_posts_user_id ON posts (user_id);
CREATE INDEX idx_posts_published ON posts (published) WHERE published = true;
-- Add index on columns used in ORDER BY
CREATE INDEX idx_posts_created_at ON posts (created_at DESC);
-- Composite index for common query patterns
CREATE INDEX idx_posts_user_published ON posts (user_id, published);Indexes make reads fast but add overhead to writes. Don't index every column β only columns your queries actually filter on.
Real Web Application Patterns
Pagination
-- Page 1 (items 1-20)
SELECT * FROM posts
WHERE published = true
ORDER BY created_at DESC
LIMIT 20 OFFSET 0;
-- Page 2 (items 21-40)
SELECT * FROM posts
WHERE published = true
ORDER BY created_at DESC
LIMIT 20 OFFSET 20;
-- Cursor-based pagination (better for large tables)
SELECT * FROM posts
WHERE published = true
AND created_at < '2025-05-01T00:00:00Z' -- cursor
ORDER BY created_at DESC
LIMIT 20;Search
-- Basic text search
SELECT * FROM posts
WHERE title ILIKE '%javascript%' -- case-insensitive
OR body ILIKE '%javascript%';
-- Full-text search (PostgreSQL)
SELECT * FROM posts
WHERE to_tsvector('english', title || ' ' || body)
@@ to_tsquery('english', 'javascript & react');Using SQL with Node.js (pg)
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
// Safe parameterized query β prevents SQL injection
async function getUserByEmail(email) {
const result = await pool.query(
'SELECT id, name, email FROM users WHERE email = $1',
[email] // $1 is replaced safely β never use string interpolation
);
return result.rows[0] ?? null;
}
async function createPost(title, body, userId) {
const result = await pool.query(
`INSERT INTO posts (title, body, user_id)
VALUES ($1, $2, $3)
RETURNING id, created_at`,
[title, body, userId]
);
return result.rows[0];
}Always use parameterized queries β never interpolate user input into SQL strings. SQL injection is one of the most common security vulnerabilities. For a broader look at web security, see our HTTP vs HTTPS guide.
For learning where databases fit in the full web application stack, see our web developer roadmap and the API tutorial that covers how APIs expose database data to the frontend.
Further Reading
- How the Internet Works: A Simple Guide for Curious Beginners
- Docker for Beginners: Containers Explained Without the Jargon
- CSS Grid vs Flexbox: When to Use Which Layout Method
- Git and GitHub Complete Guide for Beginners β 2026 Edition
- The Web Developer's Guide to Chrome DevTools (Hidden Features)
- Async Python: Why Your Programs Are Slow and How to Fix Them
- React Native vs Flutter: Building Mobile Apps in 2025
- How to Deploy a React App to Vercel in 10 Minutes
Advertisement
π¬ DiscussionPowered by GitHub Discussions
Frequently Asked Questions
Problem Solver and Cloud Expert
Solves complex infrastructure challenges and architects reliable, scalable cloud deployments for every project. Shamshur Rahman keeps AiTechWorldsβ hosting and cloud infrastructure fast, resilient, and cost-efficient.
Not sure yet? Ask AI about this article
Get an instant, unbiased AI summary of βSQL for Web Developers: Database Basics You Can't Skipβ.
Advertisement
Related Articles
Understanding APIs: A Beginner's Story About How Apps Talk
API tutorial for beginners β understand what APIs are, how REST APIs work, HTTP methods, JSON, authentication, and how to call APIs in JavaScript with real examples.
The Web Developer's Guide to Chrome DevTools (Hidden Features)
Chrome DevTools guide for web developers β master the Elements panel, Network tab, Console, Performance profiler, and hidden features most developers never use.
CSS Grid vs Flexbox: When to Use Which Layout Method
CSS Grid vs Flexbox explained clearly β understand the difference, when each layout method excels, and how to choose the right one for every UI pattern.
Docker for Beginners: Containers Explained Without the Jargon
Docker tutorial for beginners β learn containers vs VMs, Docker images, Dockerfiles, docker-compose, and how to containerize a real web application step by step.