Skip to content

// Communication & Integration · 01

REST / HTTP

Service Aleads toHTTP + JSONService B

Ask, then wait. Simple, universal, and blocked until it answers.

How it fits together

REST / HTTP structureService Ablocked until replytimeout 2sTimeoutService BGET /orders/42builds the response1 GET /orders/422 200 + JSONno reply in 2s
uses / callsfails / error path· numbered steps replay in order
  • Service A uses / calls Service B (1 GET /orders/42)
  • Service B uses / calls Service A (2 200 + JSON)
  • Service A fails / error path Timeout (no reply in 2s)

Spot it

  • The caller needs an answer before moving on
  • Third parties must integrate with plain tools
  • Chained sync calls make p99 latency balloon

It costs

The caller is coupled in time: if the callee is slow or down, so is the caller.

In code

public sealed record Order(Guid Id, string Customer, decimal Total, int Version);​app.MapPost("/orders", async (CreateOrder body, OrderDb db) =>{    var order = await db.AddAsync(body);    return Results.Created($"/orders/{order.Id}", order);});​app.MapGet("/orders/{id:guid}", async (Guid id, OrderDb db, HttpContext http) =>{    var order = await db.FindAsync(id);    if (order is null) return Results.NotFound();    http.Response.Headers["ETag"] = $"\"{order.Version}\"";    return Results.Ok(order);});​app.MapPut("/orders/{id:guid}", async (Guid id, UpdateOrder body, OrderDb db, HttpContext http) =>{    var order = await db.FindAsync(id);    if (order is null) return Results.NotFound();    if (http.Request.Headers["If-Match"].ToString() != $"\"{order.Version}\"")        return Results.StatusCode(StatusCodes.Status412PreconditionFailed);​    var updated = await db.ReplaceIfVersionAsync(id, body, order.Version);    if (updated is null) return Results.StatusCode(StatusCodes.Status412PreconditionFailed);    http.Response.Headers["ETag"] = $"\"{updated.Version}\"";    return Results.Ok(updated);});
Go deeperThe full explanation and what to ask for · 1 min readExpand

REST models the system as resources addressed by URLs and changed through a small set of HTTP verbs. Its strength is reach: every language, proxy, cache and browser understands it, and GET responses can be cached by HTTP infrastructure itself.

The classic mistake is a chain of synchronous calls: A calls B calls C, so availability multiplies down and latency adds up. Another is ignoring method semantics: a retried POST can create the thing twice, while PUT and DELETE are defined as idempotent.

Use it when the caller needs an answer now, and put a timeout on every call. When the result can wait, a message queue or an event decouples the two in time; for chatty internal calls, gRPC is leaner.

Ask for it

  • resource and verb
  • status codes
  • idempotent methods
  • ETag / If-Match
  • Problem Details
  • cursor pagination

Expose orders as an HTTP API: GET /orders/{id} returns 200 or 404, POST /orders returns 201 with a Location header, errors use Problem Details, GET sends an ETag and PUT requires If-Match (412 on mismatch), GET /orders uses cursor pagination, and publish an OpenAPI document.