This pattern describes how to manage long-running or background processing tasks in a REST-based API, allowing clients to initiate a process and check its progress asynchronously.

Details

When a client requests a long-running task, the API immediately returns a response with an HTTP status code of 202 Accepted. This response includes a Location header that points to a resource where the client can check the status of the task. The client then periodically polls this URL to get updates on the task’s progress until it is complete.

Common Pattern Names/Synonyms

  • Asynchronous Task Pattern
  • Deferred Result Pattern
  • 202 Accepted Pattern
  • Polling Pattern
  • Long Running Operation Polling

Common Use Cases

  1. Data Processing: Initiating a data import process that takes time to complete.
  2. Report Generation: Requesting the generation of a report that requires complex calculations or aggregation of large datasets.
  3. Video Processing: Starting a video encoding or transformation task that runs in the background.

When to Use

  • High scalability needs: Ideal for systems requiring concurrent handling of numerous requests.
  • Variable task durations: Effective for operations with unpredictable processing times.
  • Client-specific polling flexibility: Suitable when clients may vary in their needs for update frequency, or for applications needing frequent updates on a resource’s state, but the client is unable to leverage Webhooks and/or is not capable of using Websockets or other server-push notification options.

When Not to Use

  • Immediate feedback required: Not suitable for operations where instant updates are crucial.
  • Risk of over-polling: Avoid if there is a concern that clients may poll too frequently, potentially leading to performance issues.

Examples

Creating a New Resource

Request:

POST /api/reports HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "reportType": "yearly-sales",
  "parameters": {
    "year": "2023"
  }
}

Response:

HTTP/1.1 202 Accepted
Location: /api/reports/12345/status

Polling for Status

Request:

GET /api/reports/12345/status HTTP/1.1
Host: example.com

Response (In Progress and Complete):

HTTP/1.1 200 OK
Content-Type: application/json

{
  "status": "In Progress",
  "estimatedTimeRemaining": "120 seconds"
}

HTTP/1.1 200 OK
Content-Type: application/json

{
  "status": "Complete",
  "reportURL": "/api/reports/12345/download"
}

Sequence Diagram (queries background job status)

sequenceDiagram
    participant Client
    participant Server
    participant BackgroundProcess as Background Process

    Client->>Server: 1. POST /api/reports
    Server->>BackgroundProcess: 2. Initiate Background Process
    Server-->>Client: 3. 202 Accepted (Location: /api/reports/123)
    Client->>Server: 4. GET /api/reports/123/status
    Server->>BackgroundProcess: 5. Query Process Status
    Server-->>Client: 6. 200 OK (Status: In Progress)
    Client->>Server: 7. GET /api/reports/123/status
    Server->>BackgroundProcess: 8. Query Process Status
    Server-->>Client: 9. 200 OK (Status: Complete, Report URL: /api/download)

Sequence Diagram (API is updated via message to update state)

sequenceDiagram
    participant Client
    participant Server
    participant MessageBroker as Message Broker
    participant BackgroundProcess as Background Process
    participant Database

    Client->>Server: 1. POST /api/reports
    Server->>BackgroundProcess: 2. Initiate Background Process
    Server-->>Client: 3. 202 Accepted (Location: /api/reports/123)
    BackgroundProcess->>MessageBroker: 4. Post Job Completion Status
    MessageBroker->>Server: 5. Notify API of Job Completion
    Server->>Database: 6. Update Resource Status in DB
    Client->>Server: 7. GET /api/reports/123/status
    Server->>Database: 8. Retrieve Status from DB
    Server-->>Client: 9. 200 OK (Status: Complete, Report URL: /api/download)

OpenAPI Example

openapi: 3.0.0
info:
  title: Asynchronous Processing API
  version: 1.0.0
servers:
  - url: 'https://api.example.com'
paths:
  /reports:
    post:
      summary: Initiates a report generation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                reportType:
                  type: string
                parameters:
                  type: object
                  properties:
                    year:
                      type: string
            examples:
              reportRequest:
                value:
                  reportType: "yearly-sales"
                  parameters:
                    year: "2023"
      responses:
        '202':
          description: Report generation initiated
          headers:
            Location:
              schema:
                type: string
              description: URI to poll for report status
  /reports/{reportId}/status:
    get:
      summary: Polls the status of the report generation
      parameters:
        - name: reportId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Current status of the report
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  estimatedTimeRemaining:
                    type: string
              examples:
                inProgress:
                  value:
                    status: "In Progress"
                    estimatedTimeRemaining: "120 seconds"
                complete:
                  value:
                    status: "Complete"
                    reportURL: "/reports/12345/download"

Updated: