The CRUD (Create, Read, Update, Delete) pattern is often the first interaction style developers learn.
While simple and powerful, CRUD is frequently misapplied to domains that require more expressiveness (e.g., workflows, state transitions, business actions).
This section clarifies when CRUD is appropriate and how to design consistent, resource-oriented CRUD APIs aligned with governance expectations.
4.1. Overview
CRUD is a pattern for managing simple, data-centric resources where the primary interactions are creating new records, retrieving them, modifying them, and deleting them.
Key characteristics:
- Entity-focused: CRUD works best when modeling information that behaves like stored records.
- State-agnostic: CRUD implies no formal lifecycle beyond basic creation and modification.
- Uniform semantics: CRUD uses standard HTTP verbs consistently across resources.
- Stable structure: CRUD endpoints rarely change unless the resource schema evolves.
CRUD excels in systems where operations have minimal business logic and primarily manipulate data.
4.2. When to Use CRUD
Use CRUD when the resource represents a simple, stable entity and the domain’s interactions can be expressed through basic data modifications.
Use cases include:
- Reference data (countries, currencies, categories, settings)
- Basic records (contacts, profiles, accounts, stores, locations)
- Simple configuration objects
- Resources with no notable workflow or state transitions
- Domain objects whose rules are enforced via validation, not workflow
CRUD is ideal when:
- The consumer expects predictable, consistent operations.
- The resource is primarily a bag of attributes with simple validation.
- Side-effects are minimal or non-existent.
- Multiple clients can safely perform basic operations without domain conflicts.
4.3. When NOT to Use CRUD
Avoid CRUD when operations imply business intent, workflow steps, or state transitions.
Signs CRUD is the wrong pattern:
- “PATCH /order/123” actually means “ship order.”
- “PATCH /loan/456” actually triggers a risk review.
- “DELETE /user/789” actually disables an account with downstream effects.
- Resource fields are used as a “command transport system.”
CRUD should NOT be used when the resource:
- Has a defined lifecycle (draft → submitted → approved → published)
- Requires stepwise progression or domain-specific transitions
- Triggers significant side effects (notifications, workflows, system integrations)
- Needs auditability of specific actions, not generic updates
- Involves approval, rejection, confirmation, or business decisions
- Will evolve into a workflow later — better to model the workflow explicitly now
In these cases, use Extended CRUD or Functional Resource (Commands) instead.
4.4. What CRUD Looks Like (URI Structure, Verbs, Status Codes)
CRUD uses a consistent and predictable structure based on REST resource semantics.
Standard CRUD Endpoint Set
| Operation | HTTP Verb | URI Pattern | Description |
|---|---|---|---|
| Create | POST | /resources |
Creates a new item. |
| Read (collection) | GET | /resources |
Returns a list of items. |
| Read (item) | GET | /resources/{id} |
Returns one item. |
| Update | PUT or PATCH | /resources/{id} |
Updates all or part of an item. |
| Delete | DELETE | /resources/{id} |
Removes an item. |
Best Practices for CRUD URI/Verb Design
- Use plural nouns for collections:
/books,/orders,/customers. - Use globally unique identifiers:
/orders/{orderId}. - Use PUT for full replacements; PATCH for partial updates.
- DELETE should be idempotent — repeated calls should be safe.
- POST should return 201 + Location header when creating new items.
- Avoid including verbs in the URI (e.g.,
/createUser,/update-profile). - Response bodies should follow your API platform’s response envelope and error model (Problem Details).
Typical Status Codes
201 Created— Successful resource creation200 OK— Successful read or update204 No Content— Successful delete400 Bad Request— Validation errors404 Not Found— Resource not found409 Conflict— Duplicate resource or state conflict
4.5. Anti-Patterns to Avoid
CRUD misuse is one of the most common governance issues in enterprise platforms. Typical anti-patterns include:
1. Overloading CRUD for business workflows
Using CRUD semantics to trigger domain actions:
PATCH /invoice/123 { status: "PAID" }PATCH /shipment/567 { status: "DELIVERED" }
These obscure intent, weaken authorization boundaries, and break auditability.
2. “Field-driven commands”
Putting commands in the payload instead of the API structure:
{ "action": "approve" }
This is effectively reimplementing commands inside CRUD and should be refactored into Extended CRUD or Functional Resources.
3. Bloated resource representations
Trying to capture too many concepts in one object, resulting in:
- Entities with dozens of optional fields
- Overloaded PATCH logic
- Tight coupling to backend data models
4. CRUDifying complex domains
When a domain has processes, steps, or roles, CRUD can’t represent:
- What’s allowed
- What’s next
- Who can do what
- How state transitions are governed
Use the right pattern for the job.
4.6. OpenAPI Example
Below is a canonical CRUD example demonstrating collection and item operations.
openapi: 3.0.3
info:
title: Books API - CRUD Pattern Example
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/books:
get:
summary: List books
tags: [Books]
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Book'
examples:
default:
value:
- id: bk_12345
title: "Domain-Driven Design"
author: "Eric Evans"
isbn: "978-0321125217"
publishedDate: "2003-08-30"
status: "PUBLISHED"
- id: bk_67890
title: "Implementing Domain-Driven Design"
author: "Vaughn Vernon"
isbn: "978-0321834577"
publishedDate: "2013-02-13"
status: "PUBLISHED"
post:
summary: Create a new book
tags: [Books]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BookCreateRequest'
examples:
default:
value:
title: "Patterns of Enterprise Application Architecture"
author: "Martin Fowler"
isbn: "978-0321127426"
publishedDate: "2002-11-15"
responses:
'201':
description: Created
headers:
Location:
schema:
type: string
example: https://api.example.com/books/bk_24680
content:
application/json:
schema:
$ref: '#/components/schemas/Book'
examples:
default:
value:
id: bk_24680
title: "Patterns of Enterprise Application Architecture"
author: "Martin Fowler"
isbn: "978-0321127426"
publishedDate: "2002-11-15"
status: "DRAFT"
/books/{bookId}:
get:
summary: Get a book by ID
tags: [Books]
parameters:
- in: path
name: bookId
required: true
schema:
type: string
example: bk_12345
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Book'
examples:
default:
value:
id: bk_12345
title: "Domain-Driven Design"
author: "Eric Evans"
isbn: "978-0321125217"
publishedDate: "2003-08-30"
status: "PUBLISHED"
'404':
description: Not Found
patch:
summary: Update a book
tags: [Books]
parameters:
- in: path
name: bookId
required: true
schema:
type: string
example: bk_12345
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BookUpdateRequest'
examples:
default:
value:
title: "Domain-Driven Design (Anniversary Edition)"
status: "PUBLISHED"
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Book'
examples:
default:
value:
id: bk_12345
title: "Domain-Driven Design (Anniversary Edition)"
author: "Eric Evans"
isbn: "978-0321125217"
publishedDate: "2003-08-30"
status: "PUBLISHED"
'404':
description: Not Found
delete:
summary: Delete a book
tags: [Books]
parameters:
- in: path
name: bookId
required: true
schema:
type: string
example: bk_12345
responses:
'204':
description: No Content
'404':
description: Not Found
components:
schemas:
Book:
type: object
properties:
id:
type: string
example: bk_12345
title:
type: string
example: "Domain-Driven Design"
author:
type: string
example: "Eric Evans"
isbn:
type: string
example: "978-0321125217"
publishedDate:
type: string
format: date
example: "2003-08-30"
status:
type: string
enum: [DRAFT, PUBLISHED, ARCHIVED]
example: PUBLISHED
required:
- id
- title
- author
- isbn
- status
BookCreateRequest:
type: object
properties:
title:
type: string
example: "Patterns of Enterprise Application Architecture"
author:
type: string
example: "Martin Fowler"
isbn:
type: string
example: "978-0321127426"
publishedDate:
type: string
format: date
example: "2002-11-15"
required:
- title
- author
- isbn
BookUpdateRequest:
type: object
properties:
title:
type: string
example: "Domain-Driven Design (Anniversary Edition)"
author:
type: string
example: "Eric Evans"
isbn:
type: string
example: "978-0321125217"
publishedDate:
type: string
format: date
example: "2003-08-30"
status:
type: string
enum: [DRAFT, PUBLISHED, ARCHIVED]
example: PUBLISHED
4.7. Visualizing the CRUD Pattern (Mermaid)
The following Mermaid diagram shows how a client interacts with a typical CRUD-style Books API, and how those calls map to data store operations. This helps learners connect the HTTP surface to the underlying resource lifecycle.
sequenceDiagram
autonumber
participant C as Client
participant API as Books API
participant DB as Data Store
Note over C,API: Read (List)
C->>API: GET /books
API->>DB: SELECT * FROM books
DB-->>API: [Book...]
API-->>C: 200 OK<br/>[Book[]]
Note over C,API: Create
C->>API: POST /books<br/>{BookCreateRequest}
API->>DB: INSERT INTO books (...)
DB-->>API: new row (id = bk_24680)
API-->>C: 201 Created<br/>Location: /books/bk_24680<br/>[Book]
Note over C,API: Read (Single)
C->>API: GET /books/bk_24680
API->>DB: SELECT * FROM books WHERE id = 'bk_24680'
DB-->>API: Book row
API-->>C: 200 OK<br/>[Book]
Note over C,API: Update (Partial)
C->>API: PATCH /books/bk_24680<br/>{BookUpdateRequest}
API->>DB: UPDATE books SET ... WHERE id = 'bk_24680'
DB-->>API: updated row
API-->>C: 200 OK<br/>[Book]
Note over C,API: Delete
C->>API: DELETE /books/bk_24680
API->>DB: DELETE FROM books WHERE id = 'bk_24680'
DB-->>API: success
API-->>C: 204 No Content