15. Pattern: Singleton
The Singleton pattern is used when a resource has exactly one instance in a given context (such as globally, per tenant, or per user).
Instead of treating the resource as a collection requiring an identifier (e.g., /users/123/profile), you expose it as a direct endpoint without an ID (e.g., /profile or /settings).
15.1. Overview
Singleton simplifies the API design by removing the collection wrapper and identifier from the path when only one instance makes logical sense.
Typical examples:
GET /profile(The authenticated user’s profile)GET /config(Global system configuration)PATCH /account/billing-preferences(Tenant-specific billing settings)
These operations:
- Remove the burden of discovering or storing an ID from the client
- Rely on context (like an authentication token or API key) to scope the resource
- Typically support only
GET,PUT, andPATCHmethods
15.2. When to Use Singleton
Use Singleton when:
- The resource is logically unique in the current scope.
- e.g., A user only has one
profileorwallet.
- e.g., A user only has one
- The resource identifier is redundant or derived entirely from authentication context.
- e.g., Using
/meinstead of forcing the client to decode a JWT to find their user ID for/users/{id}.
- e.g., Using
- The resource implicitly exists upon creation of a parent resource.
- e.g., When a user is registered, their
settingsare generated with default values.
- e.g., When a user is registered, their
- You want to prevent clients from creating multiple instances.
- Without a
POSTcollection endpoint, the cardinality is strictly limited to one.
- Without a
15.3. When NOT to Use Singleton
Avoid Singleton when:
- There is a possibility of multiple instances in the future.
- If a user might eventually need multiple profiles or multiple addresses, use a standard collection (
/addresses/{id}) from the start.
- If a user might eventually need multiple profiles or multiple addresses, use a standard collection (
- You need to reference the resource from other entities by ID.
- If the singleton needs a distinct ID to be referenced elsewhere, it might still need to exist as a collection item, or the ID needs to be explicitly returned in the singleton representation.
- The resource can be cleanly deleted and re-created frequently, mimicking a collection lifecycle rather than a persistent single state.
15.4. What the Pattern Looks Like
The Singleton pattern modifies standard CRUD by omitting the collection endpoints and stripping the ID from the URL path.
Collection CRUD (For Comparison)
POST /settings→ Create new settingsGET /settings/{id}→ Read settingsPATCH /settings/{id}→ Update settings
Singleton Resource
GET /settings- Returns the single settings object directly (not an array).
PUT /settings- Replaces the entire singleton resource. If it doesn’t exist yet, this creates it.
PATCH /settings- Partially updates the singleton resource.
Notice that POST and DELETE are typically omitted. You do not “create” a singleton (it inherently exists or is created via PUT), and you typically do not “delete” it (though you might revert it to defaults).
15.5. Anti-Patterns to Avoid
1. Returning a single-item array
GET /settings
[
{
"theme": "dark",
"notifications": true
}
]
- A singleton is not a collection. Returning an array of one item forces the client to always extract the first element. Return the object directly.
2. Requiring a static or hardcoded ID
GET /settings/1
- If the ID is always
1,default, orglobal, remove the ID from the path. It provides no value and complicates the URI.
3. Using POST to update the Singleton
POST /settings
{
"theme": "light"
}
POSTis traditionally for appending to a collection or triggering processing. UsePUT(for replacement) orPATCH(for partial updates) to modify a singleton.
15.15. OpenAPI Example
A lean but complete OpenAPI 3.0.3 document showing:
- A Singleton
/profileendpoint. - Contextual derivation (no ID required).
GETandPATCHmethods.
openapi: 3.0.3
info:
title: User API - Singleton Pattern Example
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/profile:
get:
summary: Get the current user's profile
description: Returns the singleton profile for the authenticated user.
tags: [Profile]
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Profile'
examples:
default:
value:
displayName: "Jane Doe"
email: "jane@example.com"
timezone: "America/Denver"
patch:
summary: Update the current user's profile
description: Partially updates the singleton profile resource.
tags: [Profile]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ProfileUpdateRequest'
examples:
default:
value:
timezone: "America/New_York"
responses:
'200':
description: Profile updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Profile'
examples:
default:
value:
displayName: "Jane Doe"
email: "jane@example.com"
timezone: "America/New_York"
components:
schemas:
Profile:
type: object
properties:
displayName:
type: string
email:
type: string
format: email
timezone:
type: string
ProfileUpdateRequest:
type: object
properties:
displayName:
type: string
timezone:
type: string
15.7. Visualizing Singleton (Mermaid Diagram)
sequenceDiagram
participant Client
participant API
participant Database
Note over Client,API: GET Singleton (No ID needed)
Client->>API: GET /profile (Auth: Bearer JWT)
API->>Database: Query Profile by context (userId from JWT)
Database-->>API: Return Profile Object
API-->>Client: 200 OK { displayName: "Jane" }
Note over Client,API: Update Singleton
Client->>API: PATCH /profile { timezone: "UTC" }
API->>Database: Update Profile for contextual user
Database-->>API: Acknowledge Update
API-->>Client: 200 OK { ...updated profile }