SE 11 API Design Principles - hmislk/hmis GitHub Wiki
SE-11: API Design Principles
Part of the Software Engineering Principles series
What Is an API?
An API (Application Programming Interface) is a defined contract that allows software components to communicate. An API specifies what operations are available, what inputs they expect, and what outputs they produce — without exposing how they work internally.
APIs exist at many levels:
- Class-level — the public methods of a Java class
- Library-level — the public surface of a JAR file
- Service-level — HTTP endpoints a service exposes to other systems
This article focuses primarily on HTTP/REST APIs, which are the most common form in modern web applications, but the principles apply broadly.
REST Principles
REST (Representational State Transfer) is an architectural style for web APIs defined by Roy Fielding in 2000. A REST API models resources and uses HTTP methods to manipulate them.
The Six Constraints of REST
| Constraint | Meaning |
|---|---|
| Stateless | Each request contains all information needed; the server stores no session state per client |
| Client-Server | UI concerns and data concerns are separated |
| Cacheable | Responses must declare whether they can be cached |
| Uniform Interface | Resources are identified by URIs; manipulation is through representations |
| Layered System | Clients do not know whether they are connected directly or through intermediaries |
| Code on Demand (optional) | Servers may send executable code to clients |
Resource Design
Think in Resources, Not Actions
REST models things (resources), not operations. URLs should be nouns, not verbs.
// Bad — RPC-style, action-oriented
POST /createPatient
POST /getPatientById?id=42
POST /deletePatient?id=42
// Good — REST-style, resource-oriented
POST /patients (create)
GET /patients/42 (read)
PUT /patients/42 (replace)
PATCH /patients/42 (partial update)
DELETE /patients/42 (delete)
Use Plural Nouns for Collections
/patients — collection of all patients
/patients/42 — a specific patient
/patients/42/bills — bills belonging to patient 42
Hierarchical URIs Reflect Relationships
/departments/5/stock — stock in department 5
/departments/5/stock/item-12 — a specific stock item in department 5
/prescriptions/99/items — items in prescription 99
Avoid going deeper than 2–3 levels — deeply nested URIs become unwieldy.
HTTP Methods
| Method | Purpose | Safe? | Idempotent? |
|---|---|---|---|
| GET | Retrieve a resource | Yes | Yes |
| POST | Create a new resource | No | No |
| PUT | Replace a resource entirely | No | Yes |
| PATCH | Partially update a resource | No | No |
| DELETE | Remove a resource | No | Yes |
Safe: The operation has no side effects (no data modification).
Idempotent: Calling the operation multiple times produces the same result as calling it once. PUT and DELETE are idempotent: deleting an already-deleted resource should return success (or 404), not an error.
HTTP Status Codes
Status codes tell the caller exactly what happened. Use them precisely.
Success (2xx)
| Code | Meaning | When to use |
|---|---|---|
| 200 OK | Success | GET, PUT, PATCH responses |
| 201 Created | Resource created | POST that creates a resource |
| 204 No Content | Success with no body | DELETE, or PUT/PATCH with no response needed |
Client Errors (4xx)
| Code | Meaning | When to use |
|---|---|---|
| 400 Bad Request | Invalid request syntax or data | Validation failures |
| 401 Unauthorized | Not authenticated | Missing or invalid credentials |
| 403 Forbidden | Authenticated but not authorised | Valid user, insufficient privileges |
| 404 Not Found | Resource does not exist | Patient 42 does not exist |
| 409 Conflict | Request conflicts with current state | Duplicate patient, stock already finalised |
| 422 Unprocessable Entity | Valid syntax but semantic errors | Business rule violation |
Server Errors (5xx)
| Code | Meaning | When to use |
|---|---|---|
| 500 Internal Server Error | Unexpected failure | Unhandled exception |
| 503 Service Unavailable | Temporarily unable to handle requests | During maintenance or overload |
Request and Response Design
Consistent JSON Structure
Adopt a consistent envelope for all responses:
// Success
{
"success": true,
"data": {
"id": 42,
"name": "Alice Perera",
"dateOfBirth": "1985-03-12"
}
}
// Error
{
"success": false,
"error": {
"code": "PATIENT_NOT_FOUND",
"message": "No patient found with id 42"
}
}
Naming Conventions
Use camelCase for JSON field names (consistent with JavaScript conventions):
{
"patientId": 42,
"dateOfBirth": "1985-03-12",
"isActive": true,
"admissionCount": 3
}
Dates and Times
Always use ISO 8601 format. Always include the timezone offset.
{
"admittedAt": "2025-06-08T14:30:00+05:30",
"dateOfBirth": "1985-03-12"
}
Versioning
APIs change over time. Versioning allows existing clients to continue working while new clients use the improved version.
URI Versioning (most common)
/api/v1/patients
/api/v2/patients
Header Versioning
GET /api/patients
Accept: application/vnd.hmis.v2+json
Versioning Rules
- Never make breaking changes to an existing version
- A breaking change is anything that causes existing clients to fail: removing a field, changing a field type, changing a status code, removing an endpoint
- Adding new optional fields is backward-compatible (not a breaking change)
- Deprecate old versions before removing them; communicate timelines clearly
Documentation
An undocumented API is unusable. Good API documentation includes:
- Overview — what the API does and who it is for
- Authentication — how to obtain and use credentials
- Endpoints — URL, method, parameters, request body, responses, error codes
- Examples — real request/response pairs
OpenAPI / Swagger
The OpenAPI Specification is the standard for documenting REST APIs. It produces machine-readable documentation that can generate client SDKs, server stubs, and interactive documentation (Swagger UI).
paths:
/patients/{id}:
get:
summary: Get patient by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Patient found
'404':
description: Patient not found
API Design Principles Summary
| Principle | Practice |
|---|---|
| Design for the caller | Make the API easy to understand and use; hide internal complexity |
| Be consistent | Same patterns for naming, errors, and formats throughout |
| Be explicit | Use precise HTTP methods and status codes |
| Fail clearly | Error messages must tell the caller what went wrong and how to fix it |
| Version from day one | Assume the API will change; plan for it |
| Document everything | If it is not documented, it does not exist for consumers |
| Validate early | Reject bad input at the boundary with a clear error, not deep inside processing |
| Make it predictable | Callers should be able to infer how untried endpoints work from the patterns they know |
Previous: SE-10: Version Control and Git Workflows
Next: SE-12: Security Principles