skip to content

What roles do the Encoder and Decoder play in a Feign client, and how do you customize them?

level: middleimportance: should knowfreq 48%

answer

  1. Encoder: object → request body + Content-Type
  2. Decoder: response body → return type
  3. SpringEncoder/SpringDecoder reuse HttpMessageConverters
  4. ResponseEntity/Optional/feign.Response return types
  5. SpringFormEncoder for form/multipart; per-client scope

basics

~20 s

The Encoder serializes a method argument (like a @RequestBody object) into the HTTP request body; the Decoder deserializes the HTTP response body into the method's return type. By default Spring Cloud OpenFeign uses Spring's HttpMessageConverters (Jackson JSON).

solid answer

~40 s

Feign's **`Encoder`** turns the object you pass (typically `@RequestBody`) into the request body bytes and sets `Content-Type`; the **`Decoder`** turns the response body into the declared return type based on `Accept`/`Content-Type`. Spring Cloud OpenFeign auto-configures a `SpringEncoder` and `SpringDecoder` that delegate to the same `HttpMessageConverters` used by Spring MVC — so Jackson JSON works out of the box, and you get consistent (de)serialization with your controllers. You customize by declaring `Encoder`/`Decoder` beans — globally in a scanned `@Configuration` or per-client via `@FeignClient(configuration = ...)`. Common customizations: a `FormEncoder`/`SpringFormEncoder` for multipart or form-URL-encoded bodies, wrapping in a custom Jackson `ObjectMapper`, or `feign-jackson`'s `JacksonDecoder`. The Decoder can also return `feign.Response`, `Optional`, or `ResponseEntity` for header/status access.

code

java · 19 lines
java
// Per-client config: send multipart, decode JSON normally
public class UploadClientConfig {

    @Bean
    public Encoder multipartEncoder(ObjectFactory<HttpMessageConverters> converters) {
        return new SpringFormEncoder(new SpringEncoder(converters));
    }
}

@FeignClient(name = "media", configuration = UploadClientConfig.class)
public interface MediaClient {

    @PostMapping(value = "/files", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    FileMeta upload(@RequestPart("file") MultipartFile file);

    // ResponseEntity gives access to status + headers, not just the body
    @GetMapping("/files/{id}")
    ResponseEntity<FileMeta> get(@PathVariable("id") String id);
}

go deeper

for a junior

Know Encoder serializes the request body and Decoder deserializes the response; JSON works by default.

for a middle

Explain SpringEncoder/SpringDecoder reusing HttpMessageConverters and how to plug in form/multipart encoders.

for a senior

Discuss return-type options (ResponseEntity/Optional/feign.Response), per-client scoping, and ObjectMapper consistency.

for a principal

Standardize serialization across services, handle non-JSON/streaming formats, and avoid config drift between client and server.

## Where Encoder/Decoder sit After the **Contract** builds request metadata and before dispatch, the **`Encoder`** serializes the body argument; after a 2xx response returns, the **`Decoder`** deserializes the body into your return type. (Non-2xx bodies go to the **ErrorDecoder** instead.) ## Encoder ``` public interface Encoder { void encode(Object object, Type bodyType, RequestTemplate template); } ``` It writes `object` (usually the `@RequestBody` argument) into `template.body(...)` and sets the appropriate `Content-Type`. Spring Cloud OpenFeign's **`SpringEncoder`** delegates to the application's `HttpMessageConverters` — the very converters that back `@RestController`, so a POJO becomes JSON via Jackson by default, honoring the same modules/config (e.g. Java Time, naming strategy). For form or multipart payloads you swap in `SpringFormEncoder`/`feign-form`'s encoders. ## Decoder ``` public interface Decoder { Object decode(Response response, Type type) throws IOException; } ``` **`SpringDecoder`** reads the response body through `HttpMessageConverters` into the method's return type. Return-type options: - A **domain type** → deserialized JSON POJO. - **`feign.Response`** → raw access to status/headers/body (Decoder mostly bypassed). - **`ResponseEntity<T>`** → body plus status/headers. - **`Optional<T>`** → empty on 404 (with the `OptionalDecoder`, enabled by Spring's default decoder wrapping). - **`String`/`byte[]`** → raw content. ## How to customize Declare beans: ``` @Bean public Decoder feignDecoder(ObjectFactory<HttpMessageConverters> converters) { return new ResponseEntityDecoder(new SpringDecoder(converters)); } ``` Placement decides scope: a bean in a component-scanned `@Configuration` becomes the **global** default; a bean in a class referenced only by `@FeignClient(configuration = MyClientConfig.class)` (kept out of the scan) is **per-client** and overrides the global. This lets one client speak form-encoding while others stay JSON. ## Gotchas - **Consistency with the server**: because `SpringEncoder`/`SpringDecoder` reuse `HttpMessageConverters`, custom Jackson config (modules, `ObjectMapper` bean) generally flows through automatically — but if you build a raw `JacksonEncoder`/`JacksonDecoder` from `feign-jackson`, you must configure its `ObjectMapper` separately or serialization can diverge from your controllers. - **Content negotiation**: the Encoder sets `Content-Type` and the request's `Accept` header (from `@GetMapping(produces=...)` etc.) drives which converter/Decoder path is used; mismatches cause `DecodeException`. - **Streaming/large bodies**: default decoding buffers; for large downloads return `feign.Response` or an `InputStream` and enable response streaming. - **Single Encoder/Decoder per client** — you can't mix two arbitrary encoders on one client without a composite/delegating implementation. ## When to customize Most apps never touch these — the Spring defaults handle JSON well. Reach for custom Encoder/Decoder for multipart uploads, form-URL-encoded APIs, non-JSON formats (XML/protobuf), or when a third-party API needs a bespoke `ObjectMapper` distinct from your app's.

  • How does a Feign client pick up your app's custom Jackson configuration without extra wiring?
    The default SpringEncoder/SpringDecoder delegate to the shared HttpMessageConverters, which include the Jackson converter built from your ObjectMapper bean and registered modules. So the same serialization config used by your controllers applies to Feign automatically — unless you replace them with a raw feign-jackson JacksonEncoder/Decoder, which needs its own ObjectMapper.
  • How can a Feign method access the response status code and headers, not just the body?
    Declare the return type as ResponseEntity<T> (handled by ResponseEntityDecoder) or feign.Response for raw access. Both expose status and headers; ResponseEntity also deserializes the body into T.

saying these in an interview costs you the question

  • Thinking the Decoder handles non-2xx responses (that's the ErrorDecoder)
  • Assuming a raw feign-jackson JacksonEncoder automatically inherits your app's ObjectMapper config
  • Believing you must always write a custom Encoder/Decoder (defaults handle JSON)
  • Confusing Encoder (request body) with Decoder (response body) directions

context