How to Write Detailed Design for APIs with Create, Update, Delete Functionality
back-end WEB

How to Write Detailed Design for APIs with Create, Update, Delete Functionality

VIOLET Dang

Table of Contents

After mastering how to design search APIs (see the previous article), it’s time to dive into the family of “write” APIs — create, update, and delete. These APIs directly affect system data, so their design must be careful and rule-driven.

Structure of Detailed Design for Create, Update, Delete APIs

1. API Name

As with search APIs, naming for write APIs should be concise, consistent, and clear. Avoid verbose names like “API to add a new product” or “API to delete a user.” Use these conventions:

  • Standard create: [Entity] + Create/Add
    Example: `userCreate` — Create a new user
  • Standard update: [Entity] + Update/Edit
    Example: `productUpdate` — Update product information
  • Standard delete: [Entity] + Delete
    Example: `orderDelete` — Delete an order
  • Partial update: [Entity] + [Action]
    Examples:
       - `orderUpdateStatus` — Update order status
       - `userUnblock` — Unblock a user
  • Create/update/delete by a foreign key (parent ID)
    Example: `productUpdateByShopId` — Update product by shop ID (often includes constraints)
  • Bulk create multiple records: [Entity] + BulkCreate
    Example: `shopBulkCreate` — Create multiple shops at once

With this convention, the team can infer functionality from the API name alone — no extra explanation needed.

2. HTTP Method

Write APIs typically use these methods:

Function Method When to use
Create POST When adding a new record to the system
Update (full) PUT When replacing the entire record
Partial update PATCH When updating part of a record
Delete DELETE When removing a record from the system

Why these methods and not others?

Technically, nothing stops you from using POST to update or PATCH to create. However, choosing the method that matches the intent makes your API easier to manage, read, maintain, and it adheres to RESTful conventions. Each method carries meaning, helping readers of docs or code understand the API’s purpose without extra commentary. When the team sticks to these rules, collaboration and development go much smoother.

Method breakdown

  • POST:
     - Used to create a new resource. The client sends data in the request body; the server creates the record and returns it (usually with an ID). → Hence POST is typically for creation because it generates a new resource.
     - Can be used for single or bulk creation, or to create a child resource under a parent (e.g., create a product for a shop).
     - Some systems use POST for partial updates, but this is discouraged because it’s confusing.
  • PUT:
     - Used to replace a record entirely. The client usually sends all fields, including unchanged ones.
     - PUT is “replace” semantics — missing fields may be removed or set to default (system-dependent).
     - Use PUT when you want the record’s state to match exactly what’s sent.
  • PATCH:
     - Used to update part of a record — only fields sent are changed; others remain.
     - Good for flexible updates, e.g., changing status or tweaking one field without sending the full payload.
     - Helps save bandwidth and avoids unintended overwrites.
  • DELETE:
     - Used to remove a record. The client typically sends the record identifier (ID).
     - Some systems implement soft delete, marking records as deleted rather than physically removing them. The DELETE endpoint remains, but the deletion logic must be documented.

Practical notes

  • If deletion requires confirmation (e.g., `confirm=true`), document it clearly
  • For multi-record updates, you can use PUT/PATCH with an array of IDs or a dedicated endpoint like `/products/bulk-update`.
  • Don’t overuse POST for all write operations — follow REST to keep your API extensible, maintainable, and compatible with tooling and documentation.
  • Legacy or third-party integrations may force POST for update/delete. If so, document the reason clearly to avoid confusion.

In short, picking the right HTTP method is not just theoretical — it improves development, maintenance, testing, and integration. If you have exceptions, note them clearly in the design so everyone aligns.

3. URI

URI design for create, update, delete is similar to search APIs. If needed, revisit the previous article’s URI principles. Here are practical tips for this group:

  • For operations on a specific resource (e.g., update or delete a product), place the resource ID in the URI path. It should be immediately clear what you’re doing and to which ID.
    Examples:
       - `PUT /products/123` — Update product with ID 123
       - `DELETE /products/123` — Delete product with ID 123
  • For child resources scoped by a parent foreign key, include the parent ID in the path to clarify the context. This makes documentation readable and the business flow easier to grasp.
    Example: `POST /shops/{shop_id}/products` → `POST /shops/5/products` — Create/update/delete a product for shop ID 5
  • For bulk operations (bulk update, bulk delete), don’t cram IDs into the path. Long, comma-separated or special-character-separated lists make the URI unwieldy and parsing error-prone. Instead, send the list via query parameters or the request body. It’s cleaner, more extensible, and safer for backend parsing.
  • A common pattern is to share the same URI for read, update, and delete — differing only by HTTP method. This is normal and increases consistency.
    Examples:
       - `GET /products/{product_id}` — Get product details
       - `PUT /products/{product_id}` — Update product details
       - `DELETE /products/{product_id}` — Delete product

Design URIs so that, at a glance, you know what the API does, on which resource, and within what scope. Don’t hesitate to reuse search API URI examples — the core principles are clarity, consistency, and ease of team communication.

4. Authentication and Authorization

This was covered in detail previously, so revisit that article if needed.

One extremely important point for write APIs that you must never gloss over: Insecure Direct Object Reference (IDOR). In search or read-only APIs, IDOR primarily leaks data — users can view others’ information. In create/update/delete, the consequences are much more severe: attackers can modify, delete, or even take over others’ data simply by changing IDs in requests.

For example, if an API allows order status updates and only checks the order ID provided by the client, user A might update user B’s order by swapping IDs. This leads to unintended modifications/deletions and damages trust. Always check ownership and permissions rigorously — never trust IDs from the client alone.

5. Input Parameters

Unlike search APIs, create/update/delete APIs usually have fewer complex URI or query parameters. These typically just identify the resource to operate on (e.g., product ID, user ID).

Most of the data needed to create, update, or delete is sent via the request body. Why?

  • First, request body is more secure, especially for sensitive fields (passwords, personal info). URLs are easily logged by proxies, browsers, or intermediaries.
  • Second, payloads are often large and structured — query strings have length limits (often ~2048 chars) and can be truncated.
  • Third, the body supports various data types, such as images, audio, CSV, Excel. Query parameters cannot do this. For file upload or binary payloads, only the body (`multipart/form-data` or binary) suffices. This is crucial for create/update APIs involving attachments.
  • Fourth, body data makes backend validation and extensibility easier. You can define a body schema (JSON Schema, Joi, Yup, etc.), declare validation rules, and let the framework enforce them before business logic. Adding fields later is simple — update the schema without changing the path or query string.

◆ Common request body types

Below are the typical body formats for write APIs:

  • application/json: The most common for structured create/update operations.
    Example:
POST /products
Content-Type: application/json
{
  "name": "iPhone 17 Pro Max 512GB",
  "price": 40000000,
  "description": "Apple’s latest product",
  "in_stock": true
}
  • multipart/form-data: Used when sending files (images, audio, documents) alongside text fields. Each part is separate; backend receives both file(s) and metadata.
    Example:
POST /products
Content-Type: multipart/form-data
name: iPhone 15
price: 25000000
image: [image file]
  • application/x-www-form-urlencoded: Common for simple forms; data is key=value. Suitable for endpoints with few fields and no files.
    Example:
POST /login
Content-Type: application/x-www-form-urlencoded
username=abc&password=xy

The above are the three most common ways to send data; there are also less common types such as:

  • text/plain: Plain text payloads for very simple data.
  • application/xml: XML payloads for legacy systems or specific partner integrations.
  • application/octet-stream: Binary data such as zip/exe or specially encoded content.
  • application/pdf, image/png, image/jpeg, audio/mpeg, video/mp4...: Direct file uploads where the payload itself is the file.
  • application/csv, application/vnd.ms-excel: Tabular data uploads for bulk import.

◆ Why not use `multipart/form-data` for everything?

You might wonder: “Why not always use `multipart/form-data` since it handles files and text?” Sounds convenient, but it’s not ideal:

  • If the API only receives text/JSON, `multipart/form-data` complicates things:
     - Modern frameworks provide strong schema/type/value validation for JSON. With multipart, you lose these conveniences and must manually parse and validate fields.
     - Frontend can send JSON with a simple function call; multipart requires building `FormData`, appending fields, handling files.
     - Backend must parse parts and cannot leverage JSON validators effectively.
  • `multipart/form-data` adds boundaries, headers, and encoding, making requests heavier than plain JSON. If you’re sending only text, JSON saves bandwidth and reduces server load.
  • Documentation/testing tools (Swagger/OpenAPI, CI/CD integrations) default to application/json for write APIs. Using multipart everywhere complicates docs and automation and confuses teams.

Practical examples:

  • Create product with image: use `multipart/form-data` for file + metadata.
  • Create product without files: use `application/json` — simpler and more robust.

Bottom line: choose the body type that fits the API’s purpose. Use multipart for file uploads; use JSON for text-only payloads. This improves validation, maintainability, performance, and integration.

6. Data Validation

Unlike search APIs, write APIs must enforce multiple layers of validation — tightly — to protect the system from attacks and business errors. For search, validation mainly prevents resource abuse (e.g., huge limits). For write, validation is the “security fence” guarding data from corruption, loss, and exploitation.

◆ Mandatory validations for write APIs

◇ Security hardening:

  • Block SQL Injection: Always sanitize client inputs. Disallow dangerous characters/patterns like single/double quotes, semicolons, SQL comments (`--`), or keywords like SELECT/INSERT/DROP/UNION. Prefer parameterized queries/ORMs; never string-concatenate SQL.
  • Block XSS: Strip dangerous HTML/script patterns such as `<script>`, `javascript:`, `onload`, `onerror` in all user-provided fields, especially descriptions/notes. Use sanitize libraries or escape HTML before storing/rendering.
  • Blacklist attack patterns: Maintain known-dangerous patterns (command strings, malware payloads, special characters) and reject inputs that match. Use security libraries or custom rules.
  • Validate uploaded files: Check MIME type, extension, and actual content. Allow only safe types (jpg/png/pdf...), reject dangerous types (.exe/.js/.bat/.sh...). Also verify file size and enforce limits to prevent resource exhaustion.

◇ Authorization checks: Define exactly which user roles may perform which actions under which business states. Rule of thumb: only allow the right user, right role, right state to perform the right action; reject everything else.

Example for order status updates:
- Sales staff can set status to “awaiting payment confirmation”.
- Warehouse staff can set to “shipped from warehouse” or “returned to warehouse”.
- Accounting can set to “payment confirmed”.
- Customer can set to “order canceled”.
- Admin may have broader permissions (if business permits).
- Any mismatch (e.g., warehouse confirming payment) must be rejected.

◇ Required fields: Fields that are non-nullable/required in the database must be required by create/update APIs as well. Required means the client must send the field, it must have a value, and it must not be an empty string ''.

  • Create APIs: If the database has no default, the API must require the field and reject empty ''. If the database has a default and you want to always store that default when creating, do not accept the field from the client (even if sent). This avoids security holes, e.g., creating an order with initial status set to cancel.
  • Update APIs: Data already exists. If a field may be edited, accept it; otherwise block it. Do not allow updating fields to empty ''.

◇ Type validation: Every field must match its declared type (integer, float/decimal, string, boolean, array, object, date, enum…) and this must align with the database schema. If the type is wrong, return an error — do not auto-coerce or ignore.

  • Integer: Only integers; no floats, strings, or special characters.
    Examples:
       - 1, 100, -5 are valid
       - 1.5, "abc" are invalid
  • Float/double/decimal: Accept floats or integers.
    Examples:
       - 1.5, 100.0, -3 are valid
       - "abc" are invalid
  • String: Only character strings.
    Examples:
       - "iPhone", "abc123" are valid
       - 123 are invalid
  • Boolean: Accept true/false or 1/0 depending on the system.
    Examples:
       - true, false, 1, 0 are valid
       - "yes", "no" are invalid
  • Date/datetime/timestamp: Strings in a valid format (ISO 8601, yyyy-MM-dd, yyyy-MM-dd HH:mm:ss...).
    Examples:
       - "2025-10-14", "2025-10-14T10:00:00Z" are valid
       - "2025/10/14", "14/10/2025", "abc" are invalid
  • Enum: Values must be within the allowed set.
    Examples:
       - Order status: only "pending", "confirmed", "cancel"
       - Others are invalid.
  • Array: Must be a list of elements of consistent types; validate count and element types.
    Examples:
       - [1,2,3], ["a","b"] are valid
       - "1,2,3", null are invalid
  • Object: Must match the designed structure; nested fields must also be valid.
    Examples:
       - {"name":"A","price":100} is valid
       - "{name:A}", null are invalid

◇ Value validation:

  • Examples:
       - Quantity must be between 1 and 100; <=0 or >100 is invalid.
       - Date must be today or future; past dates are invalid.

◇ Length, pattern, format:

  • Examples: product name <= 255 chars, email format valid, phone number matches pattern.
  • Don’t forget file checks: if API allows file uploads, validate type, count, and size. Clarify whether limits apply per request, per resource ID, or per file.

◇ Uniqueness checks:

  • Examples: email, username, product code must be unique and not duplicate existing records.
  • Consider whether to include soft-deleted records in uniqueness checks. Some systems ignore soft-deleted records — e.g., if an account with username "test" was soft-deleted, creating a new account with "test" may be allowed.

◇ Business logic validation: e.g., order status may only transition from "pending" to "confirmed", not backwards.

◆ Why is validation critical for write APIs?

If you skip or loosen validation for write endpoints, you risk:

  • Security vulnerabilities: SQLi, XSS, malicious uploads.
    Examples:
     - Without input sanitization, a client could set product name to `'; DROP TABLE products; --` and attempt SQL injection, wiping the table.
     - Without script blocking, a client could set a description to `<script>alert('XSS')</script>`, attacking users viewing it on the web UI.
     - Without file type checks, attackers could upload `.exe` or scripts, potentially compromising the server.
  • Unauthorized actions: users performing operations beyond their roles.
    Example: warehouse staff setting order status to “paid”.
  • Bad data and broken business processes.
    Example: if types aren’t checked, sending price as "abc" may crash the system or corrupt the DB.
  • Duplicates and inconsistency, hard to audit and maintain.
    Examples:
     - Multiple users with the same email/username breaks login, notifications, and role assignments.
     - Duplicate product codes cause confusion in inventory, invoicing, or external integrations.
     - Invalid status transitions or unsynchronized multi-table writes lead to retrieval/reporting errors.

In short, validation for write APIs is not "nice-to-have" — it’s mandatory. Invest in designing, enforcing, and documenting validation rules to protect your system, data, and product reputation.

7. Data Persistence

This is the heart of create/update/delete APIs. Bad or incomplete writes cause bad data and downstream pain. Document thoroughly:

  • Which tables/columns to insert/update/delete.
  • For create/update:
     - Specify stored values (omit when using DB defaults).
     - If branching logic results in different storage semantics, document each branch clearly.
     - If files are uploaded, specify storage location, naming conventions, and any file metadata to persist (original name, size…).
     - For date/time/datetime, state the timezone explicitly.
     - Don’t forget related tables! If writing to `orders`, should you also write to `order_details`? If writing to `products`, do you also write to `product_suppliers`?
  • For delete:
     - Hard delete or soft delete?
     - Whether to delete dependent records (foreign-key-bound tables).
     - If records have files stored elsewhere, whether to delete corresponding files.

8. Output Parameters

Unlike search APIs, write APIs focus on writing, so output guidelines are simpler:

  • Return the minimal information necessary. Typically `id` (and optionally `version`/ETag) so the client can confirm and proceed. For detailed views, call the corresponding GET. Returning full objects from multiple endpoints leads to divergent contracts and higher maintenance costs.
  • Standardize HTTP status codes for each case so frontend messaging is consistent. For example: `201 Created` for create, `204 No Content` for update/delete when no body is needed.

Sample Create/Update/Delete APIs Detailed Design

You can view and download the template (API Blueprint) to use as a standard frame for writing detailed designs for search APIs [here](https://github.com/sa-violetdang/advanced-api-documentation).

Conclusion

From naming, HTTP methods, URIs, security, validation, to storage and responses — you now have a solid framework to design clean, extensible, low-tech-debt write APIs.

The key to write APIs isn’t a verbose response but discipline: perform the write correctly, respond minimally, and use standard status codes so the client understands immediately without extra ceremony. The less extraneous data you return, the more stable the contract and the lower the maintenance cost.

Key Takeaways

  1. Start minimal, expand when needed: Prefer `204 No Content` for update/delete and return only `id` on create. If required, add minimal `version`/ETag or `links.self` — avoid returning full objects.
  2. Consistent contracts: Names, URIs, methods reflect semantics; status codes clear (`201/204/400/404/409`). Use stable time/id formats to simplify integration and testing.
  3. Validation is the safety fence: Security (SQLi/XSS/files), authorization, types, uniqueness, and business rules. Document once and enforce across the team.
  4. Principled persistence: Transactions, write order, hard/soft delete, table relations, file handling. Document semantics to avoid bad data and painful rollbacks.
  5. Advanced features when needed: Add idempotency for POST, concurrency via `ETag/If-Match`, observability with `requestId`/timestamp, and support bulk/async with clear contracts (partial success, `202 Accepted`).

Above all, remember: good design reduces guesswork for API consumers (FE/BE). Every line in your docs should cut communication errors and speed development. Keep responses lean and contracts clear — reads belong to GET — that’s how your system stays robust over time.