10 min read

API From A to Z: Theory and Practice

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

API From A to Z: Theory and Practice

The Specification That Nearly Killed an Integration

Fall 2021. I'd just joined a banking project, and in the first few days they hand me an API specification from an adjacent team to 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, errors included — 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 run into all of them. A single integration layer might talk to SOAP services in the core banking system, REST APIs for the mobile app, gRPC between internal microservices, and JSON-RPC for internal utilities. The "let's just move everything to REST" idea shatters against reality: nobody is 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 keep a session), a uniform interface via HTTP methods, resource addressing through URIs, and HATEOAS (hypermedia as the engine of application state). Almost nobody implements that 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 one carries semantics, and those semantics aren't decoration. Proxies, caches, monitoring — they all rely on methods being used correctly. 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 handed for free.

SOAP: The Dinosaur That Will Outlive Us All

In startups, SOAP is a meme. In a bank, it's 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 every new system was obliged to adapt to it. Changing a service that handles millions of transactions a day because "REST is trendier" is an idea 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 a bank, that third point overrides everything else.

GraphQL and JSON-RPC: Niche but Useful

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

JSON-RPC — for when the interaction is best described as "call a function with parameters" rather than "an operation on a resource." Internal service calls, utilities, anything that doesn't map onto REST's resource model.

gRPC: When Milliseconds Matter

gRPC sits in the table, but it deserves its own paragraph — 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 on 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 came in at 2-3 ms. At thousands of calls per second, that's the difference between "works" and "doesn't fit 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 want browser access. But for inter-service communication inside the perimeter — I haven't found a better option yet.

How I Choose a Protocol

Four questions that save weeks of arguing:

Who's the consumer? An external partner — REST, because everyone will understand 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 a single service is madness. If all your microservices are on gRPC — a REST service will look like an outsider.

Who's going to maintain it? REST — easier to find people. GraphQL — you need expertise. SOAP — you need people who aren't scared 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 extra fields." I asked the first question — who's the consumer? Turned out the service had exactly two clients: the mobile app and an internal back office. Both consumed three endpoints each. The upside from GraphQL — zero, while the cost of maintaining a schema, resolvers, and N+1 problems — 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 an 18-wheeler to bring a carton of milk home from the store.

Five API Mistakes I Run Into on Every Project

1. Verbs in the URL

/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 of the developers wrote POST /calculateRisk — and formally that's not a resource operation, it's a procedure. We argued about it for a long time. In the end we decided: POST /risk-assessments — you're creating a "risk assessment" object, and that maps onto the resource model. But the argument was useful — it showed that REST isn't the right fit for every task.

2. Everything Through POST

That very specification from the start of the article. POST for reads, POST for deletes, POST for everything. Why do people do this? Because "POST definitely works through every proxy" and "I don't want to deal with idempotency." A lazy decision that comes back to bite you at every later stage: you can't cache GET requests, you can't safely retry, you can't tell reads from writes in monitoring.

3. 200 OK on Errors

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

I saw 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 its data. We spent a week hunting down why data was disappearing from an analytics dashboard — turned out the upstream service was returning 200 with an error in the body, and our ETL process was treating 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 here we go: 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 hitting a single 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 of them — works fine. In production — a million. One unlucky request, and the service is down under OOM. This happened to us with a customer transactions export endpoint. The developer added it "for now, we'll bolt on pagination later." "Later" showed up in the form of a 3 AM incident.

Bonus: Hands-On Practice with a Telegram Bot

In my systems analysis course, I hand out an assignment: build a Telegram bot on Flask. The Telegram Bot API is a beautifully documented REST API. Flask receives a webhook (POST with JSON), parses the message, handles the command, sends a reply 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 an abstraction, because the bot either works or it doesn't. No "well, it's basically right."

Why You Need to Know This

I had a junior analyst who came in to sign off on an integration with an external partner. The partner had sent over 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. It's just that 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 a response schema isn't a minor detail but an architectural decision.

He ran the meeting with the partner and agreed on the integration "verbally." No questions about date formats, no clarifications on pagination, no discussion of timeout behavior. Two months later, when development began, it turned out half the agreements didn't match what was in the spec. Another month of renegotiation. A classic.

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


Original article: API From A to Z on Habr

Yours, DPUPP

Related Articles