In openapi-generator, what do you get from a server stub versus a client SDK for the same spec?
answer
- -g picks the side and the language
- Stub receives calls; SDK makes them
- Both share the model layer
- operationId names the method
- Tags decide the class it lands in
basics
~20 sBoth derive models from the spec's schemas, but a server generator emits routing or controller interfaces you implement, while a client generator emits a ready-to-call HTTP client. The generator is chosen with -g, and operationId and tags determine method and class names.
solid answer
~50 sYou run `openapi-generator-cli generate -i openapi.yaml -g <generator> -o <dir>`, and `-g` decides which side you get. A **server** generator produces the incoming edge: model classes plus interfaces or abstract controllers with routes, parameter binding and validation already wired — you implement the method bodies, and the generated code is deliberately not your business logic. A **client** generator produces the outgoing edge: the same models plus a class per tag whose methods build the request, serialise the body, send it and deserialise the response. Both derive names from the document: `operationId` becomes the method name (and a missing one gets a synthesised name from the verb and path), the first `tag` decides which API class an operation lands in, untagged operations fall into a default one, and `components.schemas` entries become model classes while inline schemas get synthesised names.
code
bash · 12 lines# server side: interfaces you implement
openapi-generator-cli generate \
-i specs/orders-v1.yaml \
-g spring \
-o build/generated/orders-api \
--additional-properties=interfaceOnly=true
# client side: a callable SDK from the same document
openapi-generator-cli generate \
-i specs/orders-v1.yaml \
-g typescript-fetch \
-o build/generated/orders-clientgo deeper
Know the command shape and the split: -g picks the generator, a server stub gives you interfaces to implement, a client SDK gives you methods to call.
Explain how the document drives the output — operationId to method names, first tag to API class, named components to model classes, response schema to return type.
Show how you keep the boundary safe: implementation separate from generated code, contract changes surfacing as compile errors, and generation run in CI to catch spec problems early.
Own the spec conventions that make generation viable across an organisation — mandatory operation ids, a tag taxonomy, named components — and which targets are generated as a supported deliverable.
## One document, two edges A generator turns the contract into code for whichever side of the wire you are on. The command is the same either way — `openapi-generator-cli generate -i openapi.yaml -g <generator> -o out` — and the `-g` value selects a *generator*, of which there are many per language (for example `spring` and `kotlin-spring` on the server side, `java`, `typescript-fetch`, `typescript-axios`, `python` and `go` on the client side). ## What a server generator emits A server stub is the plumbing between an HTTP request and your code: - **Model classes** for the schemas, with serialisation annotations and, where the target supports it, validation constraints derived from `required`, `maxLength`, `minimum` and friends. - **API interfaces or abstract controllers**, one per tag, with a method per operation carrying the route, the HTTP method, and parameters bound from path, query, header and body. What it does *not* emit is behaviour. The generated method either throws "not implemented" or delegates; you supply the implementation. This matters because it fixes the boundary: regeneration replaces the stub and must never replace your logic, so your code has to live in a separate class that implements the generated interface, not in edits to the generated file. Most server generators expose options for exactly this. `--additional-properties=interfaceOnly=true` on the `spring` generator emits interfaces without a framework wiring layer, so your own controller implements the interface; `delegatePattern=true` generates a controller that forwards to a delegate interface you implement. Either way you get compile-time enforcement: change the spec, regenerate, and the build breaks wherever the contract moved. ## What a client generator emits A client SDK is the mirror image: - The **same model classes** derived from the same schemas. - An **API class per tag** whose methods take typed parameters, build the URL and query string, serialise the request body, apply configured authentication, execute the call and deserialise the response into a model — throwing or returning an error type on non-success statuses. - Supporting infrastructure: a configuration object for base URL, timeouts and credentials, and serialisation setup. Many client generators offer a `library` option to select the underlying HTTP stack, which is worth setting deliberately rather than accepting a default your project does not otherwise use. ## How the document shapes the output This is the part interviewers probe, because it explains why generated code is sometimes unusable. - **`operationId` → method name.** A clean `listOrders` gives `listOrders(...)`. Omit it and the generator synthesises a name from the HTTP method and path, producing things like `ordersOrderIdItemsGet`. Every operation should have a hand-written, unique `operationId`. - **`tags` → class grouping.** Operations are grouped by their first tag into an API class named after it; an operation with no tag lands in a default API class. Tagging is therefore a code-structure decision, not just a docs-navigation one. - **`components.schemas` → model names.** A named component becomes a named class. An inline schema gets a synthesised name derived from the operation and property path — unstable across edits and meaningless to consumers. If you want a first-class model in the SDK, name it as a component. - **Responses → return types.** The success response's schema becomes the return type; how non-2xx responses map to exceptions or result types varies by generator. - **`required` and `nullable` → nullability.** Sloppy use here produces a client where everything is optional and callers null-check constantly. ## Where generated code disappoints Support for advanced schema composition is uneven across generators — polymorphic unions in particular map differently, or awkwardly, depending on the target language. Free-form objects tend to degrade to a map type. And because everything flows from names in the document, an inconsistent spec produces an inconsistent SDK: mixed tag naming yields oddly named classes, missing `operationId`s yield unreadable methods. The practical conclusion is that a spec intended for codegen has extra requirements beyond validity — unique operation ids, deliberate tags, named components, honest required/nullable — and the cheapest way to find violations is to generate for at least one target in CI and let compilation fail.
- Why should every operation carry a hand-written operationId?Because it becomes the generated method name. Without one the generator synthesises a name from the HTTP method and path, giving unreadable identifiers like `ordersOrderIdItemsGet` that also change whenever the path changes — a silent breaking change for anyone who has written code against the SDK.
- How do tags affect generated code?Operations are grouped by their first tag into one API class per tag, and untagged operations land in a default catch-all class. Tagging is therefore a structural decision about the SDK, not only a documentation grouping — inconsistent or missing tags produce oddly named or overloaded API classes.
- You regenerate a server stub and your implementation stops compiling. Is that a problem?No, that is the mechanism working. The generated interface encodes the contract, so a spec change surfaces as a compile error exactly where your code no longer satisfies it. The failure mode to fear is the opposite: a change that regenerates cleanly and drifts silently, which is why implementations should implement generated interfaces rather than merely resemble them.
saying these in an interview costs you the question
- Thinks a server stub contains business logic
- Ships without operationIds and accepts synthesised names
- Implements logic by editing generated files
- Assumes every generator handles polymorphic schemas identically
- Ignores tags because they are just docs grouping