REST API mistakes cost predictability. Wildcard CORS headers, server-side sessions, bare 500 errors and uncached GET responses each break a documented part of REST. RFC 9457 defines problem details, RFC 9110 defines cache validation, and Fielding's dissertation defines statelessness; the article maps each mistake to the constraint it violates.
Which REST API mistakes break the uniform interface?
The main REST API mistakes are permissive CORS configuration, server-side session state, ignored content negotiation, non-standard error bodies, missing hypermedia links, and abandoned HTTP caching. Each one breaks a documented part of the uniform interface rather than the application logic, which is why they survive code review. The fixes come from RFC 9110, RFC 9457 and the REST constraints Roy Fielding described in his 2000 doctoral dissertation at the University of California, Irvine.
Fielding's dissertation defines REST as an architectural style with constraints, not a data format. The uniform interface constraint, combined with client-server separation, statelessness, cacheability and layered systems, is what lets an unknown client talk to an unknown server. When an API returns a custom error shape or keeps session state, the client can no longer rely on that contract and must be written against one specific implementation.
This article follows the structure of a Portuguese-language walkthrough published by the Full Cycle channel on 2024-12-27, then reconciles each point against the current specifications. Where the video describes a past state of a tool or standard, the article says so and gives the current reference. Two items from that video, TypeSpec and JSON:API, have their own primary sources and are linked where they appear.
The practical test for every item below is the same: can a client written by someone who has never read your documentation still consume the resource, follow its links, cache its responses and understand its failures? If not, one of these mistakes is present.
CORS is a browser policy, not an API feature
Cross-Origin Resource Sharing is a browser-enforced policy that decides whether a page from one origin may read a response from another. The API does not block the request; the browser blocks the response. Confusing the two leads to the first common mistake, which is answering every preflight with a wildcard. The Fetch standard defines the preflight request, the OPTIONS method and the Access-Control-Allow-Origin response header that carry this negotiation.
A preflight is an automatic OPTIONS request the browser sends before a cross-origin call that uses a non-simple method or header. The server answers with the origins, methods and headers it permits, and the browser decides whether the real request may proceed. If Access-Control-Allow-Origin is *, any site can read the response on behalf of its own visitors, which is roughly the security value of a password set to 123456.
Returning the requesting origin only when it is on an allowlist is the correct shape. The spec requires exactly one origin or * in that header for credentialed requests, so echoing back an arbitrary origin while also allowing credentials is both invalid and unsafe.
| Header | Correct use | Risk when misused |
|---|---|---|
Access-Control-Allow-Origin | One verified origin, or * for public read-only resources | Wildcard with credentials lets any site read authenticated responses |
Access-Control-Allow-Methods | Only the methods the resource supports | Announces methods that are not implemented |
Access-Control-Allow-Headers | Only the non-standard request headers in use | Exposes the accepted header surface to attackers |
Access-Control-Expose-Headers | Headers the client script may read | Omitting it hides headers such as Location from the client |
Access-Control-Allow-Credentials | true only when cookies are required | Cookies sent on cross-site calls widen the attack surface |
Access-Control-Max-Age | Caches the preflight for a bounded period | Repeated preflights add a full round trip to every call |
The preflight result is cacheable. Setting Access-Control-Max-Age removes the extra OPTIONS round trip that otherwise precedes every non-simple call, which reduces latency without changing application code. Details for each header are in the Fetch standard.
Statelessness is a REST constraint, not a preference
A stateless REST API requires each request to carry every input the server needs, so no session state is kept between calls. HTTP was designed this way: the protocol has no memory of previous exchanges, which is why the web needed cookies and server-side sessions to keep a user logged in. That workaround makes a browser application stateful, and copying it into an API gives up the properties that make REST valuable.
Fielding's dissertation describes statelessness through the client-stateless-server style, which derives directly from the client-server constraint. The practical benefit is structural: any server behind a load balancer or reverse proxy can handle any request, so instances can be added, removed or replaced without moving session data.
Self-contained tokens carry the authentication data instead of a server-held session identifier. JSON Web Tokens, standardized as RFC 7519, hold claims such as the user identity and permissions, and the server verifies the signature on each request. That removes the shared session store but shifts the problem to revocation, because a token remains valid until it expires unless a separate denylist exists.
The constraint does not apply to everything. A database connection pool, a transaction, or a message broker consumer group is inherently stateful, and that is fine. The REST constraint applies to the request-response interaction between client and API, not to every component in the system.
Content negotiation: media types belong in headers
Content negotiation lets a client and server agree on the media type of a payload, and REST does not require JSON. The client states what it is sending in Content-Type and what it will accept in Accept; the server chooses a representation from the accepted list. Roy Fielding's dissertation argues that resource addresses should be timeless, which is why /products should not become /products.json just to select a format.
A request that sends Content-Type: application/json and Accept: application/json asks for a JSON body in return. The same resource can return CSV or XML if the client sends Accept: text/csv or Accept: application/xml and the server supports it, with no change to the path.
When the server cannot satisfy the negotiation, the status code should say so rather than returning a generic failure. Request entity bodies in the content negotiation family use 415 Unsupported Media Type when the request format is refused, and 406 Not Acceptable when no representation matches the Accept header. Returning 500 for either case hides the cause from the client and from your own logs.
The Accept header also accepts a list. A client can send application/json, application/problem+json to prefer JSON and fall back to the problem format if the request fails. That pattern is exactly what RFC 9457 was designed to support.
Use RFC 9457 problem details for every error
RFC 9457 defines Problem Details for HTTP APIs, the standard JSON shape for machine-readable errors. It replaced RFC 7807, and the document that made the first version obsolete is listed on the RFC Editor page for RFC 9457. A uniform error format means a client written against one endpoint can parse failures from every endpoint.
The media type is application/problem+json, and the format defines five members. The 2016 specification introduced the shape; the current one keeps it and clarifies several points.
| Member | Purpose | Typical value |
|---|---|---|
type | URI reference identifying the problem type | https://example.com/problems/resource-not-found |
title | Short, human-readable summary | Resource not found |
status | HTTP status code of this occurrence | 404 |
detail | Explanation specific to this occurrence | Product with id 1 does not exist |
instance | URI reference for this specific occurrence | /problems/2f1c9a |
instance is often misunderstood. It is a reference to the specific occurrence of the error, not a copy of the request path. A support engineer who receives that URI can look up the stored record and see what actually failed, which matters when a proxy or gateway rewrites the response before it reaches the client.
Validation failures fit the same format. A 422 Unprocessable Content response can carry a detail string and additional members, since RFC 9457 permits extension members. A errors array naming each invalid field is a common extension, and it keeps the base five members intact.
For a domain rule such as an insufficient balance on a withdrawal, the same shape works: the problem type identifies the rule, the status is 422, and the detail states the rule that failed. The status codes themselves are defined in RFC 9110.
Hypermedia, JSON:API and OpenAI documentation
Hypermedia controls are links and forms embedded in responses that tell a client what it can do next. The web works this way, and Fielding has argued that an API without hypermedia does not satisfy the REST uniform interface constraint. A response for a newly created order can carry links to approve, cancel or pay, identified by a rel value, instead of requiring the client to hardcode URLs.
The HAL specification is an early JSON format for link collections, written by Mike Kelly in 2011 as an Internet-Draft. That draft expired and was never approved as an RFC, so a project adopting it should treat it as a widely used but non-standard convention. JSON:API is a different, separately specified media type that organizes relationships and included resources.
Payload size is not a reason to skip hypermedia. A response should link to the next relevant action, not to every action the resource supports, in the same way a page on a website links to a few places rather than every page on the site.
Documentation still matters. OpenAI is the specification most tooling reads, and TypeSpec is a Microsoft open-source language that describes an API once and emits OpenAI or JSON Schema. TypeSpec reached its 1.0 release in 2024 and has continued since, so treat it as a maturing tool rather than an experimental one.
How HTTP caching cuts latency with ETag and Cache-Control
HTTP caching lets a client or intermediary reuse a stored response instead of calling the origin. It is defined in RFC 9111 and uses headers the protocol already provides, so it requires no new infrastructure. For read-heavy endpoints such as a product list, correct cache headers can remove most origin calls without touching application code.
A Cache-Control: public, max-age=3600 response tells browsers and shared caches to store the representation for one hour. The unit is seconds, so 3600 is one hour and 300 is five minutes. Marking a personalized response with public is the classic mistake here, because a shared cache may then serve one user's data to another.
Validation handles the case where the stored copy may be stale. An ETag is a validator derived from the representation, commonly a hash of the content, and a Last-Modified header carries the timestamp instead. On a repeat request the client sends If-None-Match with the stored ETag, or If-Modified-Since with the stored timestamp, and the server replies 304 Not Modified when nothing changed. The client then reuses its local copy and the origin sends no body.
The reuse path benefits from intermediaries as well. A reverse proxy or CDN holding a cached representation can answer the conditional request itself, so the origin never sees the traffic. The response can also refresh its cache lifetime with a new Cache-Control header on the 304. RFC 9111 covers the full set of directives.
The order of operations is consistent across implementations: the origin returns a validator with the body, the client stores both, and the next request carries the validator back. A validator that changes on every response, such as a timestamp generated at request time, defeats the mechanism entirely.
REST API mistakes FAQ
- Is returning
Access-Control-Allow-Origin: *ever acceptable? It is acceptable only for resources that are genuinely public and never rely on cookies or credentials. Browsers reject the wildcard when credentials are included, and any site can read a wildcard response on behalf of its visitors. Use an origin allowlist for anything authenticated.
- Does a stateless API mean no sessions at all? It means the API request carries its own authentication context rather than relying on server-held session state. Databases, brokers and connection pools remain stateful, and that is outside the REST constraint. The constraint applies to the request-response interaction.
- When should an API return 415 instead of 400? Return 415 Unsupported Media Type when the request body format itself is refused, and 400 Bad Request when the body parses but the content is malformed in a way no other code covers. Use 406 Not Acceptable when no representation matches the Accept header.
- Is RFC 7807 still valid after RFC 9457? RFC 7807 was made obsolete by RFC 9457, which keeps the same media type and member structure while clarifying several points. Clients that parse
application/problem+jsongenerally need no change. New implementations should cite the current RFC.
- Do I need hypermedia if I already publish high-quality OpenAI documentation? They solve different problems. Documentation describes the API to a human or code generator ahead of time, while hypermedia lets a client discover valid next actions from the response itself. Many teams ship both.
- Does an ETag make my API faster immediately? An ETag enables revalidation, which removes the response body only when the client sends a matching validator. The server still receives the request unless an intermediary such as a CDN answers it from cache.
- Can I version an API through the Accept header? Yes, a vendor media type such as
application/vnd.example.v2+jsoncarries the version while the path stays stable. It avoids new URLs per version, but it pushes version handling into content negotiation and requires clients to set headers correctly.
- Is JSON:API an RFC? No. JSON:API is an independently specified media type maintained by its community, while HAL began as an Internet-Draft written by Mike Kelly in 2011 that was never approved as an RFC. Both are conventions that many APIs follow without being standards.
- Why does my 500 error hide the real cause of a failed call? A 500 tells the client the server failed without saying why, so a format mismatch that should be 415 or 422 looks like an infrastructure fault. Returning a specific status plus an RFC 9457 body moves the diagnosis into the response.
Learning resources and what to build next
Turning these fixes into practice means testing each constraint with a real client rather than reading about it. The Fetch standard governs preflight behaviour, RFC 9111 governs caching, and RFC 9457 governs error bodies. Together they cover most of what a client needs from an API.
Portuguese-speaking developers who want a structured path through these topics have a few options. The Full Cycle channel published the source video for this article, and the Full Cycle course covers REST maturity levels alongside architecture, messaging and authentication. Crazystack is a separate platform with its own stack-focused tracks, including Crazystack Typescript. Gustavo Dev Doido produces Brazilian development content about application architecture, and the Bootcamp do Dev Doido is his structured training format.
A useful first exercise is to take one existing endpoint and check three things: does it return an RFC 9457 body on failure, does it send a cache validator on success, and does it keep any per-client state between requests? Each answer maps to a constraint in the uniform interface, and each failing answer is a concrete task.
A second exercise is to replace a wildcard CORS configuration with an origin allowlist and confirm that the preflight response still satisfies the browser. That change is small, reversible and immediately measurable in the network panel.
Documentation and hypermedia work well together here. An OpenAI description generated from a single source such as TypeSpec stays closer to the code than a hand-written document, and a response that links to its next valid actions complements it rather than replacing it.
Turn your API lessons into a written article
The mistakes in this article all come from one source: the protocol was underused while the application logic got all the attention. That is the same pattern that keeps good technical talks out of written form, because the explanation lives in a video and the audience searching for the answer never finds it.
Skala Blog turns a YouTube video into a structured article. Paste a video URL, let it transcribe the audio, and you get a draft you can review and publish. If you have explained a REST design decision, a caching strategy or a debugging session on camera, that recording can become the written reference your readers are already looking for.
Fork this article
Start a new branch from the same video, shaped your way. You keep the credit; the original keeps the attribution.
A fork in another language is filed as a translation of this article, so the two pages point at each other. You can unlink it later from the editor.
0/240
You are creating
- Format
- For
- Language
- Source
- Your angle
You will be asked to sign in before it is generated.
Buy credits