CRUD is a shorthand for create, read, update, and delete — the four basic operations every persistent data store must support. Coined in James Martin’s 1983 Managing the Data-base Environment, it remains the most durable acronym in backend work because it names the floor: whatever else your system does, records must come into being, be found, change, and go away.
Key takeaways
| Question | Answer |
|---|---|
| The four verbs | Create · Read · Update · Delete — the minimum of persistence |
| In SQL | INSERT · SELECT · UPDATE · DELETE |
| Over HTTP | POST · GET · PUT/PATCH · DELETE |
| vs. REST | CRUD is what you do to data; REST is how clients reach resources |
| The modern move | The CRUD layer is generated, not written |
CRUD in Four Languages
-- CRUD as SQL: the original mapping
INSERT INTO tasks (title) VALUES ('Write the launch post'); -- C
SELECT * FROM tasks WHERE done = false; -- R
UPDATE tasks SET done = true WHERE id = 42; -- U
DELETE FROM tasks WHERE id = 42; -- D
And the same cycle as an SDK expresses it — the form most application code actually writes:
// JavaScript / Node.js — Back4app JS SDK
// The full CRUD cycle on one object
const task = new Parse.Object('Task');
task.set('title', 'Write the launch post'); // C — create
await task.save();
const fetched = await new Parse.Query('Task')
.equalTo('done', false).first(); // R — read
fetched.set('done', true); // U — update
await fetched.save();
await fetched.destroy(); // D — delete // Flutter / Dart — Back4app Flutter SDK
// The full CRUD cycle on one object
final task = ParseObject('Task')..set('title', 'Write the launch post');
await task.save(); // C — create
final query = QueryBuilder<ParseObject>(ParseObject('Task'))
..whereEqualTo('done', false);
final fetched = (await query.query()).results!.first; // R — read
fetched.set('done', true);
await fetched.save(); // U — update
await fetched.delete(); // D — delete // iOS / Swift — Back4app Swift SDK
// The full CRUD cycle on one object
var task = Task()
task.title = "Write the launch post"
let saved = try await task.save() // C — create
let fetched = try await Task.query("done" == false)
.first() // R — read
var updated = fetched
updated.done = true
_ = try await updated.save() // U — update
try await updated.delete() // D — delete // Android / Kotlin — Back4app Android SDK
// The full CRUD cycle on one object
val task = ParseObject("Task").apply {
put("title", "Write the launch post")
}
task.save() // C — create
val fetched = ParseQuery.getQuery<ParseObject>("Task")
.whereEqualTo("done", false).first // R — read
fetched.put("done", true)
fetched.save() // U — update
fetched.delete() // D — delete One table to map them all
The six-column mapping no single reference assembles:
| Operation | SQL | HTTP verb | Status | Document DB | SDK/ORM idiom |
|---|---|---|---|---|---|
| Create | INSERT | POST | 201 | insertOne | object.save() (new) |
| Read | SELECT | GET | 200 | find / findOne | query.find() / .get() |
| Update | UPDATE | PUT / PATCH | 200 | updateOne | object.save() (dirty fields) |
| Delete | DELETE | DELETE | 204 | deleteOne | object.destroy() |
Two footnotes from the HTTP spec worth actually knowing: GET is safe (no state change), PUT and DELETE are idempotent (repeat without harm), POST is neither — which is why retry logic treats them differently, and why PUT means “replace whole” while PATCH means “modify part.”
Where CRUD happens
The diagram is also the security reminder: every verb crosses the API layer, which is where permissions and validation belong — a CRUD surface without per-operation access control is a public database with extra steps.
CRUD vs. REST
| Question | CRUD | REST |
|---|---|---|
| What it names | Operations on data | An architectural style for clients and resources |
| Defined by | Four verbs | Constraints: stateless, uniform interface, cacheable… |
| Lives where | SQL, SDKs, queues, anywhere | HTTP APIs |
| Relationship | The usual payload of REST endpoints | Often maps to CRUD — but can expose non-CRUD actions |
The practical takeaway: a REST API is frequently a CRUD API wearing HTTP — but “approve invoice” belongs in your API and isn’t a CRUD verb, and CRUD happily exists with no HTTP in sight. The terms cooperate; they don’t compete.
Common use cases
- The CRUD app proper. Admin panels, CMSs, CRMs, inventory, bookings — the majority of business software, honorably.
- Prototyping and MVPs. The four verbs on a handful of classes are the first version of most products.
- Auto-generated APIs. Schema in, CRUD surface out — the modern default that makes the four verbs configuration rather than code, covered fully in the auto-generated APIs entry.
- Admin and support tooling. Visual grids over the same CRUD surface, permissions included.
- The substrate under everything else. Event-sourced and workflow-heavy systems still expose CRUD somewhere — settings, profiles, reference data.
Should every action be CRUD? A decision matrix
| Model it as CRUD when… | Reach for more when… |
|---|---|
| The record’s current state is the truth | History is the domain → event sourcing |
| Edits are the user’s mental model | Reads and writes scale differently → CQRS |
| ”Delete” may really remove | Audit or undo demands soft deletes |
| Concurrent edits are rare | Lost updates loom → optimistic locking, versions |
| The action is “change this record” | The action is a business verb → name the workflow |
The last row is the design smell worth memorizing: when endpoint names drift toward updateStatus, the domain is asking for verbs of its own.
Limitations and trade-offs
- Update destroys history. In-place mutation is CRUD’s defining act and its defining loss — anything needing “what did this look like Tuesday” wants journaling or events.
- Delete is a policy, not a verb. Soft vs. hard delete trades auditability against privacy-regime erasure and query complexity; decide per class, on purpose.
- Concurrency is unpriced. Two updates, last writer wins, first writer’s change silently gone — version fields and conditional saves are the standard antidote.
- List is the fifth verb. Filtering, sorting, and pagination carry most real read traffic and most performance bugs — the reason CRUDL and BREAD exist.
- CRUD APIs can go anemic. A surface that’s only rows-in-rows-out pushes business logic to clients; keep the workflows server-side, next to the data.
CRUD on Back4app
Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Its relationship to this article is subtraction: define a class and the entire CRUD matrix above exists at once — REST endpoints per the HTTP column, GraphQL mutations, and the SDK idioms in the code tabs, with class-level permissions gating each verb and beforeSave triggers holding the validation. The four verbs stop being your codebase and become your vocabulary.
Frequently asked questions
What does CRUD stand for?
Create, Read, Update, Delete — the four fundamental operations of persistent storage, and the minimum any data-backed application must support. The acronym was popularized by James Martin in his 1983 book Managing the Data-base Environment, with related semantics formalized by Haim Kilov in 1990 — making it one of the oldest still-daily-used terms in backend vocabulary.
How does CRUD map to SQL and to HTTP?
Two clean mappings every developer memorizes once: in SQL, create is INSERT, read is SELECT, update is UPDATE, delete is DELETE. Over HTTP, create is POST, read is GET, update is PUT for full replacement or PATCH for partial changes, and delete is DELETE — with 201, 200, and 204 as the happy-path status codes respectively.
Is CRUD the same as REST?
No — they answer different questions. CRUD names what you do to data; REST is an architectural style for how clients interact with resources over HTTP, with constraints like statelessness and a uniform interface. REST endpoints often map neatly onto CRUD, but REST can expose non-CRUD actions, and CRUD exists happily outside HTTP — in SQL sessions, SDKs, and queues.
What is the difference between PUT and PATCH?
Scope of the update. PUT replaces the entire resource with the representation you send — idempotent by definition, since sending it twice yields the same state. PATCH applies a partial modification — only the fields you send change. Most real-world "update" traffic is PATCH-shaped, which is why SDK save() methods send only dirty fields.
What is a CRUD app?
An application whose core loop is creating, viewing, editing, and deleting records — admin panels, CMSs, CRMs, inventory and booking systems. It is a slightly derisive term that shouldn't be: the majority of business software is CRUD at heart, which is exactly why platforms that generate the CRUD layer automatically remove so much of the work.
What is a soft delete?
Marking a record as deleted — a flag or timestamp — instead of removing it. It preserves audit history, referential integrity, and undo, at the price of filtering every query and complicating unique constraints. The hard-delete counterpart is real removal, which privacy regimes like GDPR erasure requests can genuinely require. Most systems need a deliberate policy per class, not a default.
When is CRUD not enough?
When the domain is about events and history rather than current state. Update-in-place destroys the past — event sourcing keeps an append-only log and derives state from it; CQRS splits the read and write models entirely. And business actions like "approve invoice" or "checkout" are workflows, not row edits — modeling them as bare updates hides the domain. CRUD is the floor of data access, not the ceiling.
What are CRUD variants like CRUDL and BREAD?
Extensions and re-spellings of the same idea: CRUDL adds List as a distinct operation from single-record Read; BREAD spells it Browse, Read, Edit, Add, Delete. They acknowledge the practical truth that listing collections — with filtering and pagination — is its own operation with its own design decisions, not just "read, plural."