· 10 min read

API From A to Z: Theory and Practice

API horror stories from banking development, protocol comparison, and mistakes I've seen in every other specification. Material from a systems analysis course.

The Specification That Nearly Killed an Integration

Fall 2021. I’d just joined a banking project, and they hand me an API specification from an adjacent team for review. I open the Swagger file. Every endpoint is POST. All of them. Including fetching customer data, checking status, deleting. POST /api/client/get, POST /api/client/delete, POST /api/client/getStatus. Status codes: 200 for everything, including errors — the error is tucked into the body as a JSON field "error": "not found". No versioning. No pagination. When I asked “where’s the field documentation?” — a link to an Excel file on a shared drive, dated last year.

I’m not exaggerating. This isn’t the worst API I’ve seen in enterprise. It’s average. And when I started teaching a systems analysis course, I understood why — people simply aren’t taught how to design APIs. They learn the word “REST,” but nobody explains what’s behind it.

Protocols: A Table Instead of a Thousand Words

Before we dig into the mistakes — a map of the territory. Because “API” isn’t just REST, and the protocol choice determines everything else.

API protocol comparison

CriteriaRESTSOAPGraphQLJSON-RPCgRPC
Data formatJSON (usually)XML (strictly)JSONJSONProtobuf (binary)
TransportHTTPHTTP, SMTP, JMSHTTPHTTP, WebSocketHTTP/2
ContractOpenAPI/SwaggerWSDLSchema + SDLJSON Schema.proto files
TypingWeakStrongStrongWeakStrong
CachingHTTP cache out of the boxNoDifficult (single URL)NoNo
StreamingSSE, WebSocket separatelyNoSubscriptionsNoBidirectional
Barrier to entryLowHighMediumLowMedium
Where it livesEverywhereEnterprise, government, banksFrontend-heavy appsInternal callsInter-service communication

In a bank, you’ll encounter all of them. A single integration layer might talk to SOAP services of the core banking system, REST APIs for the mobile app, gRPC between internal microservices, and JSON-RPC for internal utilities. The idea of “let’s move everything to REST” shatters against reality: nobody’s going to rewrite the core banking system.

REST: What Fielding Actually Meant

REST is not a protocol. It’s an architectural style. And most “REST APIs” in production aren’t REST at all — they’re HTTP APIs with JSON. The difference is fundamental.

True REST implies statelessness (the server doesn’t maintain a session), a uniform interface via HTTP methods, resource addressing through URIs, and HATEOAS (hypermedia as the engine of application state). Almost nobody implements the last point, and that’s fine — for most tasks it’s overkill.

HTTP methods — this is where the fun begins. GET — retrieve, idempotent. POST — create, not idempotent. PUT — replace entirely, idempotent. PATCH — partial update. DELETE — remove, idempotent. Each carries semantics, and those semantics aren’t decoration. Proxies, caches, monitoring — all rely on correct method usage. When you do POST /getUser, you’re not just breaking aesthetics, you’re breaking infrastructure.

Status codes — the second battleground. 200 (OK), 201 (created), 204 (OK, no body), 400 (client error), 401 (not authenticated), 403 (not authorized), 404 (not found), 409 (conflict), 500 (server died). Returning 200 on an error with the description in the body means turning HTTP into a dumb transport and killing all the semantic infrastructure you were given for free.

SOAP: The Dinosaur That Will Outlive Us All

In startups, SOAP is a meme. In banking — harsh reality. WSDL contract description, envelope-header-body structure, WS-Security for encryption and signing, WS-ReliableMessaging for guaranteed delivery. Strict typing through XSD schemas.

I’ve worked with integrations where the core banking system’s SOAP service hadn’t changed in years — and all new systems were required to adapt. Changing a service that handles millions of transactions a day because “REST is trendier” is an idea that business rightly runs from.

When to choose SOAP: strict contract typing is critical, you need transactionality and guaranteed delivery, you’re integrating with systems that only speak SOAP. In banking, the third point overrules everything else.

GraphQL and JSON-RPC: Niche but Useful

GraphQL solves a real problem: the mobile client needs three fields from an object, but REST returns fifty. One endpoint, the client controls the response structure, built-in typing through schema. But the complexity shifts to the server, caching through identical POST requests is painful, and deeply nested queries can take down the database.

JSON-RPC — when the interaction is best described as “call a function with parameters” rather than “operation on a resource.” Internal service calls, utilities, everything that doesn’t fit the resource model of REST.

gRPC: When Milliseconds Matter

gRPC is in the table but deserves its own section — because in microservices architecture, it’s increasingly becoming the de facto standard. Protobuf instead of JSON — binary serialization, strict typing through .proto files, HTTP/2 with multiplexing and bidirectional streaming. On our banking project (more about the real-time architecture), gRPC was used for communication between the scoring service and the decision engine: where REST gave 15-20 ms per call, gRPC delivered 2-3 ms. At thousands of calls per second, that’s the difference between “works” and “doesn’t meet the SLA.”

The entry cost — generating clients and servers from .proto, no human-readable format (forget curl for debugging), and the need for a gRPC-gateway if you need browser access. But for inter-service communication within the perimeter — I haven’t found a better option yet.

How I Choose a Protocol

Four questions that save weeks of debate:

Who’s the consumer? An external partner — REST, because everyone understands it. An internal service with strict contract requirements — SOAP or gRPC. A mobile app with a dozen different screens — GraphQL.

What does the landscape dictate? If the corporate ESB runs on SOAP, adding GraphQL for one service is madness. If all microservices use gRPC — a REST service will look like an outsider.

Who will maintain it? REST — easier to find people. GraphQL — requires expertise. SOAP — requires people who aren’t afraid of XML and can read XSD.

What are the reliability requirements? Need transactionality — SOAP. Retries and eventual consistency are acceptable — REST.

These four questions once saved me from a month of pointless work. The frontend team came with a request: “We need GraphQL, we’re tired of pulling unnecessary fields.” I asked the first question — who’s the consumer? Turns out, the service had exactly two clients: the mobile app and an internal back-office. Both consumed three endpoints. The benefit of GraphQL — zero, while the cost of maintaining a schema, resolvers, and N+1 problems was very real. We added two query parameters for field selection to the existing REST and closed the issue in a day. GraphQL is a great technology, but for two consumers with three requests, it’s like renting a truck to deliver a carton of milk from the store.

Five API Mistakes I See in Every Project

1. Verbs in URLs

/getUsers, /createOrder, /deleteAccount. The HTTP method already contains the verb. GET /users = “get users.” POST /orders = “create order.” When I see POST /createUser — I know the designer copied the URL from some 2010 tutorial.

On our project, we caught this in review. One developer made POST /calculateRisk — and technically this isn’t a resource operation, it’s a procedure. We debated for a while. In the end, we settled on POST /risk-assessments — you’re creating a “risk assessment” object, which fits the resource model. But the debate was useful — it showed that REST isn’t for every task.

2. Everything via POST

That same specification from the beginning of the article. POST for reads, POST for deletes, POST for everything. Why do people do this? Because “POST definitely works through all proxies” and “I don’t want to deal with idempotency.” A lazy decision that comes back to haunt you at every subsequent stage: can’t cache GET requests, can’t safely retry, can’t distinguish reads from writes in monitoring.

3. 200 OK on Errors

{
  "status": 200,
  "error": true,
  "message": "User not found"
}

I’ve seen this in production at a major bank. The API Gateway faithfully proxies the response, monitoring sees 200, no alerts fire. Meanwhile the client isn’t getting data. We spent a week hunting down why an analytics dashboard was losing data — turns out the upstream service was returning 200 with an error in the body, and our ETL process treated it as success.

4. No Versioning

/api/users instead of /api/v1/users. The first six months, everything’s fine. Then the business asks to add a field that breaks backward compatibility. And it begins: either break all consumers at once, or introduce hacks with optional fields and conditions in the documentation. In a banking system where dozens of consumers may be calling the same API — this isn’t a theoretical problem, it’s a production incident.

5. No Pagination

An endpoint that returns all records. On the test stand, there are a hundred — works fine. In production — a million. One bad request — and the service is down with OOM. This happened to us with a customer transactions export endpoint. The developer added it “for later, we’ll add pagination later.” “Later” arrived in the form of a 3 AM incident.

Bonus: Hands-On Practice with a Telegram Bot

In my systems analysis course, I give an assignment: build a Telegram bot on Flask. The Telegram Bot API is an excellently documented REST API. Flask receives a webhook (POST with JSON), parses the message, handles the command, sends a response through api.telegram.org.

One project — and students get hands-on experience with HTTP methods, JSON serialization, webhooks, token authentication, and error handling. API theory stops being abstract, because the bot either works or it doesn’t. No “well, it’s basically correct.”

Why You Need to Know This

I had a junior analyst who came to negotiate an integration with an external partner. The partner sent a Swagger specification. The guy opened the file, looked at the wall of endpoints, and honestly said: “I don’t understand what’s written here.” Not because he was dumb — he was sharp. Simply nobody had ever explained to him what a path parameter is, how 201 differs from 200, why you need a Bearer token in the header, and why nullable: true in the response schema isn’t a minor detail but an architectural decision.

He had the meeting with the partner and agreed on the integration “verbally.” Without questions about date formats, without clarifying pagination, without discussing timeout behavior. Two months later, when development began, it turned out that half the agreements didn’t match what was in the spec. Another month of renegotiation. Classic.

If you’re an analyst — knowing how to read an API specification isn’t optional. It’s like knowing how to read blueprints for an engineer. Without it, you’re not designing an integration, you’re playing telephone between business and development.


Original article: API From A to Z on Habr

— Vladimir Lovtsov

Stay Updated

New articles, talks, and projects — no spam, only substance.

No spam. Unsubscribe anytime.

Related Articles