How do you document request and response fields with Spring REST Docs, and what happens if you leave a field undocumented?
answer
- fieldWithPath(...).description(...)
- requestFields / responseFields
- undocumented field => test FAILS
- .optional() / .ignored() / subsectionWithPath
- relaxed* to turn off strict check
basics
~10 sUse PayloadDocumentation.responseFields(fieldWithPath("name").description("...")) inside document(). REST Docs is strict: if a field in the payload isn't documented (or a documented field is missing), the test fails, so docs stay complete.
solid answer
~40 sYou pass snippet descriptors to document(): PayloadDocumentation.requestFields(...) and responseFields(...), each built from fieldWithPath("json.path").description("..."), plus pathParameters(...), queryParameters(...), requestHeaders(...) from RequestDocumentation/HeaderDocumentation. REST Docs validates strictly against the actual payload: every field present in the JSON must have a descriptor, and every descriptor must match a real field — otherwise the test throws (undocumented fields, or missing/optional violations). This forces docs to stay complete and accurate. For fields that may be absent you mark .optional(); to skip a subtree you use subsectionWithPath(...) or .ignored(). Paths use a JSONPath-like syntax with [] for arrays and dot notation for nesting. The resulting request-fields.adoc / response-fields.adoc tables are then included in your Asciidoctor source. This strictness is the key differentiator: you can't silently ship an undocumented field.
code
java · 18 linesimport static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
mockMvc.perform(get("/users/{id}", 1))
.andExpect(status().isOk())
.andDo(document("users-get",
pathParameters(
parameterWithName("id").description("The user's unique id")
),
responseFields(
fieldWithPath("id").description("Unique identifier"),
fieldWithPath("name").description("Display name"),
fieldWithPath("contact.email").description("Primary email").optional(),
subsectionWithPath("_links").description("HATEOAS links")
)));
// If the JSON also returns "createdAt" with no descriptor -> SnippetException, test fails.go deeper
Know fieldWithPath(...).description(...) inside responseFields/requestFields.
Explain the two-way strict validation (undocumented AND missing) and .optional()/.ignored()/subsectionWithPath escapes.
Cover JsonFieldType inference, relaxed* snippets, and documenting constraints from Bean Validation via ConstraintDescriptions.
Decide team policy: strict-by-default for public APIs, when relaxed* is acceptable, and how field docs gate payload changes in review.
**Field descriptors.** Beyond the default request/response body snippets, you describe the *shape* of payloads with descriptor-based snippets from `org.springframework.restdocs.payload.PayloadDocumentation`: - `requestFields(FieldDescriptor...)` → `request-fields.adoc` - `responseFields(FieldDescriptor...)` → `response-fields.adoc` Each `FieldDescriptor` is built via `PayloadDocumentation.fieldWithPath("a.b")` or `subsectionWithPath("a")`, then `.description("...")`, optionally `.type(JsonFieldType.STRING)`, `.optional()`, or `.ignored()`. Related descriptor snippets: - `RequestDocumentation.pathParameters(parameterWithName("id").description(...))` → `path-parameters.adoc` (requires you use templated URIs like `get("/users/{id}", 1)` with `RestDocumentationRequestBuilders`). - `RequestDocumentation.queryParameters(...)` → `query-parameters.adoc`. - `HeaderDocumentation.requestHeaders(...)` / `responseHeaders(...)` → header snippets. - `HypermediaDocumentation.links(...)` → HATEOAS link documentation. **Path syntax.** Dot-notation with array support: `contact.email`, `items[]`, `items[].price`, `*.name` (wildcard). `subsectionWithPath("contact")` documents an entire nested object as one entry (useful for `_links` or large sub-objects) without describing each leaf. **Strict validation — the crucial behavior.** REST Docs enforces *completeness and correctness* of field docs: - **Undocumented field**: if the payload contains a field with no matching descriptor, the test fails with `SnippetException: The following parts of the payload were not documented: ...`. - **Missing/absent field**: if you declare a descriptor for a path that isn't present, it fails with `Fields with the following paths were not found in the payload: ...` — unless you mark it `.optional()`. This two-way check is what guarantees docs mirror reality. To intentionally skip parts, use `.ignored()` (documented-but-hidden) or `subsectionWithPath` to collapse a subtree. **Type inference.** REST Docs infers `JsonFieldType` from the payload; `.type(...)` overrides or is required when a field is optional and absent (so it can't infer). `JsonFieldType` values: OBJECT, ARRAY, BOOLEAN, NUMBER, STRING, NULL, VARIES. **Constraints.** `org.springframework.restdocs.constraints.ConstraintDescriptions` can read Bean Validation annotations (`@NotNull`, `@Size`) off your request DTO and inject them into the description via a custom snippet template — a common way to document validation rules without duplicating them. **Gotchas.** - Path parameters require `RestDocumentationRequestBuilders.get(...)` (not the plain `MockMvcRequestBuilders`) so the URI template is preserved; otherwise `pathParameters` throws. - Documenting a subset of a large array: descriptors apply per-path, so `items[].name` covers every element. - `relaxedResponseFields` / `relaxedRequestFields` exist to *disable* the undocumented-field check for that snippet — handy for shared/partial payloads, but you lose the completeness guarantee. **When to use.** Always document fields for public/consumer-facing endpoints; the strictness is a feature that catches accidental payload changes in review.
- What error do you get if the response contains a field you didn't document, and how do you handle it intentionally?A SnippetException saying parts of the payload were not documented, which fails the test. To intentionally skip, use .ignored() on a descriptor, subsectionWithPath to collapse a subtree, or relaxedResponseFields to disable the completeness check for that snippet.
- Why is get("/users/{id}", 1) required for pathParameters to work?pathParameters documents the URI template variables, so REST Docs needs the un-expanded template. RestDocumentationRequestBuilders preserves {id}; using the plain MockMvcRequestBuilders (or a pre-expanded string) loses the template and the snippet throws.
saying these in an interview costs you the question
- Claiming undocumented fields are silently omitted rather than failing the test
- Using .optional() to hide a field instead of .ignored() (optional only relaxes the missing-field check)
- Forgetting that path parameters need RestDocumentationRequestBuilders to keep the URI template