skip to content

Return Value Mapping

How a handler's return value becomes a response: object to serialized body, view name to page, status inferred from type, void to no content, or wrapper. Interviewers ask where the framework guesses.

on this pageshow

questions

5

In a server-side web framework, how does an object returned from a handler become a response body and a status code?

level: juniorimportance: must knowfreq 68%

answer

  1. the return value is a body model
  2. somebody had to pick the status
  3. writer, status, entity headers are supplied
  4. 200 is a default, not a decision
  5. absence is where the guess breaks

basics

~20 s

The returned value models the body, not the whole response. The framework picks a writer for the negotiated media type, serializes the value, synthesises Content-Type and framing, then applies a default success status, conventionally 200.

solid answer

~40 s

A handler returns a value; the framework runs a **return-value mapping** step that turns it into a response. It selects a writer for the negotiated media type, asks it to serialize the value into the body bytes, and then fills in everything the handler never named: `Content-Type`, the length or chunked framing, and a status. Because a value that produced a body is a success with content, that status is conventionally `200`. The important part for an interview is that `200` is a **default, not a decision** — nothing in the returned type says the operation succeeded, so if the handler needs another status or an extra header it must say so by returning a wrapper that carries status and headers, or by setting them on the response before returning.

go deeper

for a junior

Remember the chain: return a value, the framework serializes it into the body and fills in the status. Be able to say that the success status was a default nobody explicitly chose.

for a middle

Explain the three things supplied for you — writer selection, status, entity headers — and name the cases where the default is wrong, especially an absent value shipping as an empty success.

for a senior

Show that you audit endpoints for silent defaults: absence mapped to success, error branches inheriting the happy-path status, and serialization work that can fail after the status is already fixed.

for a principal

Frame it as a contract question: every endpoint whose status is inferred is a contract asserted by omission, so the tradeoff is terse handlers against a response contract nobody wrote down.

## The return value is a model, not a response Most server-side web frameworks let a handler be written as an ordinary function: it takes bound arguments and **returns a value**. That value is not the HTTP response. It is a *model of the response body*. Between the handler's `return` and the first byte on the socket, the framework runs a step usually called **return-value mapping** (or result handling): it inspects the value and often its declared type, picks something that can write it, and then fills in every part of the response the handler left unmentioned. This is the single biggest convenience a framework offers over writing raw HTTP, and it is also where it **guesses** on your behalf — which is exactly why interviewers ask about it. ## The three decisions the framework makes for you 1. **Which writer serializes the value.** Frameworks keep a registry of writers (serializers/renderers) and choose one for the value being returned. *Which* writer wins is the content-negotiation rule — the route's declared produced types matched against the request's `Accept` header — and that selection is a topic of its own. 2. **What status to use.** The handler named none, so a default applies. A value that produced a body is treated as a success with content, so the default is conventionally `200`. 3. **Which entity headers to synthesise.** `Content-Type` comes from the negotiated media type, and the transport framing — `Content-Length` for a fully buffered body, chunked framing otherwise — comes from the bytes the writer produced. ## How common return shapes map | What the handler returns | Typical body | Typical status | |---|---|---| | An object or record | serialized per the negotiated media type | `200` | | A collection | serialized sequence; an empty one is usually an empty array, not an empty body | `200` | | An absent/empty optional or a null reference | framework-dependent: some serialize a null literal, some send an empty body, some map declared absence to `404` | `200` or `404` | | A plain string | either the body text or a view name, depending on how the handler is registered | `200` | | Nothing at all | empty | `200` or `204`, framework-dependent | | A wrapper carrying status, headers and body | the wrapper's body | the wrapper's status | ## Where the inference goes wrong - **Absence.** Returning nothing found as a null or empty optional is the classic silent bug: the caller gets `200` with an empty or null-literal body instead of the `404` the API contract promised. Frameworks differ sharply here, so a handler that cares must be explicit. - **Empty collections.** An empty list is a legitimate `200` with `[]`. Do not let a reviewer talk you into `404` for it; "no matching items" is a successful query. - **Accidentally empty bodies.** If the returned type exposes nothing the writer can see, the body serializes to an empty structure and still ships as `200`. The status says success and the body says nothing. - **Error paths.** A handler that catches an exception and returns a partially built value gets the same `200` default as the happy path. The failure disappears into a success. - **Work done at write time.** The writer runs *after* the handler returns. If serializing the value needs resources that were scoped to the handler, it fails at write time — after the status has already been chosen. ## Taking the decision back When the inferred mapping is not what you want, frameworks give you two escapes, and both are ordinary: - **Return a wrapper type** that carries status, headers and body together. The wrapper is still just a return value, so the handler stays a function and stays easy to test. - **Mutate the response** before returning — set the status or a header on the response object the framework handed you, then return the body model. The first composes better because status and body travel as one value; the second is convenient for a header that applies regardless of which branch produced the body. ## What a good answer sounds like Say plainly that the handler returns a **body model**, that the framework supplies **writer, status and entity headers**, and that `200` is a default filled in because nothing else was stated. Then name one case where the default is wrong — absence is the best one — and how you would override it. That is the whole question: knowing which parts of the response nobody actually chose.

  • A read endpoint returns an absent value when the record does not exist and the client sees status 200 with an empty body. What happened?
    Nothing mapped absence to a failure. The framework saw a value to write, ran a writer over it and applied the default success status, so `200` was filled in for you. The fix is to make absence explicit in the handler — return a wrapper carrying `404`, or use whatever absence-aware result type the framework maps to a not-found response rather than relying on the null-to-body default.
  • Why does the same handler return the same object but a different body shape on two requests?
    Because the writer, not the return type, decides the bytes. Two requests that negotiate different media types select different writers over the same value, and a writer's own configuration — field naming, null handling, date format — changes the output. The return type fixes *what* is written, never *how* it is encoded.
  • If serialization throws while writing the body, what has the client already received?
    Possibly the status line and headers. The status was chosen before the writer ran, so once the first bytes are flushed the framework can no longer replace a `200` with a `500`; it can only abort the body, which the client sees as a truncated or malformed payload. That is why writers should not run logic that can realistically fail.

saying these in an interview costs you the question

  • Thinks the handler itself writes the body bytes
  • Says the returned class picks the media type instead of negotiation
  • Assumes returning an absent value automatically produces 404
  • Believes 200 is asserted by the framework rather than defaulted
  • Treats an empty collection as a missing resource
  • Expects a serialization failure to still become a clean 500
open as a page

When should a handler stop returning a plain object and return an explicit response wrapper or a redirect result instead?

level: seniorimportance: must knowfreq 52%

basics

~20 s

Switch to a wrapper as soon as anything beyond the body varies: a status the default cannot reach, a header derived from the result, or several outcomes from one handler. A redirect result packages status and target together.

open as a page

What does a web framework send when a handler returns nothing, and how does it tell that from a handler that already wrote the response?

level: middleimportance: should knowfreq 45%

basics

~20 s

With no returned value there is no body model to serialize, so the framework sends an empty response: some frameworks use status 204, others an empty 200. Mapping is skipped entirely when the response is already marked handled.

open as a page

When a handler returns a plain string, how does a web framework decide whether that is the response body or a view name?

level: middleimportance: should knowfreq 48%

basics

~20 s

The handler's registration decides, not the string's contents: a page-oriented handler treats it as a view name for the template layer, a data-oriented one writes it as the body. Declare the shape and the ambiguity disappears.

open as a page

How would you set a codebase-wide convention for handler return types so response mapping stays predictable across hundreds of endpoints?

level: principalimportance: nice to knowfreq 36%

basics

~20 s

Pick a default and make deviation visible: plain returns where only the body varies, a wrapper wherever status or headers do, error paths always explicit. Then enforce it mechanically and re-check on every framework upgrade.

open as a page