What Is CRUD? The Four Operations Behind Almost Every App
Create, Read, Update, Delete: most app features are some combination of these four. What CRUD means, how it maps to SQL and HTTP methods, what a CRUD API looks like, and the details — validation, permissions, pagination — that separate a demo from a real app.
Look at almost any app — a to-do list, a CRM, an online shop, a blog — and most of what it does is four things: create something, read it, update it, delete it. Developers call this CRUD, and once you see it, you'll see it everywhere.
The four operations
| Letter | Operation | Example in a to-do app |
|---|---|---|
| C | Create | Add a new task |
| R | Read | See your list; open one task |
| U | Update | Rename a task; mark it done |
| D | Delete | Remove a task |
A "CRUD app" is one that's mostly these four operations on a few kinds of data. That's not an insult — it describes most useful software.
How CRUD maps to databases and APIs
The same four operations appear at every layer of an app:
| CRUD | SQL | HTTP method | Example API route |
|---|---|---|---|
| Create | INSERT |
POST |
POST /api/tasks |
| Read | SELECT |
GET |
GET /api/tasks, GET /api/tasks/42 |
| Update | UPDATE |
PUT / PATCH |
PATCH /api/tasks/42 |
| Delete | DELETE |
DELETE |
DELETE /api/tasks/42 |
When you click "Save" on an edited task, the browser sends a PATCH to the API, which runs an UPDATE on the database. Same idea, three layers. (What Is an API? and SQL for Beginners.)
PUT traditionally means "replace the whole thing," and PATCH means "change these fields." Many apps just use one; what matters is consistency. (Designing a REST API.)
What a simple CRUD API looks like
In an Express server:
app.get("/api/tasks", async (req, res) => {
const tasks = await db.task.findMany({ where: { userId: req.user.id } })
res.json(tasks)
})
app.post("/api/tasks", async (req, res) => {
const task = await db.task.create({
data: { title: req.body.title, userId: req.user.id },
})
res.status(201).json(task)
})
app.patch("/api/tasks/:id", async (req, res) => { /* … */ })
app.delete("/api/tasks/:id", async (req, res) => { /* … */ })
AI tools generate code like this very quickly. Getting it working is easy. The details below are what make it correct.
What makes CRUD harder than it looks
Permissions on every operation
The most important detail and the most commonly missed. It's not enough that the user is logged in — each operation must check that this user may touch this row.
Notice where: { userId: req.user.id } above. Without it, GET /api/tasks returns everyone's tasks. And for update and delete:
// ❌ Anyone logged in can delete any task by changing the ID
await db.task.delete({ where: { id: req.params.id } })
// ✅ Only the owner's task matches
await db.task.deleteMany({ where: { id: req.params.id, userId: req.user.id } })
(Authentication vs Authorization.)
Validation
Never trust what the browser sends. Check that required fields exist, text isn't absurdly long, numbers are in range, and users can't set fields they shouldn't — like isAdmin or price. (Form Validation Explained.)
Reading lots of data
"Read" often means "read a list." A list that returns every row works with 20 tasks and fails with 200,000. Real lists need pagination — returning a page at a time — and usually sorting and filtering. (API Pagination.)
Deleting safely
Does deleting a user delete their tasks? Their invoices? Decide deliberately. Many apps use soft deletes — marking rows as deleted rather than removing them — so mistakes can be undone and history is kept. (Soft Deletes and Audit Logs.)
Concurrent updates
Two people edit the same record at once; the second save silently overwrites the first. For collaborative apps, you need a way to detect it — like checking an updated_at value hasn't changed since the edit began.
Returning the right status codes
201 after create, 404 when the thing doesn't exist (or isn't theirs), 400 for bad input. (HTTP Status Codes Explained.)
A CRUD checklist
For each kind of data in your app:
- Create, read, update, and delete each check the user owns (or may access) the row
- Input is validated on the server
- Users can't set protected fields
- Lists are paginated
- Deleting is deliberate: cascade, block, or soft delete
- Errors return sensible status codes
It's a useful checklist to hand to your AI tool, too: "Review every CRUD endpoint against this list."
EasySpawn gives Claude Code a real backend and managed Postgres in one workspace, so your CRUD endpoints can be tested against actual data and actual permission checks before launch. See how it works or join the waitlist.
Related: What Is an API? · Design Your First Database · GraphQL vs REST
Keep reading
What Is a Database? A Beginner's Guide for People Building Apps
Almost every app needs somewhere to remember things — users, orders, messages. That's a database. What databases are, how tables and rows work, the difference between SQL and NoSQL, and the three things every beginner should know before real users arrive.
Supabase vs Firebase: Which Backend for Your First App?
Lovable and Bolt lean on Supabase; many tutorials use Firebase. How the two backends compare — database model, auth, security rules, real-time, pricing shape, and lock-in — and which one fits the app you're building.