Request Lifecycle
This document outlines the end-to-end data flow for both procedure calls and stream subscriptions in UFO RPC. It details the process from the client’s initial request to the server’s final response, including URL structure, JSON payloads, and error handling. This specification is language-agnostic and applies to all official UFO RPC code generators.
Procedures (Request-Response)
Section titled “Procedures (Request-Response)”Procedures follow a standard request-response model over HTTP. The entire lifecycle is completed within a single HTTP transaction.
1. Client Invocation
Section titled “1. Client Invocation”The developer uses the generated client to call a procedure (e.g., CreateUser). The client library is responsible for constructing the HTTP request.
2. HTTP Request
Section titled “2. HTTP Request”The client sends an HTTP POST request to the server.
- Method:
POST - URL Structure: The URL is formed by appending the procedure name to the base URL.
- Format:
<baseURL>/<ProcedureName> - Example:
https://api.example.com/urpc/CreateUser
- Format:
- Headers:
Content-Type: application/jsonAccept: application/json
- Body: The request body contains the JSON-encoded input for the procedure.
{"name": "John Doe",}
3. Server Handling
Section titled “3. Server Handling”The server receives the request and performs the following steps:
- Routing: It maps the URL path (
/CreateUser) to the corresponding procedure handler. - Deserialization & Validation: It decodes the JSON body and performs built-in validation (e.g., checking for required fields). If this fails, it immediately responds with a validation error.
- Handler Execution: The server invokes the user-defined business logic for the procedure, passing the validated input.
Note: UFO RPC provides a hook system that allows developers to run custom code at various points in the lifecycle for tasks like authentication, custom input validation, logging, metrics, etc.
4. Server Response
Section titled “4. Server Response”The user’s handler returns data on success or error information on failure. The server then constructs the final JSON payload and serializes it to a string.
5. HTTP Response
Section titled “5. HTTP Response”The server sends a single HTTP response back to the client.
-
Status Code:
200 OK. The HTTP status is always 200 for successfully processed requests, even if the application logic resulted in an error. The success or failure is indicated within the JSON payload via theokfield. -
Headers:
Content-Type: application/json -
Body: A JSON payload containing the result of the operation.
On Success:
{"ok": true,"output": {"userId": "user-123","status": "created"}}On Failure:
{"ok": false,"error": {"message": "A user with this email already exists.","category": "ValidationError","code": "EMAIL_ALREADY_EXISTS","details": {"field": "email"}}}
6. Client Handling
Section titled “6. Client Handling”The client library receives the HTTP response.
- Deserialization: It decodes the JSON body.
- Result Unwrapping:
- If
okistrue, it returns the content of theoutputfield to the application code. - If
okisfalse, it returns the content of theerrorfield.
- If
- Resilience: For transport-level failures or 5xx server errors, the client automatically handles retries with exponential backoff according to its configuration.
Streams (Server-Sent Events)
Section titled “Streams (Server-Sent Events)”Streams use Server-Sent Events (SSE) to maintain a persistent connection, allowing the server to push multiple events to the client over time.
1. Client Subscription
Section titled “1. Client Subscription”The developer subscribes to a stream (e.g., NewMessage) using the generated client.
2. HTTP Request (Connection)
Section titled “2. HTTP Request (Connection)”The client initiates the connection with a single HTTP POST request.
- Method:
POST - URL Structure:
<baseURL>/<StreamName>- Example:
https://api.example.com/urpc/NewMessage
- Example:
- Headers:
Accept: text/event-streamContent-Type: application/json
- Body: The JSON-encoded input for the stream subscription.
{"chatId": "room-42"}
3. Server Handling (Connection)
Section titled “3. Server Handling (Connection)”The server receives the request and establishes the persistent connection.
- Validation: It validates the input just like a procedure. An error here terminates the connection attempt with a single JSON error response.
- Connection Upgrade: If validation passes, the server sends back HTTP headers to establish the SSE stream. The connection is now open and long-lived.
- Status Code:
200 OK - Headers:
Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-alive
- Status Code:
- Handler Execution: The server invokes the user-defined stream handler, providing it with an
emitfunction.
Note: Just like with procedures, a hook system is available for streams to run custom code for authentication, validation, logging, and other cross-cutting concerns.
4. Event Emission
Section titled “4. Event Emission”The server-side handler logic can now call the emit function at any time to push data to the client.
-
SSE Formatting: The server formats the output into a standard JSON payload and sends it as an SSE
dataevent. -
Data Transmission: The formatted event is written to the open HTTP connection.
Success Event:
data: {"ok":true,"output":{"messageId":"msg-abc","text":"Hello world!"}}(Note the required blank line after the data line)
Error Event (for stream-specific errors):
data: {"ok":false,"error":{"message":"You do not have permission to view this chat."}}
5. Client Handling (Event Loop)
Section titled “5. Client Handling (Event Loop)”The client library maintains the open connection and listens for incoming events.
- Event Parsing: As data arrives, the client parses the SSE
data:payload. - Deserialization: It decodes the JSON from the data field.
- Delivery: It delivers the content of the
outputorerrorfield to the application code, typically through a channel or callback.
6. Stream Termination
Section titled “6. Stream Termination”The connection can be closed in several ways:
- Client-side: The developer cancels the context, which closes the connection.
- Server-side: The stream handler function returns, signaling the end of the stream.
- Network Error: The connection is lost.
Resilience: If the connection is lost unexpectedly, the client automatically attempts to reconnect with exponential backoff, re-submitting the initial request.