Every JSON API you've called so far follows a set of conventions — this lesson names them.
You've now called HTTP APIs and worked with their JSON responses. But notice the vocabulary that kept sneaking in — GET, POST, status codes, "the resource wasn't found." None of that was arbitrary. It's part of a shared design convention called REST, and almost every JSON API you'll ever call (or, later, build yourself) follows it, at least loosely. Understanding REST as a set of ideas — not just a grab-bag of HTTP verbs you memorize — is what lets you predict how an unfamiliar API probably behaves before you've even read its documentation.
In this lesson, you'll learn what a "resource" is, what each HTTP verb actually means semantically, how status codes communicate outcomes, what "stateless" means for an API, and what separates a genuinely RESTful API from an API that merely happens to run over HTTP.
REST (Representational State Transfer) is a set of design conventions for building APIs around resources — nouns, like "an order" or "a customer" — that you act on using a small, standard set of HTTP verbs, like GET (read it) and POST (create one). It's not a protocol, a library, or a format you install — it's a shared way of thinking about how an API's URLs and verbs should be organized so they're predictable to anyone who already understands the conventions.
REST is an architectural style, originally described by Roy Fielding, built around a handful of constraints: resources are identified by URLs, resources are manipulated through a uniform, small set of standard HTTP methods, each request from client to server contains all the information needed to understand it (statelessness — the server doesn't remember anything about your previous requests), and responses declare their own format and are cacheable when appropriate. An API that follows these constraints is described as RESTful.
/orders/4471GET /orders/4471 — "read this order"Before conventions like REST became widespread, APIs were often designed as a loose bag of one-off action endpoints: /getOrder, /createNewOrderForCustomer, /orderCancelAction — each with its own quirky naming, its own idea of which HTTP method (if any) mattered, and its own way of reporting success or failure. A developer integrating with a new API had to read extensive documentation just to answer basic questions like "how do I fetch a single record?" — because there was no shared convention to lean on.
HTTP already has verbs with well-established meanings (GET, POST, PUT, DELETE) and status codes with well-established meanings (200, 404, 500). REST's core insight is: stop inventing new, bespoke conventions on top of HTTP, and instead lean directly on the semantics HTTP already provides. Once you know an API is "RESTful," you can make strong, mostly-correct guesses about it before reading a single line of documentation — GET /orders/4471 almost certainly fetches order 4471, and a 404 almost certainly means it doesn't exist.
Resource: /orders/4471 GET /orders/4471 → read order 4471 POST /orders → create a NEW order (note: no id — the server assigns one) PUT /orders/4471 → replace order 4471 entirely with the given data PATCH /orders/4471 → partially update order 4471 (only the given fields) DELETE /orders/4471 → remove order 4471
Notice the pattern: the URL almost always names what you're acting on (a resource — a noun), and the HTTP verb says what kind of action you're performing on it. Once that split clicks, most well-designed REST APIs stop looking like a memorization exercise and start looking predictable.
POST /orders, not POST /orders/4471, since the ID doesn't exist yet). Calling it twice with the same body generally creates two separate resources — POST is not idempotent.Status codes are how the server tells you what actually happened — grouped into ranges by their first digit:
2xx — Success
200 OK → generic success, response has a body (typical for GET)
201 Created → a POST successfully created a new resource
204 No Content → success, but there's deliberately nothing to send back (typical for DELETE)
4xx — "You (the client) made a mistake"
400 Bad Request → the request body/params are malformed or invalid
401 Unauthorized → you're not authenticated at all
403 Forbidden → you ARE authenticated, but you're not allowed to do this
404 Not Found → the resource at this URL doesn't exist
409 Conflict → the request conflicts with the resource's current state
5xx — "We (the server) made a mistake"
500 Internal Server Error → something broke unexpectedly on the server
503 Service Unavailable → the server is temporarily overloaded or down
The 401-vs-403 distinction is a common point of confusion worth calling out explicitly: 401 means "I don't know who you are" (no valid credentials at all), while 403 means "I know exactly who you are, and you're still not allowed to do this."
Consider a shopping cart API a checkout worker might integrate with. Even without reading its documentation, REST conventions let you predict its shape reasonably accurately:
GET /carts/9001 → 200 OK, the whole cart
GET /carts/9001/items → 200 OK, just the line items in that cart
POST /carts/9001/items → 201 Created, a new line item was added
PATCH /carts/9001/items/3 → 200 OK, updated just the quantity on item 3
DELETE /carts/9001/items/3 → 204 No Content, item 3 was removed
GET /carts/9999 → 404 Not Found, cart 9999 doesn't exist
A .NET client consuming this looks exactly like what you built in the previous two lessons — the REST conventions are what let you predict this shape in the first place:
public class CartClient(HttpClient httpClient)
{
public async Task<CartDto?> GetCartAsync(int cartId)
{
var response = await httpClient.GetAsync($"carts/{cartId}");
return response.StatusCode switch
{
System.Net.HttpStatusCode.OK => await response.Content.ReadFromJsonAsync<CartDto>(),
System.Net.HttpStatusCode.NotFound => null,
_ => throw new InvalidOperationException($"Unexpected status {response.StatusCode} fetching cart {cartId}")
};
}
public async Task RemoveItemAsync(int cartId, int itemId)
{
var response = await httpClient.DeleteAsync($"carts/{cartId}/items/{itemId}");
// 204 No Content on success — nothing to deserialize, just confirm it succeeded
response.EnsureSuccessStatusCode();
}
}
Because the API follows REST conventions, this client code needed almost no surprises — GET for reading, a 404 meaning "not found," DELETE returning an empty 204 body. An API that doesn't follow these conventions would require far more guesswork and documentation reading to integrate with correctly.
Imagine visiting an office building for the first time. If every floor labels rooms differently — one floor uses numbers, another uses random codenames, another has no labels at all — you can't navigate it without a guide for every single floor. But if every floor follows the same convention (odd numbers on the left, even on the right, a consistent directory at each elevator bank), you can find any room in an unfamiliar building on the first try, because you already know the pattern.
REST is that shared building convention for APIs: resources are the rooms, HTTP verbs are the standard actions ("enter," "leave," "renovate"), and status codes are the universally understood signs ("occupied," "not found," "no entry"). You don't need a guide for every new API — you already know how to read the building.
This is the single most common misuse of the term. An API can send JSON over HTTP while still being organized entirely around actions rather than resources — for example, always using POST for everything, with the "real" operation named inside the request body (POST /api/doStuff with {"action": "deleteOrder", "orderId": 4471}). That's a perfectly valid way to build an API, but it isn't RESTful — it's not using the HTTP verbs or URL structure to convey meaning at all, which throws away exactly the predictability REST is designed to provide. "JSON over HTTP" describes the format and transport; "RESTful" describes the design conventions layered on top.
Both PUT and PATCH "update" a resource, which is exactly why they're easy to mix up. The distinguishing question is: does this request describe the resource's entire new state (PUT — anything omitted may be cleared), or does it describe only what changed (PATCH — everything else stays untouched)? Sending a PUT with only a couple of fields, expecting the rest to be preserved, is a common integration bug caused by exactly this confusion.
Designing an endpoint like GET /orders/4471/cancel that actually cancels the order as a side effect of a "read" request — this breaks GET's core promise of safety, and can cause real damage: web crawlers, browser prefetching, and retry logic all assume GET requests are harmless to repeat.
Any request that changes server state should use POST, PUT, PATCH, or DELETE — never GET, no matter how convenient a simple link seems.
Catching "not 200" as one generic bucket and showing the same error message whether the problem was "you're not logged in" (401), "you don't have permission" (403), or "that record doesn't exist" (404) — these need genuinely different handling and different messages to the user.
Branch on the specific status code, as shown in the cart example, and let each category drive a distinct response in your own code.
Automatically retrying a failed POST request (e.g. "create this order") the same way you'd safely retry a GET — if the first attempt actually succeeded but the response was lost in transit, blindly retrying can create a duplicate order.
Only automatically retry requests that are safe to repeat (GET) or genuinely idempotent (PUT, DELETE) without extra precautions. Retrying a POST safely typically requires an additional mechanism, like an idempotency key, which is beyond this lesson's scope but worth knowing exists.
/orders/4471)You've seen how resources, verbs, and status codes fit together as a shared convention. Let's confirm you can reason about them correctly.
1. Why is it considered a serious design mistake for an endpoint like GET /orders/4471/cancel to actually cancel the order?
Correct: B
Why B is correct: GET's core convention is safety — it should be harmless to call any number of times. Systems throughout the web (browsers, crawlers, proxies, retry mechanisms) rely on this assumption and may issue GET requests without expecting side effects, so a GET that secretly changes data can cause real, unintended damage.
Why A is incorrect: GET requests work identically from server-side code, browsers, or any HTTP client — there's no such restriction.
Why C is incorrect: Resource IDs in the URL path are completely normal and expected for GET requests (like /orders/4471).
Why D is incorrect: Caching behavior is unrelated to this design flaw, and isn't guaranteed to happen "forever" by default anyway.
Reinforcement: Any action that changes data belongs behind POST, PUT, PATCH, or DELETE — never GET.
2. What is the key semantic difference between PUT and PATCH when updating a resource?
Correct: B
Why B is correct: PUT describes the resource's complete new state — fields you don't include may be treated as cleared or defaulted. PATCH describes a diff — only the fields you actually send are changed, and everything else on the resource stays exactly as it was.
Why A is incorrect: Neither verb is restricted to a particular content type; both are commonly used with JSON bodies.
Why C is incorrect: They have genuinely different, well-defined semantics — conflating them is a real and common source of integration bugs, as covered in Common Confusion.
Why D is incorrect: PATCH is applied to a single resource at a specific URL (e.g. PATCH /orders/4471), just like PUT.
Reinforcement: Ask "does this request represent the resource's entire new state, or just what changed?" to decide between PUT and PATCH.
3. A client sends a request with an expired authentication token and gets back a 401. It then sends a valid token but tries to access another user's private order, and gets back a 403. What's the difference between these two responses?
Correct: B
Why B is correct: 401 Unauthorized signals a missing or invalid authentication — the server can't establish who's calling. 403 Forbidden signals that authentication succeeded (the server knows who you are) but that identity lacks permission for this specific action. They answer different questions: "who are you?" versus "are you allowed?"
Why A is incorrect: They're both denials, but for meaningfully different reasons that callers should handle differently (re-authenticate vs. show a permissions error).
Why C is incorrect: Both status codes can apply to any HTTP verb — they're about authentication/authorization, not tied to a specific verb.
Why D is incorrect: Both are 4xx codes, meaning the client side of the interaction (missing credentials, insufficient permission) is the issue — neither indicates a server-side bug.
Reinforcement: 401 = "I don't know who you are." 403 = "I know who you are, and the answer is still no."
4. An API sends and receives JSON over HTTP, but every single operation — including reading data — is implemented as POST /api/execute with an "action" field inside the JSON body describing what to do. Is this API RESTful?
Correct: B
Why B is correct: This design describes a perfectly valid HTTP+JSON API, but it isn't RESTful — it doesn't use resource-identifying URLs or the standard HTTP verbs to convey meaning at all; instead, everything is routed through one generic endpoint with the real action buried in the body. That throws away exactly the predictability that following REST conventions is meant to provide.
Why A is incorrect: This is the exact misconception the lesson calls out — "JSON over HTTP" describes format and transport, not whether REST's design conventions are actually being followed.
Why C is incorrect: Well-formatted JSON and a status field don't address whether URLs and verbs are being used meaningfully — that's the actual test of "RESTful."
Why D is incorrect: RESTfulness is about the API's design conventions (URLs, verbs, status codes), not the implementation language behind it.
Reinforcement: A RESTful API expresses meaning through its URLs and HTTP verbs, not by tunneling every operation through one generic action endpoint.
You now understand REST as a design convention — resources, verbs, status codes, and statelessness — that lets you predict how a well-designed API behaves.
dotnetmadeeasy.com — Learn C# and .NET, the right way.