0% found this document useful (0 votes)
2 views38 pages

REST API Methods - Basic Fundamental

The document outlines the six core HTTP methods used in REST APIs, detailing their functions, idempotency, and safety. It also explains status codes, authentication, and authorization, as well as the Richardson Maturity Model for assessing RESTful API design. Additionally, it discusses content negotiation and API versioning strategies, providing practical examples and scenarios for each concept.

Uploaded by

shakibscro
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views38 pages

REST API Methods - Basic Fundamental

The document outlines the six core HTTP methods used in REST APIs, detailing their functions, idempotency, and safety. It also explains status codes, authentication, and authorization, as well as the Richardson Maturity Model for assessing RESTful API design. Additionally, it discusses content negotiation and API versioning strategies, providing practical examples and scenarios for each concept.

Uploaded by

shakibscro
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

REST API Methods — Basic Fundamental

Created by Shakib Ali

The 6 Core HTTP Methods


GET — Retrieve a resource. You're asking the server to send you data. Nothing changes on
the server. If you call GET /users/5 ten times, you get the same user ten times. Nothing is
created, nothing is deleted. Safe and idempotent by nature. Used whenever a browser loads
a page, whenever a frontend fetches a list.

POST — Create a new resource, or trigger an action. You're sending data to the server and
asking it to process it. Every call can produce a different result — call POST /orders twice and
you'll get two separate orders. Not safe, not idempotent. The server decides what to do with
the payload.

PUT — Replace a resource completely. You send the entire object, and the server replaces
whatever was there. Call PUT /users/5 with the same payload twice — the result is the same.
Idempotent. If the resource doesn't exist, many implementations create it.

PATCH — Update part of a resource. You send only the fields that changed. Unlike PUT, this is
a partial update. Whether it's idempotent depends on what the patch does — more on this
below.

DELETE — Remove a resource. Call DELETE /users/5 and the user is gone. Call it again — still
gone. The state of the system after the first call and the tenth call is identical. Idempotent.

OPTIONS — Ask the server what it supports. Browsers send this automatically before cross-
origin requests (the CORS preflight). It asks: "What methods do you allow on this URL?" The
server responds with Allow: GET, POST, PUT, DELETE in the headers. Your frontend code
rarely calls this manually — the browser does it behind the scenes.

HEAD — Same as GET, but returns only headers, no body. Used to check if a resource exists,
or to get its Content-Length before deciding whether to download it. Never has a response
body.

What is Idempotency?
Idempotency means: calling the same operation multiple times produces the same result as
calling it once.

Think of it like a light switch vs. a toggle button.

A light switch with labeled ON and OFF positions is idempotent — press ON five times, the
light is still just ON. A toggle button is not idempotent — press it five times and you don't
know if the light is on or off.
In REST terms: if retrying a request due to a network timeout produces the same server state
as sending it once — that's idempotent. If retrying creates duplicates or causes extra side
effects — that's not idempotent.

Safe is a related concept. A method is safe if it causes no side effects — it only reads, never
writes. Safe methods are always idempotent. Idempotent methods are not always safe
(DELETE changes state but calling it repeatedly is stable).

Which Methods are Idempotent?

Note that PATCH is intentionally excluded from the table above — it deserves its own
explanation.

Why PATCH is not guaranteed idempotent:

It depends on what the patch instruction says. Consider two types of PATCH requests:

PATCH /users/5 with body { "name": "Shakib" } — this is effectively idempotent. Run it
ten times, the name is "Shakib" every time.
PATCH /posts/3 with body { "views": "+1" } (increment views) — this is not idempotent.
Every call increments the counter. 10 calls = 10 extra views.

The HTTP spec does not guarantee PATCH is idempotent — it explicitly leaves it up to the
operation. That's the critical distinction. PUT sends the full replacement object, so
repeating it always produces the same state. PATCH sends a diff, and diffs can be absolute or
relative. Relative diffs are not idempotent.

Which Status Code Returns in Which Condition

This is the most practical section. Every scenario matters.

GET /users — success → 200 OK with the list in the body.

GET /users/5 — user exists → 200 OK. User doesn't exist → 404 Not Found.

POST /users — resource created → 201 Created with the new resource in body and a Location
header pointing to /users/5. Request body is malformed JSON → 400 Bad Request. JSON is
valid but a required field is missing → 422 Unprocessable Entity. A user with that email
already exists → 409 Conflict.

PUT /users/5 — full replace succeeded → 200 OK (if body returned) or 204 No Content (if no
body). Resource didn't exist but was created → 201 Created. Sent the wrong Content-Type →
415 Unsupported Media Type.

PATCH /users/5 — partial update succeeded → 200 OK with updated resource, or 204 No
Content. Resource not found → 404 Not Found. Invalid field values → 422 Unprocessable
Entity.

DELETE /users/5 — deleted successfully → 204 No Content (no body needed). Resource not
found → 404 Not Found. Some APIs return 200 OK with a message body on DELETE — both
are acceptable conventions, but 204 is more common.

Authentication and Authorization:

No token sent at all → 401 Unauthorized ("you haven't told me who you are")
Token sent but the user doesn't have permission → 403 Forbidden ("I know who you are,
but you can't do this")

This distinction is the most commonly confused in interviews. 401 = identity unknown. 403 =
identity known, access denied.

2xx — Success

200 -OK-GET request returned data. PUT/PATCH succeeded and body is returned. DELETE with a message body.

201-Created-POST created a new resource. Include Location header pointing to the new resource. Body should contain the
created object.

204-No Content-DELETE succeeded. PUT/PATCH succeeded with no body to return. Success but nothing to show. Never
has a response body.

202-Accepted-Request received and queued for async processing. Not done yet — job is in progress. Use for long-running
operations like report generation, email sending.
3xx — Redirection

301-Moved Permanently-Resource has permanently moved to a new URL. Browsers and clients should update bookmarks.
Used in domain migrations.

304-Not Modified-Client's cached version is still fresh. Sent in response to conditional GET (If-None-Match / If-Modified-
Since). No body returned.

4xx — Client Errors

400-Bad Request-Malformed JSON, invalid syntax, missing required query params. The request itself is broken — server
can't parse it. General catch-all for bad input.

401-Unauthorized-No authentication provided, or token is expired/invalid. "I don't know who you are." Client must
authenticate first. Often returned with WWW-Authenticate header.

403-Forbidden-Authenticated but not authorized. "I know who you are, but you can't access this." Role/permission denied.
ADMIN endpoint called by USER role.

404-Not Found-Resource doesn't exist at this URL. GET/PUT/DELETE on an ID that doesn't exist in the database. Also used
to hide existence of protected resources.

405-Method Not Allowed-HTTP method not supported on this endpoint. Calling DELETE on /users when DELETE isn't
implemented. Server must return Allow header listing valid methods.

409-Conflict-Resource already exists. POST to create a user with an email that's already registered. Concurrent edit conflict
(optimistic locking). Duplicate unique constraint violation.

415-Unsupported Media Type-Sent wrong Content-Type header. Server expects application/json but received text/plain or
multipart/form-data without support.

422-Unprocessable Entity-Request is syntactically valid JSON but semantically invalid. Age field has value -5. Email field
passes as a string but isn't an email format. Business rule violations. Use this over 400 when you understood the payload but
rejected it for logical reasons.

429-Too Many Requests-Client has exceeded the rate limit. Usually returned with Retry-After header. Common in public
APIs, login endpoints, OTP services.

5xx — Server Errors

500-Internal Server Error-Unhandled exception on the server. NullPointerException, database query failure, unexpected
state. Client did nothing wrong. Generic server crash.

502-Bad Gateway-Your API gateway or reverse proxy (Nginx, AWS ALB) received an invalid response from the upstream
service. The gateway is alive; the backend behind it is not responding properly.

503-Service Unavailable-Server is temporarily down — maintenance window, overloaded, or a circuit breaker tripped.
Expected to be temporary. Clients should retry after Retry-After header value.

504-Gateway Timeout-Proxy/gateway waited too long for the upstream service to respond and gave up. Common in
microservices when one service calls another and it hangs. Feign Client timeout scenario.
The Three Pairs That Get Confused Most in Interviews

400 vs 422 — Both are client errors with a request body. The difference is where the problem
is. 400 means the server couldn't even parse the request — broken JSON, missing Content-
Type, invalid URL structure. 422 means the server parsed it fine, understood it, but the data
inside fails validation — an age of -5, a phone number with letters, a date range where end is
before start. In Spring Boot with @Valid, MethodArgumentNotValidException maps to 400
by default, but many teams remap it to 422 because the semantics fit better.

401 vs 403 — Both block access, but for different reasons. 401 means the server has no
identity for the caller — no token, expired token, invalid token. The fix is to log in. 403 means
the server knows exactly who you are but your role doesn't allow this action. A USER hitting
an ADMIN endpoint gets 403. The fix is not to log in again — the fix is to get a different role or
not make that call.

204 vs 200 on DELETE — Convention, not a hard rule. 204 is more semantically correct
because there's nothing to return after deletion. 200 is acceptable if you want to send a
confirmation message body. Most production APIs use 204 for DELETE. Both are right — but
know that 204 never has a body. If you send a 204 with a body, clients are supposed to
ignore the body.

Practical Scenario Summary

Every operation in my HMS project maps directly to these rules. When a doctor is not found
— 404. When a user tries to access an admin endpoint — 403. When JWT is expired — 401.
When creating a patient with a duplicate record — 409. When the appointment date field has
an invalid format — 422. When the hospital service is down and the gateway waits — 504.

That is the full picture. No memorization list — just understanding the reason behind each
code.

Richardson Maturity Model — All 4 Levels


Roy Fielding defined what "true REST" means. Leonard Richardson broke it down into 4
levels — a ladder showing how RESTful your API actually is.

Level 0 — The Swamp of POX (Plain Old XML)

Your entire API is one single URL. Everything goes to that one endpoint. The HTTP method
doesn't matter — everything is POST.

Real example: Old SOAP-based APIs worked exactly like this.

POST /api
POST /api

POST /api

All three calls go to the same URL. The server figures out what to do by reading the body —
<action>getUser</action>, <action>createOrder</action>. HTTP is just a tunnel here, nothing
more.

Level 1 — Resources

Now you have separate URLs for separate things. No more single endpoint. But you still use
POST for everything — GET, delete, update — all POST.

POST /users ← to get users

POST /users ← to create a user

POST /orders ← to get orders

Right direction — at least resources are separated. But HTTP methods are still ignored.

Level 2 — HTTP Verbs (this is where most APIs sit)

Now you use the correct HTTP method for the correct action. GET to read, POST to create,
PUT to update, DELETE to delete. Proper status codes — 201, 404, 204. This is what you build
in Spring Boot every day.

GET /users ← get all users

POST /users ← create user

GET /users/5 ← get user 5

DELETE /users/5 ← delete user 5

Most production APIs, including my HMS project, are at Level 2. This is considered good
REST by most teams.

Level 3 — HATEOAS (Hypermedia as the Engine of Application State)

This is where the API tells the client what it can do next. Every response includes links — not
just data.

Real example — you call GET /users/5 and the response is:

json

"id": 5,
"name": "Shakib",

"email": "shakib@[Link]",

"_links": {

"self": { "href": "/users/5" },

"update": { "href": "/users/5", "method": "PUT" },

"delete": { "href": "/users/5", "method": "DELETE" },

"orders": { "href": "/users/5/orders" }

The client does not need to know any URLs in advance. The API guides it. Like a website —
you land on a homepage and follow links. You don't type URLs manually.

Why interviewers ask this — they want to know if you understand that most real-world APIs
are Level 2, not Level 3. HATEOAS is rarely implemented fully in practice because it adds
complexity. But knowing the concept and being able to explain the _links block is enough to
impress.

One line answer for interview — Richardson Maturity Model has 4 levels. Level 0 is single
endpoint, Level 1 adds resources, Level 2 adds proper HTTP verbs and status codes — which
is what most production APIs are. Level 3 adds HATEOAS where responses include links to
guide the client's next actions.

Concept 2 — Content Negotiation


The client and server negotiate what format the response should be in. The client says "I
want this format" — the server either agrees and sends it, or says "I can't do that."

The real world analogy

You go to a restaurant and say "can I get this dish without onions?" The waiter either says
yes and brings it without onions, or says "sorry, it comes pre-made, can't change it." That
negotiation between you and the kitchen — that is content negotiation.

How it works technically

The client sends an Accept header in the request telling the server what format it can
handle.

GET /users/5
Accept: application/json

Server reads that header and sends back JSON with Content-Type: application/json.

Now if another client sends:

GET /users/5

Accept: application/xml

The same endpoint, same URL — but now the server sends XML back if it supports it.

If the server cannot produce what the client asked for — it returns 406 Not Acceptable. This
is the status code most freshers don't know exists.

The two headers involved

Accept — sent by the client in the request. This is what I can receive.

Content-Type — sent by both sides. In a request body it means this is what I am sending you.
In a response it means this is what I am sending back.

These two are always confused in interviews. Remember — Accept is about what you want to
receive. Content-Type is about what you are currently sending.

In Spring Boot

When you write @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) —


you are telling Spring this endpoint only produces JSON. If a client sends Accept:
application/xml, Spring returns 406 automatically.

You can also support multiple formats on one endpoint:

java

@GetMapping(

value = "/users/{id}",

produces = { "application/json", "application/xml" }

Same URL, same method — different output based on what the client asked for.

Why interviewers ask this

Because most freshers think one endpoint always returns one format. Content negotiation
shows you understand that HTTP is designed to be flexible — the same resource can be
represented in multiple formats.
One line answer for interview — Content negotiation is the process where the client tells the
server what format it wants using the Accept header, and the server responds in that format
using Content-Type. If the server cannot produce the requested format it returns 406.

Concept 3 — API Versioning Strategies


Why versioning exists at all

Your API is live. Clients — mobile apps, frontend, third party integrations — are consuming it.
Now you need to change a response structure, rename a field, or remove something. If you
change it directly, every existing client breaks. Versioning solves this — old clients keep
using v1, new clients use v2.

4 strategies interviewers ask about

Strategy 1 — URI Versioning

Version number is part of the URL.

GET /api/v1/users

GET /api/v2/users

Most commonly used in real projects. Easy to see, easy to test in browser, easy to document.
Your HMS project should use this.

Downside — URL should ideally represent a resource, not a version. Purists argue this is not
clean REST. But practically, everyone uses it.

Strategy 2 — Request Parameter Versioning

Version is a query parameter.

GET /api/users?version=1

GET /api/users?version=2

Easy to implement. But messy in large APIs — every URL needs the param. Not commonly
used in production.

Strategy 3 — Header Versioning

Version is sent in a custom request header.

GET /api/users

X-API-Version: 1
GET /api/users

X-API-Version: 2

Clean URL — the resource path stays the same. Used by companies like Microsoft. Downside
— not visible in the browser URL, harder to test quickly, harder to document.

Strategy 4 — Accept Header Versioning (Media Type Versioning)

Version is embedded inside the Accept header — also called content negotiation versioning.

GET /api/users

Accept: application/[Link].v1+json

GET /api/users

Accept: application/[Link].v2+json

Most RESTfully correct approach — because the URL represents the resource and the
version is part of the representation format. Used by GitHub's API.

Downside — complex to implement, hard to test in browser, unfamiliar to most junior


developers.

Which one to use — interview answer

Most teams use URI versioning because it is simple, visible, and easy to maintain. Header
versioning is cleaner architecturally. The right answer in an interview is — "it depends on the
team's preference, but URI versioning is the most practical and widely adopted."

In Spring Boot — URI versioning example

You create two separate controllers or two separate methods:

@GetMapping("/api/v1/users")

public List<UserV1Dto> getUsersV1() { ... }

@GetMapping("/api/v2/users")

public List<UserV2Dto> getUsersV2() { ... }


V1 returns the old structure, V2 returns the new one. Both live side by side until V1 is
deprecated.

One line answer for interview — API versioning ensures existing clients don't break when the
API changes. The four strategies are URI versioning, request parameter versioning, header
versioning, and media type versioning. URI versioning is most commonly used in practice.

Concept 4 — Idempotency Keys


The problem it solves first

You already know POST is not idempotent. Now think about this real scenario —

A user clicks Place Order on a payment page. The request goes to the server. The server processes the
payment, charges the card, creates the order — but the response never reaches the client due to a network
timeout. The user sees an error screen. They click Place Order again.

Now the server processes it a second time. The card gets charged twice. Two orders created. The user is
furious.

This is the exact problem idempotency keys solve.

What an idempotency key is

It is a unique ID generated by the client and sent with the request. The server uses this key to recognize — "I
have already processed this exact request before. I will not process it again. I will just return the previous
result."

How it works step by step

Step 1 — Client generates a unique key before sending the request. Usually a UUID.

Step 2 — Client sends the request with the key in a header.

POST /payments

Idempotency-Key: 7f3b2a1c-9d4e-4f8a-b6c2-1a2b3c4d5e6f

Content-Type: application/json

{ "amount": 5000, "userId": 5 }

Step 3 — Server receives the request. It checks its storage — "have I seen this key before?"

If no — process the payment, store the key with the result, return the response.
If yes — do not process again. Just return the previously stored response directly.

Step 4 — Client retries due to timeout. Same key is sent again. Server recognizes it, returns the same result.
No duplicate charge.

Where the key is stored on the server

Usually in Redis or a database table. The key is stored with a TTL — time to live. After 24 hours the key
expires. This prevents the storage from growing forever.

The stored entry looks like:

key: 7f3b2a1c-9d4e-4f8a-b6c2-1a2b3c4d5e6f

result: { "orderId": 1042, "status": "SUCCESS" }

expires: 24 hours

Real companies that use this

Stripe — their payment API requires an Idempotency-Key header on every POST request. If you retry with
the same key within 24 hours, Stripe returns the original response without charging again.

The status code angle

If the client sends the same idempotency key but with a different request body — that is a conflict. Server
returns 422 Unprocessable Entity or 409 Conflict depending on implementation. Same key must always
mean same request.

Why interviewers ask this

Because payment systems, order systems, booking systems — all critical flows — need this. A fresher who
knows idempotency keys signals they think about real production problems, not just CRUD.

One line answer for interview — An idempotency key is a unique ID sent by the client in the request header.
The server uses it to detect duplicate requests and return the original result without processing again. This
prevents double charges or duplicate records in non-idempotent operations like POST.

Concept 5 — Rate Limiting and Throttling


Rate limiting means — the server puts a cap on how many requests a client can make in a given time
window. Once the limit is crossed, the server stops processing and rejects further requests temporarily.

The real world analogy

An ATM allows you to withdraw a maximum of 3 times per day. On the 4th attempt it says — "daily limit
reached, try tomorrow." The ATM is not broken. Your account is fine. You simply crossed the allowed limit.
That is rate limiting.
Why it exists

Without rate limiting three things happen —

One — a single client can flood your server with thousands of requests per second and crash it. This is a
DDoS attack scenario.

Two — one heavy user consumes all server resources, slowing down every other user.

Three — your paid third party services — SMS, email, payment gateway — get called excessively and your
bill explodes.

Rate Limiting vs Throttling — the difference

Most freshers use these words interchangeably. Interviewers notice.

Rate Limiting — hard stop. You crossed the limit, request is rejected. No processing happens. Returns 429
Too Many Requests.

Throttling — soft slow down. You are making too many requests so the server starts processing them
slower — queuing them, adding delays. It does not reject — it slows down. Think of it like a highway with a
speed limit versus a highway that gets congested and everyone slows down naturally.

In practice — rate limiting is what most REST APIs implement. Throttling is more common inside internal
systems and message queues.

How rate limiting works technically

The server tracks requests per client. Client is usually identified by —

IP address — for public APIs.

API key — for developer APIs.

User ID — for authenticated APIs.

Common algorithms used —

Fixed Window — count requests in a fixed time window. Allow 100 requests per minute. Counter resets
every minute. Simple but has a burst problem — 100 requests in the last second of minute 1 and 100 in the
first second of minute 2 means 200 requests in 2 seconds.

Sliding Window — smoother version. Instead of a fixed reset, the window slides continuously. More
accurate, no burst problem.

Token Bucket — the most commonly used algorithm. Imagine a bucket that holds tokens. Every request
consumes one token. Tokens refill at a fixed rate. If the bucket is empty, request is rejected. Allows short
bursts but controls the average rate. Stripe and AWS use this.

The response when limit is crossed


Status code — 429 Too Many Requests.

The server also sends helpful headers —

HTTP/1.1 429 Too Many Requests

Retry-After: 30

X-RateLimit-Limit: 100

X-RateLimit-Remaining: 0

X-RateLimit-Reset: 1700000060

Retry-After — wait this many seconds before retrying. X-RateLimit-Limit — total allowed requests in the
window. X-RateLimit-Remaining — how many you have left. X-RateLimit-Reset — Unix timestamp when the
counter resets.

In Spring Boot

Spring Boot does not have built-in rate limiting. You implement it using —

Bucket4j — most popular Java library. Implements token bucket algorithm. Can store state in Redis for
distributed systems.

API Gateway level — in microservices, rate limiting is usually handled at the API Gateway — Spring Cloud
Gateway, AWS API Gateway, Kong. The individual services don't worry about it.

For your interview — knowing that rate limiting is best handled at the gateway level in microservices is a
strong answer.

Real example interviewers give

You have a forgot password endpoint. How do you prevent abuse?

Answer — rate limit by IP. Allow maximum 5 requests per hour per IP. After that return 429 with Retry-After:
3600. This prevents brute force attacks on the OTP system.

One line answer for interview — Rate limiting restricts the number of requests a client can make in a time
window and returns 429 when exceeded. Throttling slows down processing instead of rejecting. In
microservices, rate limiting is handled at the API Gateway level using algorithms like token bucket.

Concept 9 — Circuit Breaker Pattern


The problem it solves first

In microservices you have multiple services calling each other. Your Order Service calls
Payment Service. Payment Service calls Bank API.
Now imagine the Bank API goes down or becomes very slow — taking 30 seconds to respond.
Every request to Payment Service now hangs for 30 seconds waiting. Order Service is waiting.
Users are waiting. Threads are piling up. Eventually your entire application crashes — not
because of your code, but because one external service is slow.

This is called a cascading failure. One service failing brings down everything connected to it.

Circuit Breaker prevents this.

The real world analogy

Your house has a circuit breaker in the electrical panel. When there is a power surge or short
circuit — the breaker trips and cuts the electricity. It does not let the surge travel through the
wires and burn your appliances. Once the problem is fixed you reset the breaker and
electricity flows again.

Same concept in software — when a downstream service is failing, the circuit breaker trips
and stops sending requests to it. Instead of waiting and crashing, it fails fast and returns a
fallback response immediately.

Three states of a Circuit Breaker

CLOSED — normal working state

Requests flow through normally. The circuit breaker is silently counting failures in the
background. If failures stay below the threshold — everything stays closed. This is the default
state.

OPEN — tripped state


Failures crossed the threshold. Example — 5 consecutive failures or 50% failure rate in 10
seconds. The circuit breaker trips to OPEN.

Now — every incoming request is immediately rejected without even trying to call the
downstream service. It returns a fallback response instantly. No waiting. No hanging threads.
Fast failure.

The fallback could be — a cached response, a default message, an empty list, or a proper error
message like "payment service temporarily unavailable."

HALF-OPEN — recovery testing state

After a configured timeout — say 30 seconds — the circuit breaker moves to HALF-OPEN
automatically. It allows one or a few test requests through to check if the downstream service
has recovered.

If the test request succeeds — circuit moves back to CLOSED. Normal flow resumes.

If the test request fails — circuit goes back to OPEN. Waits another 30 seconds. Tries again.

In Spring Boot — Resilience4j

The library used is Resilience4j. Previously it was Netflix Hystrix but that is now deprecated.
Resilience4j is the current standard.

Configuration in [Link]:

yaml

resilience4j:

circuitbreaker:

instances:

paymentService:

slidingWindowSize: 10

failureRateThreshold: 50

waitDurationInOpenState: 30s

permittedNumberOfCallsInHalfOpenState: 3

slidingWindowSize — track last 10 requests. failureRateThreshold — if 50% fail, trip to OPEN.


waitDurationInOpenState — stay OPEN for 30 seconds then try HALF-OPEN.
permittedNumberOfCallsInHalfOpenState — allow 3 test requests in HALF-OPEN.

In your service class:

java

@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")

public PaymentResponse processPayment(PaymentRequest request) {

return [Link](request);

public PaymentResponse paymentFallback(PaymentRequest request, Exception e) {

return new PaymentResponse("PENDING", "Payment service unavailable. Will retry.");

When circuit is OPEN — paymentFallback is called immediately without touching Payment


Service.

Why interviewers ask this

Because in microservices, inter-service calls are everywhere. Not knowing circuit breaker
means you don't understand how to build resilient systems. In your E-Commerce project —
Order Service calling Payment Service is the perfect real example to give.

One line answer for interview — "Circuit Breaker prevents cascading failures in microservices.
It has three states — Closed where requests flow normally, Open where requests are rejected
fast with a fallback when failure threshold is crossed, and Half-Open where it tests if the
service recovered. In Spring Boot we use Resilience4j to implement this."

Concept 10 — Request/Response Compression (gzip)


The problem it solves first

Your API returns a list of 1000 products. Each product has name, description, price, category, images — the
response JSON is 2MB in size. Every user who loads that page downloads 2MB. On a slow mobile network
that takes 10 seconds. Multiply that by 10,000 users — your bandwidth bill is huge and your app feels slow.

Compression solves this — the server compresses the response before sending it. The same 2MB response
becomes 200KB. 10x smaller. Faster transfer. Less bandwidth cost.
The real world analogy

You need to send 10 books to a friend by courier. Shipping 10 separate books costs a lot. Instead you
vacuum-pack them — same content, much smaller package, cheaper shipping. Your friend opens the
package and gets all 10 books. That is compression.

How it works technically

It follows the same content negotiation pattern you already learned.

The client sends an Accept-Encoding header saying — I can handle compressed responses.

GET /products

Accept-Encoding: gzip, deflate, br

The server compresses the response body using gzip, sends it back with a Content-Encoding header saying
— "I compressed this with gzip."

HTTP/1.1 200 OK

Content-Encoding: gzip

Content-Type: application/json

[compressed binary data]

The client receives the compressed bytes and automatically decompresses them. This all happens
transparently — your frontend code and Postman handle it automatically. You never see the compressed
data manually.

The three encoding types

gzip — most widely used. Good compression ratio. Supported everywhere. This is what you will use 99% of
the time.

deflate — older algorithm. Less common now.

br — Brotli. Newer, better compression than gzip. Used by modern browsers. Not all HTTP clients support it
yet.

Two sides of compression

Response compression — server compresses what it sends back. This is the common one. Reduces
response size.
Request compression — client compresses what it sends to the server. Used when uploading large
payloads — bulk data imports, file uploads. Less common. Client sends Content-Encoding: gzip header with
compressed body.

In Spring Boot

Spring Boot makes this extremely simple. One property in [Link] enables it:

properties

[Link]=true

[Link]-types=application/json,application/xml,text/plain

[Link]-response-size=1024

enabled=true — turn on compression. mime-types — only compress these content types. No point
compressing images — they are already compressed. min-response-size=1024 — only compress responses
larger than 1KB. Small responses don't benefit from compression — the overhead of compressing is more
than the saving.

That is all. Spring Boot handles everything else automatically using the embedded Tomcat server.

When NOT to compress

Images — JPEG, PNG, WebP are already compressed formats. Compressing them again gives almost no
benefit and wastes CPU.

Already encrypted data — compressed encrypted data does not compress further.

Very small responses — responses under 1KB are not worth compressing. The compression overhead costs
more than it saves.

The performance numbers — why it matters

Typical JSON compression ratios with gzip —

Plain text JSON — compresses to about 10-20% of original size. A 1MB JSON response becomes roughly
100-200KB. This is because JSON has a lot of repetition — field names repeat on every object in an array.
gzip is extremely good at compressing repetitive text.

Interview scenario they give

Your API is slow on mobile networks. How do you optimize it?

A complete answer covers three things — enable gzip compression, add pagination so you don't return
1000 records at once, and add caching. Knowing all three together makes a strong answer.
One line answer for interview — gzip compression reduces response size by up to 90% for JSON data. The
client sends Accept-Encoding: gzip, the server compresses the body and responds with Content-Encoding:
gzip. In Spring Boot it is enabled with a single property — [Link]=true.

Concept 11 — Long Polling vs Webhooks vs SSE


The problem all three solve

Normal REST is request-response. Client asks, server answers, connection closes. Done.

But what if the client needs to know when something happens on the server — without asking every time?
Examples —

A food delivery app showing live order status. A notification bell showing new alerts. A stock price updating
in real time. A chat message arriving instantly.

These are server to client push scenarios. Normal REST cannot do this efficiently. These three techniques
solve it in different ways.

Technique 1 — Short Polling (baseline — not in your list but needed for comparison)

Client asks the server every few seconds — anything new?

Client → GET /notifications/new → nothing

Client → GET /notifications/new → nothing

Client → GET /notifications/new → you have 1 new message

Simple but wasteful. 99% of requests return nothing. Wastes bandwidth, wastes server resources. Like
texting someone "are you there?" every 5 seconds.

Technique 2 — Long Polling

Client sends a request. Server does NOT respond immediately. It holds the connection open and waits until
something actually happens — then responds.

Client → GET /notifications/new

Server holds connection open...

Server holds connection open...

[new notification arrives on server]

Server → responds with the notification

Connection closes.
Client immediately sends another long poll request.

Better than short polling — no empty responses. But still request-response underneath. Every response
closes the connection and client must reconnect immediately.

Real analogy — you call a restaurant to ask if your table is ready. Instead of saying "call back in 5 minutes"
they say "stay on hold, we'll tell you the moment it's ready." One call, one answer, then you hang up and
call again for the next wait.

When used — when WebSockets are not available or too complex. Simple notification systems. Chat
applications before WebSockets became standard.

Downside — server holds thousands of open connections waiting. High memory usage. Not scalable for
very high traffic.

Technique 3 — SSE (Server-Sent Events)

Server holds the connection open and keeps streaming events to the client as they happen. Connection
stays open permanently. Server pushes whenever it wants. Client just listens.

Client → GET /notifications/stream

Server → keeps connection open

Server → sends event: order placed

Server → sends event: order confirmed

Server → sends event: out for delivery

Server → sends event: delivered

[connection stays open the whole time]

One connection, multiple events. Unlike long polling — connection does not close after each event.

Key characteristics —

One direction only — server to client. Client cannot send data back on the same connection.

Uses regular HTTP. Works through firewalls and proxies easily.

Browser handles reconnection automatically if connection drops.

Real analogy — subscribing to a YouTube channel's live stream. You connect once and the stream keeps
coming to you. You don't reconnect for each video frame.

When used — live notifications, real-time dashboards, live score updates, order tracking, stock price feeds.

In Spring Boot —
@GetMapping(value = "/notifications/stream”,

produces = MediaType.TEXT_EVENT_STREAM_VALUE)

public SseEmitter streamNotifications() {

SseEmitter emitter = new SseEmitter();

// push events to emitter whenever something happens

return emitter;

TEXT_EVENT_STREAM_VALUE is the content type for SSE. Spring Boot has built-in SseEmitter support.

Technique 4 — Webhooks

Completely different direction. Instead of the client asking the server — the server calls the client when
something happens.

You register a URL with the server — "when something happens, POST to this URL." The server stores your
URL. When the event occurs, the server makes an HTTP POST request to your URL with the event data.

You → register: "notify me at [Link]

[payment happens on Stripe]

Stripe → POST [Link] { "event": "[Link]", "amount": 5000 }

Your server receives that POST and handles it.

Real analogy — instead of you calling the bank every day to check if your salary arrived — you give the bank
your phone number and they call you the moment it arrives.

When used — payment notifications (Stripe, Razorpay), GitHub events (push, PR, merge), SMS delivery
receipts, email open tracking. Any scenario where two separate servers need to communicate on events.

Downside — your server must be publicly accessible. Your URL must be live and reachable. If your server is
down when the webhook fires, you miss the event — so you need retry logic on the sender side.
Technique Direction Connection Best for Scalability

Short Polling Client → Server Opens and Simple, low- Poor

closes frequency
repeatedly checks

Long Polling Client → Server Held open until Simple real-time, Medium

event, then no WebSocket


closes

SSE Server → Client Stays open, Live feeds, Good

only streams notifications,


continuously dashboards

Webhooks Server → Server New connection Payment events, Good

per event GitHub triggers

WebSockets Both directions Persistent full- Chat, gaming, Best

duplex collaborative
tools

Note WebSockets is added in the table for completeness — it is the full-duplex upgrade beyond SSE, used
for chat and gaming where both sides send data simultaneously.

Interview scenario they give

How would you implement real-time order tracking in your E-Commerce project?

Strong answer — "When an order status changes, the backend pushes an update to the frontend using SSE.
The frontend connects once to GET /orders/{id}/stream and receives status updates as they happen —
confirmed, packed, shipped, delivered — without polling repeatedly."

One line answer for interview — Long polling holds the connection until an event occurs then closes. SSE
keeps the connection open and streams multiple events from server to client over HTTP. Webhooks are
server-to-server — one server registers a URL and the other POSTs to it when events happen. SSE is best
for live UI updates, Webhooks for backend-to-backend event notification.

Concept 12 — Soft Delete vs Hard Delete


Hard Delete — The record is permanently removed from the database. DELETE FROM users WHERE id = 5.
Gone forever. No recovery.
Soft Delete — The record stays in the database but is marked as deleted. A flag like is_deleted = true or a
deleted_at timestamp is set. The record exists but is treated as if it does not exist by the application.

The real world analogy

Hard delete is throwing a document into a shredder. Gone permanently.

Soft delete is moving a document to the recycle bin. It looks deleted to the user. But it is still there. You can
restore it if needed.

Why Soft Delete exists — real reasons

Reason 1 — Audit trail

In banking, healthcare, legal systems — you legally cannot delete records. If a patient record is deleted and
there is a court case, you are in trouble. Soft delete keeps the record while hiding it from normal usage.

Reason 2 — Accidental deletion recovery

A user deletes their account by mistake and contacts support. With hard delete — impossible to recover.
With soft delete — support sets is_deleted = false and the account is back.

Reason 3 — Foreign key integrity

Your orders table has a user_id foreign key. If you hard delete the user, all their orders lose their reference
— your database throws a foreign key constraint violation or you end up with orphaned records. Soft
delete avoids this entirely.

Reason 4 — Reporting and analytics

Business teams need historical data. "How many users registered last year including those who later
deleted their accounts?" Hard delete makes this impossible. Soft delete keeps the data available for
reports.

How soft delete is implemented

Two common approaches —

Approach 1 — Boolean flag

ALTER TABLE users ADD COLUMN is_deleted BOOLEAN DEFAULT FALSE;

When deleted — set is_deleted = true. Every query adds WHERE is_deleted = false.

Approach 2 — Timestamp (better approach)

ALTER TABLE users ADD COLUMN deleted_at TIMESTAMP NULL;

When deleted — set deleted_at = current timestamp. NULL means active. Non-null means deleted. This
approach also tells you when it was deleted — more useful for auditing.
The REST API implications — what interviewers actually want to know

This is where most freshers miss the point. The interviewer is not asking about database design. They want
to know how soft delete affects your REST API behavior.

DELETE endpoint behavior

With soft delete your DELETE /users/5 endpoint does not actually delete from the database. It sets
deleted_at = now(). Returns 204 No Content — same as hard delete from the client's perspective. The client
does not know or care whether it was hard or soft deleted.

GET endpoint behavior

GET /users/5 on a soft deleted user — should return 404 Not Found. Even though the record exists in the
database, it is logically deleted. The API must behave as if it does not exist.

GET /users — must filter out soft deleted records. WHERE deleted_at IS NULL.

409 Conflict scenario

User is soft deleted. Someone tries to register with the same email. Your unique constraint on email will
pass — the old record exists with that email. You need to handle this in your service layer — check if a soft
deleted record exists with that email and decide whether to reactivate it or reject the new registration.

In Spring Boot with JPA — two ways

Manual approach — add deleted_at field to entity, filter in every query manually.

@Query("SELECT u FROM User u WHERE [Link] IS NULL AND [Link] = :id")

Optional<User> findActiveById(@Param("id") Long id);

Using @SQLRestriction (Spring Boot 3) or @Where (older versions)

@Entity

@SQLRestriction("deleted_at IS NULL")

public class User {

@Column(name = "deleted_at")

private LocalDateTime deletedAt;

Now every JPA query on User automatically adds deleted_at IS NULL condition. You never accidentally
fetch deleted records. Clean and safe.

The soft delete method in service —


java

public void deleteUser(Long id) {

User user = [Link](id)

.orElseThrow(() -> new ResourceNotFoundException("User not found"));

[Link]([Link]());

[Link](user);

Hard delete — when is it actually correct

Not everything should be soft deleted. Use hard delete for —

Temporary data — OTP records, session tokens, password reset tokens. These should be hard deleted after
use.

GDPR compliance — when a user formally requests data erasure under GDPR, you must hard delete their
personal data. Soft delete does not satisfy GDPR right to erasure.

Log and audit entries — these should never be deleted at all, soft or hard.

Interview scenario they give

"A user deletes their account. Six months later they want to come back and register with the same email.
How do you handle this?"

Strong answer — With soft delete the record exists with deleted_at set. When the same email tries to
register, the service checks for a soft deleted record with that email. If found within a grace period — say
30 days — we reactivate the account. If it has been longer we can either reactivate or create fresh based on
business rules. This is much cleaner than hard delete where the email is simply free to reuse but all history
is lost.

One line answer for interview — Soft delete marks a record as deleted using a flag or timestamp without
removing it from the database, preserving audit trail and referential integrity. Hard delete permanently
removes the record. From the REST API perspective the behavior is identical to the client — DELETE
returns 204 and GET returns 404 — but internally the data is retained for auditing and recovery.

Concept 13 — Partial Response (Field Filtering with ?fields=)


The problem it solves first

Your GET /users/5 returns a full user object —


json

"id": 5,

"name": "Shakib",

"email": "shakib@[Link]",

"phone": "9876543210",

"address": "Hyderabad",

"profilePicture": "[Link]

"dateOfBirth": "1998-01-15",

"createdAt": "2024-01-01",

"lastLogin": "2026-06-10",

"preferences": { ... },

"roles": [ ... ]

Now your mobile app only needs id, name, and profilePicture to show a user card. But you are
downloading the entire object — phone, address, DOB, preferences, roles — all of it. Wasted
bandwidth. Slower response. Worse performance on mobile.

Partial response lets the client say — "give me only the fields I need."

The real world analogy

You go to a buffet. The waiter does not decide what goes on your plate and bring you
everything available. You walk along and pick only what you want. Partial response is the
same — the client picks only the fields it needs instead of receiving everything.

How it works

The client sends a fields query parameter listing the fields it wants —

GET /users/5?fields=id,name,profilePicture

Server returns only those fields —


json

"id": 5,

"name": "Shakib",

"profilePicture": "[Link]

Same endpoint, same URL base, different response shape based on what the client asked for.

Real companies that use this

Google APIs — almost every Google API supports ?fields= parameter. Google Drive API, Gmail
API, YouTube Data API — all of them.

GET [Link]

Returns only id, name, size for each file — not the 30+ fields available.

Facebook Graph API — uses the same pattern extensively.

GET /me?fields=id,name,email

This pattern is so common it has a name — sparse fieldsets. The JSON:API specification
formalizes it as ?fields[resource]=field1,field2.

Why this matters in microservices

In your E-Commerce microservices project — Order Service needs user data to show on an
order. But it only needs userId, name, and email. It does not need address, preferences, roles.

Without partial response — Order Service calls GET /users/5 and downloads everything.
Wasteful network call between services.

With partial response — Order Service calls GET /users/5?fields=id,name,email. Lightweight


call. Faster inter-service communication.

In Spring Boot — how to implement it

There is no built-in Spring annotation for this. You implement it manually in the service or
controller layer.

Approach 1 — Using a Map instead of a DTO


java

@GetMapping("/users/{id}")

public ResponseEntity<Map<String, Object>> getUser(

@PathVariable Long id,

@RequestParam(required = false) String fields) {

User user = [Link](id);

if (fields == null) {

return [Link]([Link](user));

Set<String> requestedFields = [Link]([Link](","));

Map<String, Object> fullMap = [Link](user);

Map<String, Object> filtered = [Link]().stream()

.filter(entry -> [Link]([Link]()))

.collect([Link]([Link]::getKey, [Link]::getValue));

return [Link](filtered);

Approach 2 — Using Jackson's @JsonFilter

Jackson has a built-in @JsonFilter mechanism that lets you dynamically include or exclude
fields during serialization. More powerful but more setup.

java
@JsonFilter("userFilter")

public class UserDto { ... }

Then in the controller you build a FilterProvider based on the requested fields and apply it to
the ObjectMapper. Spring Boot handles the rest.

For interviews — knowing Approach 1 conceptually is enough. Very few interviewers go deep
into Jackson filter implementation at fresher level.

Nested field filtering

Some APIs support dot notation for nested objects —

GET /users/5?fields=id,name,[Link],[Link]

Returns —

json

"id": 5,

"name": "Shakib",

"address": {

"city": "Hyderabad",

"pincode": "500001"

This is more complex to implement and usually only needed in large APIs with deeply nested
responses.

Partial response vs DTO

A common interview follow-up — why not just create separate DTOs for each use case?

Answer — DTOs are a compile-time solution. You create UserSummaryDto, UserDetailDto,


UserCardDto — and every new use case needs a new DTO. Partial response is a runtime
solution — one endpoint, client decides what it needs. For APIs consumed by many different
clients — mobile, web, third party — partial response is far more flexible. DTOs are better for
internal, controlled use cases.
Performance angle — why interviewers care

Three things together make a REST API performant on mobile and slow networks —

One — Pagination, so you don't return 1000 records. Two — Compression, so responses are
smaller. Three — Partial response, so unnecessary fields are not transferred.

Knowing all three together and connecting them in one answer makes a strong impression.

One line answer for interview — Partial response allows clients to request only specific fields
using a query parameter like ?fields=id,name,email. The server filters the response to include
only those fields. This reduces payload size, improves performance on mobile networks, and
reduces bandwidth in inter-service communication. Used extensively by Google and
Facebook APIs.

Concept 14 — REST vs HTTP, The Distinction Most Freshers


Miss
The most common fresher mistake

Almost every fresher in an interview says — REST API uses HTTP. That is correct. But when asked is REST
the same as HTTP? or can you build a REST API without HTTP? — most go blank or say no.

This is the concept that separates someone who has just used REST from someone who actually
understands it.

Simple definition of each

HTTP — is a communication protocol. It defines how data is transferred between client and server over a
network. It specifies the format of requests and responses — methods, headers, status codes, body. It is a
set of rules for how to talk.

REST — is an architectural style. A set of design constraints and principles for building networked
applications. It was defined by Roy Fielding in his PhD dissertation in 2000. REST tells you how to design
your system — not how to transfer data.

The real world analogy

HTTP is like the road system — lanes, traffic signals, speed limits, rules of driving. It defines how vehicles
move.

REST is like urban planning principles — roads should connect to destinations logically, intersections
should be clearly marked, one-way streets should make sense. These are design guidelines for how the city
is laid out.
You can have roads without good urban planning. You can have good urban planning on any type of road.
They are separate concerns.

REST is not tied to HTTP

This is the key insight. REST is a set of constraints. Those constraints can be applied over any protocol —
HTTP, HTTPS, CoAP (used in IoT devices), even AMQP in theory.

HTTP happens to map beautifully to REST constraints which is why they are almost always used together.
But REST does not require HTTP.

Similarly — HTTP does not require REST. You can use HTTP in a completely non-RESTful way. SOAP uses
HTTP but is not REST. GraphQL uses HTTP but is not REST.

The 6 REST Constraints — what actually makes something REST

Roy Fielding defined 6 constraints. If your API follows these — it is RESTful. If it does not — it is just an HTTP
API, not a REST API.

Constraint 1 — Client-Server separation

Client and server are separate. Client handles UI. Server handles data and business logic. They
communicate only through the interface. Neither knows about the internal implementation of the other.

Why it matters — you can completely rewrite your Spring Boot backend and the React frontend does not
change, as long as the API contract stays the same.

Constraint 2 — Statelessness

Every request from client to server must contain all the information needed to understand and process it.
The server stores no session state between requests.

Why it matters — this is why JWT exists. The token carries all user information in every request. The server
does not remember the previous request.

Constraint 3 — Cacheability

Responses must define themselves as cacheable or non-cacheable. If cacheable, the client or intermediary
can reuse the response for equivalent future requests.

Why it matters — Cache-Control headers, ETags, Last-Modified headers all exist because of this constraint.

Constraint 4 — Uniform Interface

This is the most important constraint. Four sub-rules —

Resource identification in requests — use URIs to identify resources. /users/5 not /getUser?id=5.

Resource manipulation through representations — client manipulates resources through the


representations it receives. You get a JSON object, modify it, send it back.
Self-descriptive messages — each message includes enough information to describe how to process it.
Content-Type: application/json tells the receiver exactly how to parse the body.

HATEOAS — responses include links to related actions. This is Level 3 of Richardson Maturity Model which
you already know.

Constraint 5 — Layered System

Client does not know if it is connected directly to the server or through intermediaries — load balancer, API
gateway, cache server, security layer. Each layer only knows about the layer immediately next to it.

Why it matters — this is why you can put an Nginx reverse proxy, an AWS load balancer, and a Spring Cloud
Gateway in front of your service and the client never knows or cares.

Constraint 6 — Code on Demand (optional)

Server can send executable code to the client — JavaScript for example. This is the only optional
constraint. Most REST APIs do not implement this.

So what is most of what we build — really?

Honestly — most real-world APIs including yours are not fully RESTful. They are REST-like or RESTful-ish.
They follow Constraints 1, 2, 3, 4 partially, 5 — but almost none implement full HATEOAS. Roy Fielding
himself has written blog posts criticizing APIs that call themselves REST but do not implement HATEOAS.

In practice the industry accepts Level 2 Richardson APIs as REST APIs even though they technically are not
fully REST. This is fine — knowing the distinction is what matters.

The interview question that catches everyone

"Is GraphQL a REST API?"


Wrong answer — yes because it uses HTTP.

Correct answer — "No. GraphQL uses HTTP as a transport but it does not follow REST constraints. GraphQL
uses a single endpoint POST /graphql for all operations — which violates the uniform interface constraint of
REST. It is its own query language and architectural approach, not REST.

Same for SOAP — uses HTTP but is not REST.

One line answer for interview — HTTP is a communication protocol that defines how data is transferred —
methods, headers, status codes. REST is an architectural style defined by 6 constraints — statelessness,
client-server separation, uniform interface, cacheability, layered system, and optional code on demand.
REST most commonly uses HTTP but is not tied to it. SOAP and GraphQL also use HTTP but are not REST
because they do not follow REST constraints.

Concept 15 — Statelessness — What It Actually Means in Practice

The server does not remember anything about the client between requests. Every single
request must carry all the information the server needs to process it. The server treats every
request as if it is seeing that client for the very first time.

The real world analogy

Two scenarios at a hospital —

Stateful — you visit a doctor. The doctor remembers you from last time. You say the pain is
still there. The doctor knows which pain you mean, checks your previous prescription,
continues from where you left off. The doctor holds your context in memory.

Stateless — you call a hospital helpline. Every time you call you get a different operator. They
have no memory of your previous call. Every time you must say your full name, date of birth,
symptoms, previous medications — everything from scratch. The operator does not store
your context between calls.

REST servers work like the helpline — no memory between calls.

What stateful looked like before REST

Old web applications used server-side sessions. When you logged in —

Server created a session object in memory — { userId: 5, role: ADMIN, cart: [...] }. Server gave
you a JSESSIONID cookie. Every subsequent request sent that cookie. Server looked up your
session in memory using that ID.

This worked fine for one server. But in modern systems you have multiple server instances
behind a load balancer. Request 1 goes to Server A — session stored there. Request 2 goes to
Server B — Server B has no session. You are suddenly logged out.

Solutions like sticky sessions and session replication were hacks. Messy, hard to scale.

How statelessness solves this

With stateless REST — the server stores nothing. The client sends everything the server needs
in every request.

Your JWT token is the perfect example —

GET /orders

Authorization: Bearer
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzaGFraWIiLCJyb2xlIjoiVVNFUiIsImV4cCI6MTcwMDAwMH0.
abc123

That token contains — who you are, your role, when it expires. The server does not look up a
session. It decodes the token, extracts the information, processes the request. Done.

Request 1 goes to Server A — works fine. Request 2 goes to Server B — works exactly the same.
Request 3 goes to Server C — same.

No shared state between servers. Any server can handle any request. This is why
statelessness is the foundation of horizontal scaling.

What the client is responsible for storing

Since the server stores nothing — the client must store everything needed for future
requests.

Authentication state — JWT token stored in localStorage or httpOnly cookie. User preferences
— stored client side or sent with each request. Shopping cart — either in client storage or in a
database (not server memory). Pagination position — client tracks which page it is on and
sends ?page=3 with each request.

The common confusion — databases are not state

Freshers often ask — "if the server stores data in a database, is that not state?"

This is an important distinction —

Session state — temporary, request-specific, in-memory data that tracks where a client is in a
conversation with the server. This is what statelessness prohibits.
Resource state — permanent data stored in a database — users, orders, products. This is not
session state. Every REST server stores resource state. Statelessness does not prohibit this.

The rule is — the server must not store anything in memory between requests that is specific
to one client's ongoing conversation. Database records are not that — they are the actual
resources your API manages.

Practical implications in Spring Boot

Because of statelessness your Spring Security configuration explicitly disables sessions —

[Link](session ->

[Link]([Link])

);

This tells Spring — do not create HttpSession, do not store anything in session, do not issue
JSESSIONID cookies. Every request is authenticated independently via JWT. This is exactly
what you implemented in your HMS project.

Where statelessness creates challenges

Statelessness is great for scaling but creates real problems you must know —

Challenge 1 — Token invalidation

You cannot invalidate a JWT before it expires because the server stores nothing. A user logs
out — but their token is still valid until expiry. Someone steals that token — they can use it
until it expires.

Solutions — short expiry times (15 minutes) with refresh tokens, or maintain a token blacklist
in Redis (technically a minor violation of pure statelessness but accepted in practice).

Challenge 2 — Larger request size

Every request carries the full token. In session-based auth a session ID cookie is just a short
string. A JWT is much larger. On high-traffic APIs this adds up.

Challenge 3 — Cannot push server-initiated updates easily

Since the server holds no client context it cannot say remind this client about something.
This is why SSE and Webhooks — which you just learned — exist to bridge this gap.

The interview question that catches people


Your application has a shopping cart. User adds items, navigates pages, then checks out. How
do you maintain cart state in a stateless REST API?

Wrong answer — store it in server session.

Strong answer — Three options depending on requirements. One — store cart in the database
linked to the user ID, fetched with each request using JWT authentication. Two — store cart in
client-side storage like localStorage and send the full cart in the checkout request. Three — a
hybrid — persist to database for logged-in users, localStorage for guests, merge on login.
Option one is most common in production because it persists across devices and sessions.

Stateful (session-based)

Auth-JSESSIONID cookie, session in server memory

Scaling-Hard — sessions tied to one server instance

Server load-Higher — must store session per user

Logout-Instant — just invalidate session

Used in-Old MVC apps, monoliths, traditional JSP apps

Stateless (REST + JWT)

Auth-JWT token sent with every request in header

Scaling-Easy — any server handles any request

Server load-Lower — no session storage needed

Logout-Complex — token valid until expiry

Used in REST APIs, microservices, modern Spring Boot

One line answer for interview — Statelessness means the server stores no client session
between requests. Every request must carry all information needed to process it —
authentication, context, parameters. JWT implements this by embedding user identity in the
token itself. Statelessness enables horizontal scaling because any server instance can handle
any request without shared session storage.

That completes all 12 concepts you asked for. Here is the full list of what we covered —

1. HATEOAS and Richardson Maturity Model


2. Content Negotiation
3. API Versioning strategies
4. Idempotency Keys
5. Rate Limiting and Throttling
6. Circuit Breaker Pattern
7. Request/Response Compression
8. Long Polling vs Webhooks vs SSE
9. Soft Delete vs Hard Delete
10. Partial Response with field filtering
11. REST vs HTTP distinction
12. Statelessness in practice

The three maybe already knew — ETags (6), Pagination patterns (7), API Gateway (8) — are still
available if you want a clarity session on any of them. Just say the number.

Continue in Part 2. created by Shakib Ali

You might also like