# Technical Documentation: OpenFeign/feign

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/OpenFeign/feign)

### Overview (`README.md`)

<p align="center"><img src="src/docs/feign-logo.svg" alt="Feign logo" width="160"></p>

# Feign simplifies the process of writing Java HTTP clients

[](https://gitter.im/OpenFeign/feign?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](https://dl.circleci.com/status-badge/redirect/gh/OpenFeign/feign/tree/master)
[](https://central.sonatype.com/artifact/io.github.openfeign/feign-core)

Feign is a Java to HTTP client binder inspired by [Retrofit](https://github.com/square/retrofit), [JAXRS-2.0](https://jax-rs-spec.java.net/nonav/2.0/apidocs/index.html), and [WebSocket](http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html).  Feign's first goal was reducing the complexity of binding [Denominator](https://github.com/Netflix/Denominator) uniformly to HTTP APIs regardless of [ReSTfulness](http://www.slideshare.net/adrianfcole/99problems).

---
### Why Feign and not X?

Feign uses tools like Jersey and CXF to write Java clients for ReST or SOAP services. Furthermore, Feign allows you to write your own code on top of http libraries such as Apache HC. Feign connects your code to http APIs with minimal overhead and code via customizable decoders and error handling, which can be written to any text-based http API.

### How does Feign work?

Feign works by processing annotations into a templatized request. Arguments are applied to these templates in a straightforward fashion before output.  Although Feign is limited to supporting text-based APIs, it dramatically simplifies system aspects such as replaying requests. Furthermore, Feign makes it easy to unit test your conversions knowing this.

### Java Version Compatibility

Feign 10.x and above are built on Java 8 and should work on Java 9, 10, and 11.  For those that need JDK 6 compatibility, please use Feign 9.x

## Feature overview

This is a map with current key features provided by feign:



# Roadmap
## Feign 11 and beyond
Making _API_ clients easier

Short Term - What we're working on now. ⏰
---
* Response Caching
  * Support caching of api responses.  Allow for users to define under what conditions a response is eligible for caching and what type of caching mechanism should be used.
  * Support in-memory caching and external cache implementations (EhCache, Google, Spring, etc...)
* Complete URI Template expression support
  * Support [level 1 through level 4](https://tools.ietf.org/html/rfc6570#section-1.2) URI template expressions.
  * Use [URI Templates TCK](https://github.com/uri-templates/uritemplate-test) to verify compliance.
* `Logger` API refactor
  * Refactor the `Logger` API to adhere closer to frameworks like SLF4J providing a common mental model for logging within Feign.  This model will be used by Feign itself throughout and provide clearer direction on how the `Logger` will be used.
* `Retry` API refactor
  * Refactor the `Retry` API to support user-supplied conditions and better control over back-off policies. **This may result in non-backward-compatible breaking changes**

Medium Term - What's up next. ⏲
---
* Async execution support via `CompletableFuture`
  * Allow for `Future` chaining and executor management for the request/response lifecycle.  **Implementation will require non-backward-compatible breaking changes**.  However this feature is required before Reactive execution can be considered.
* Reactive execution support via [Reactive Streams](https://www.reactive-streams.org/)
  * For JDK 9+, consider a native implementation that uses `java.util.concurrent.Flow`.
  * Support for [Project Reactor](https://projectreactor.io/) and [RxJava 2+](https://github.com/ReactiveX/RxJava) implementations on JDK 8.

Long Term - The future ☁️
---
* Additional Circuit Breaker Support.
  * Support additional Circuit Breaker implementations like [Resilience4J](https://resilience4j.readme.io/) and Spring Circuit Breaker

---

# Usage

The feign library is available from [Maven Central](https://central.sonatype.com/artifact/io.github.openfeign/feign-core).

```xml
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-core</artifactId>
    <version>??feign.version??</version>
</dependency>
```

### Basics

Usage typically looks like this, an adaptation of the [canonical Retrofit sample](https://github.com/square/retrofit/blob/master/samples/src/main/java/com/example/retrofit/SimpleService.java).

```java
interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

  @RequestLine("POST /repos/{owner}/{repo}/issues")
  void createIssue(Issue issue, @Param("owner") String owner, @Param("repo") String repo);

}

public static class Contributor {
  String login;
  int contributions;
}

public static class Issue {
  String title;
  String body;
  List<String> assignees;
  int milestone;
  List<String> labels;
}

public class MyApp {
  public static void main(String... args) {
    GitHub github = Feign.builder()
                         .decoder(new GsonDecoder())
                         .target(GitHub.class, "https://api.github.com");

    // Fetch and print a list of the contributors to this library.
    List<Contributor> contributors = github.contributors("OpenFeign", "feign");
    for (Contributor contributor : contributors) {
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }
}
```

### Interface Annotations

Feign annotations define the `Contract` between the interface and how the underlying client
should work.  Feign's default contract defines the following annotations:

| Annotation     | Interface Target | Usage |
|----------------|------------------|-------|
| `@RequestLine` | Method           | Defines the `HttpMethod` and `UriTemplate` for request.  `Expressions`, values wrapped in curly-braces `{expression}` are resolved using their corresponding `@Param` annotated parameters. |
| `@Param`       | Parameter        | Defines a template variable, whose value will be used to resolve the corresponding template `Expression`, by name provided as annotation value. If value is missing it will try to get the name from bytecode method parameter name (if the code was compiled with `-parameters` flag). |
| `@Headers`     | Method, Type     | Defines a `HeaderTemplate`; a variation on a `UriTemplate`.  that uses `@Param` annotated values to resolve the corresponding `Expressions`.  When used on a `Type`, the template will be applied to every request.  When used on a `Method`, the template will apply only to the annotated method. |
| `@QueryMap`    | Parameter        | Defines a `Map` of name-value pairs, or POJO, to expand into a query string. |
| `@HeaderMap`   | Parameter        | Defines a `Map` of name-value pairs, to expand into `Http Headers` |
| `@Body`        | Method           | Defines a `Template`, similar to a `UriTemplate` and `HeaderTemplate`, that uses `@Param` annotated values to resolve the corresponding `Expressions`.|


> **Overriding the Request Line**
>
> If there is a need to target a request to a different host then the one supplied when the Feign client was created, or
> you want to supply a target host for each request, include a `java.net.URI` parameter and Feign will use that value
> as the request target.
>
> ```java
> @RequestLine("POST /repos/{owner}/{repo}/issues")
> void createIssue(URI host, Issue issue, @Param("owner") String owner, @Param("repo") String repo);
> ```
>

### Templates and Expressions

Feign `Expressions` represent Simple String Expressions (Level 1) as defined by [URI Template - RFC 6570](https://tools.ietf.org/html/rfc6570).  `Expressions` are expanded using
their corresponding `Param` annotated method parameters.

*Example*

```java
public interface GitHub {

  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repository);

  class Contributor {
    String login;
    int contributions;
  }
}

public class MyApp {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                         .decoder(new GsonDecoder())
                         .target(GitHub.class, "https://api.github.com");

    /* The owner and repository parameters will be used to expand the owner and repo expressions
     * defined in the RequestLine.
     *
     * the resulting uri will be https://api.github.com/repos/OpenFeign/feign/contributors
     */
    github.contributors("OpenFeign", "feign");
  }
}
```

Expressions must be enclosed in curly braces `{}` and may contain regular expression patterns, separated by a colon `:`  to restrict
resolved values.  *Example* `owner` must be alphabetic. `{owner:[a-zA-Z]*}`

#### Request Parameter Expansion

`RequestLine` and `QueryMap` templates follow the [URI Template - RFC 6570](https://tools.ietf.org/html/rfc6570) specification for Level 1 templates, which specifies the following:

* Unresolved expressions are omitted.
* All literals and variable values are pct-encoded, if not already encoded or marked `encoded` via a `@Param` annotation.

We also have limited support for Level 3, Path Style Expressions, with the following restrictions:

* Maps and Lists are expanded by default.
* Only Single variable templates are supported.

*Examples:*

```
{;who}             ;who=fred
{;half}            ;half=50%25
{;empty}           ;empty
{;list}            ;list=red;list=green;list=blue
{;map}             ;semi=%3B;dot=.;comma=%2C
```

```java
public interface MatrixService {

  @RequestLine("GET /repos{;owners}")
  List<Contributor> contributors(@Param("owners") List<String> owners);

  class Contributor {
    String login;
    int contributions;
  }
}
```

If `owners` in the above example is defined as `Matt, Jeff, Susan`, the uri will expand to `/repos;owners=Matt;owners=Jeff;owners=Susan` 

For more information see [RFC 6570, Section 3.2.7](https://datatracker.ietf.org/doc/html/rfc6570#section-3.2.7)

#### Undefined vs. Empty Values ####

Undefined expressions are expressions where the value for the expression is an explicit `null` or no value is provided.
Per [URI Template - RFC 6570](https://tools.ietf.org/html/rfc6570), it is possible to provide an empty value
for an expression.  When Feign resolves an expression, it first determines if the value is defined, if it is then
the query parameter will remain.  If the expression is undefined, the query parameter is removed.  See below
for a complete breakdown.

*Empty String*
```java
public void test() {
   Map<String, Object> parameters = new LinkedHashMap<>();
   parameters.put("param", "");
   this.demoClient.test(parameters);
}
```
Result
```
http://localhost:8080/test?param=
```

*Missing*
```java
public void test() {
   Map<String, Object> parameters = new LinkedHashMap<>();
   this.demoClient.test(parameters);
}
```
Result
```
http://localhost:8080/test
```

*Undefined*
```java
public void test() {
   Map<String, Object> parameters = new LinkedHashMap<>();
   parameters.put("param", null);
   this.demoClient.test(parameters);
}
```
Result
```
http://localhost:8080/test
```

See [Advanced Usage](#advanced-usage) for more examples.

> **What about slashes? `/`**
>
> @RequestLine templates do not encode slash `/` characters by default.  To change this behavior, set the `decodeSlash` property on the `@RequestLine` to `false`.

> **What about plus? `+`**
>
> Per the URI specification, a `+` sign is allowed in both the path and query segments of a URI, however, handling of
> the symbol on the query can be inconsistent.  In some legacy systems, the `+` is equivalent to the a space.  Feign takes the approach of modern systems, where a
> `+` symbol should not represent a space and is explicitly encoded as `%2B` when found on a query string.
>
> If you wish to use `+` as a space, then use the literal ` ` character or encode the value directly as `%20`

##### Custom Expansion

The `@Param` annotation has an optional property `expander` allowing for complete control over the individual parameter's expansion.
The `expander` property must reference a class that implements the `Expander` interface:

```java
public interface Expander {
    String expand(Object value);
}
```
The result of this method adheres to the same rules stated above.  If the result is `null` or an empty string,
the value is omitted.  If the value is not pct-encoded, it will be.  See [Custom @Param Expansion](#custom-param-expansion) for more examples.

#### Request Headers Expansion

`Headers` and `HeaderMap` templates follow the same rules as [Request Parameter Expansion](#request-parameter-expansion)
with the following alterations:

* Unresolved expressions are omitted.  If the result is an empty header value, the entire header is removed.
* No pct-encoding is performed.

See [Headers](#headers) for examples.

> **A Note on `@Param` parameters and their names**:
>
> All expressions with the same name, regardless of their position on the `@RequestLine`, `@QueryMap`, `@BodyTemplate`, or `@Headers` will resolve to the same value.
> In the following example, the value of `contentType`, will be used to resolve both the header and path expression:
>
> ```java
> public interface ContentService {
>   @RequestLine("GET /api/documents/{contentType}")
>   @Headers("Accept: {contentType}")
>   String getDocumentByType(@Param("contentType") String type);
> }
>```
>
> Keep this in mind when designing your interfaces.

#### Request Body Expansion

`Body` templates follow the same rules as [Request Parameter Expansion](#request-parameter-expansion)
with the following alterations:

* Unresolved expressions are omitted.
* Expanded value will **not** be passed through an `Encoder` before being placed on the request body.
* A `Content-Type` header must be specified.  See [Body Templates](#body-templates) for examples.

---
### Customization

Feign has several aspects that can be customized.  
For simple cases, you can use `Feign.builder()` to construct an API interface with your custom components.<br>
For request setting, you can use `options(Request.Options options)` on `target()` to set connectTimeout, connectTimeoutUnit, readTimeout, readTimeoutUnit, followRedirects.<br>
For example:

```java
interface Bank {
  @RequestLine("POST /account/{id}")
  Account getAccountInfo(@Param("id") String id);
}

public class BankService {
  public static void main(String[] args) {
    Bank bank = Feign.builder()
        .decoder(new AccountDecoder())
        .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true))
        .target(Bank.class, "https://api.examplebank.com");
  }
}
```

### Multiple Interfaces
Feign can produce multiple api interfaces.  These are defined as `Target<T>` (default `HardCodedTarget<T>`), which allow for dynamic discovery and decoration of requests prior to execution.

For example, the following pattern might decorate each request with the current url and auth token from the identity service.

```java
public class CloudService {
  public static void main(String[] args) {
    CloudDNS cloudDNS = Feign.builder()
      .target(new CloudIdentityTarget<CloudDNS>(user, apiKey));
  }

  class CloudIdentityTarget extends Target<CloudDNS> {
    /* implementation of a Target */
  }
}
```

### Examples
Feign includes example [GitHub](./example-github) and [Wikipedia](./example-wikipedia) clients. The denominator project can also be scraped for Feign in practice. Particularly, look at its [example daemon](https://github.com/Netflix/denominator/tree/master/example-daemon).

---
### Integrations
Feign intends to work well with other Open Source tools.  Modules are welcome to integrate with your favorite projects!

### Encoder/Decoder

#### Gson
[Gson](./gson) includes a codec you can use with a JSON API.

```java
GitHub github = Feign.builder()
                     .codec(new GsonCodec())
                     .target(GitHub.class, "https://api.github.com");
```

You can also configure the encoder and decoder separately:

```java
GitHub github = Feign.builder()
                     .encoder(new GsonEncoder())
                     .decoder(new GsonDecoder())
                     .target(GitHub.class, "https://api.github.com");
```

#### Jackson
[Jackson](./jackson) includes a codec you can use with a JSON API.

```java
GitHub github = Feign.builder()
                     .codec(new JacksonCodec())
                     .target(GitHub.class, "https://api.github.com");
```

You can also configure the encoder and decoder separately:

```java
GitHub github = Feign.builder()
                     .encoder(new JacksonEncoder())
                     .decoder(new JacksonDecoder())
                     .target(GitHub.class, "https://api.github.com");
```

For the lighter weight Jackson Jr, use `JacksonJrEncoder` and `JacksonJrDecoder` from
the [Jackson Jr Module](./jackson-jr).

#### Moshi
[Moshi](./moshi) includes a codec you can use with a JSON API.

```java
GitHub github = Feign.builder()
                     .codec(new MoshiCodec())
                     .target(GitHub.class, "https://api.github.com");
```

You can also configure the encoder and decoder separately:

```java
GitHub github = Feign.builder()
                     .encoder(new MoshiEncoder())
                     .decoder(new MoshiDecoder())
                     .target(GitHub.class, "https://api.github.com");
```

#### Sax
[SaxDecoder](./sax) allows you to decode XML in a way that is compatible with normal JVM and also Android environments.

Here's an example of how to configure Sax response parsing:
```java
Api api = Feign.builder()
               .decoder(SAXDecoder.builder()
                                  .registerContentHandler(UserIdHandler.class)
                                  .build())
               .target(Api.class, "https://apihost");
```

#### JAXB
[JAXB](./jaxb) includes a codec you can use with an XML API.

```java
Api api = Feign.builder()
               .codec(new JAXBCodec(jaxbFactory))
               .target(Api.class, "https://apihost");
```

You can also configure the encoder and decoder separately:

```java
Api api = Feign.builder()
               .encoder(new JAXBEncoder(jaxbFactory))
               .decoder(new JAXBDecoder(jaxbFactory))
               .target(Api.class, "https://apihost");
```

#### SOAP
[SOAP](./soap) includes a codec you can use with an XML API.

This module adds support for encoding and decoding SOAP Body objects via JAXB and SOAPMessage. It also provides SOAPFault decoding capabilities by wrapping them into the original `javax.xml.ws.soap.SOAPFaultException`, so that you'll only need to catch `SOAPFaultException` in order to handle SOAPFault.

```java
Api api = Feign.builder()
               .codec(new SOAPCodec(jaxbFactory))
               .errorDecoder(new SOAPErrorDecoder())
               .target(MyApi.class, "http://api");
```

You can also configure the encoder and decoder separately:

```java
Api api = Feign.builder()
               .encoder(new SOAPEncoder(jaxbFactory))
               .decoder(new SOAPDecoder(jaxbFactory))
               .errorDecoder(new SOAPErrorDecoder())
               .target(MyApi.class, "http://api");
```

NB: you may also need to add `SOAPErrorDecoder` if SOAP Faults are returned in response with error http codes (4xx, 5xx, ...)

#### Fastjson2

[fastjson2](./fastjson2) includes a codec you can use with a JSON API.

```java
GitHub github = Feign.builder()
                     .codec(new Fastjson2Codec())
                     .target(GitHub.class, "https://api.github.com");
```

You can also configure the encoder and decoder separately:

```java
GitHub github = Feign.builder()
                     .encoder(new Fastjson2Encoder())
                     .decoder(new Fastjson2Decoder())
                     .target(GitHub.class, "https://api.github.com");
```

### Contract

#### JAX-RS
[JAXRSContract](./jaxrs) overrides annotation processing to instead use standard ones supplied by the JAX-RS specification.  This is currently targeted at the 1.1 spec.

Here's the example above re-written to use JAX-RS:
```java
interface GitHub {
  @GET @Path("/repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@PathParam("owner") String owner, @PathParam("repo") String repo);
}

public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                       .contract(new JAXRSContract())
                       .target(GitHub.class, "https://api.github.com");
  }
}
```

### Client

#### OkHttp
[OkHttpClient](./okhttp) directs Feign's http requests to [OkHttp](http://square.github.io/okhttp/), which enables SPDY and better network control.

To use OkHttp with Feign, add the OkHttp module to your classpath. Then, configure Feign to use the OkHttpClient:

```java
public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                     .client(new OkHttpClient())
                     .target(GitHub.class, "https://api.github.com");
  }
}
```

#### Ribbon
[RibbonClient](./ribbon) overrides URL resolution of Feign's client, adding smart routing and resiliency capabilities provided by [Ribbon](https://github.com/Netflix/ribbon).

Integration requires you to pass your ribbon client name as the host part of the url, for example `myAppProd`.
```java
public class Example {
  public static void main(String[] args) {
    MyService api = Feign.builder()
          .client(RibbonClient.create())
          .target(MyService.class, "https://myAppProd");
  }
}
```

#### Java 11 Http2
[Http2Client](./java11) directs Feign's http requests to Java11 [New HTTP/2 Client](https://openjdk.java.net/jeps/321) that implements HTTP/2.

To use New HTTP/2 Client with Feign, use Java SDK 11. Then, configure Feign to use the Http2Client:

```java
GitHub github = Feign.builder()
                     .client(new Http2Client())
                     .target(GitHub.class, "https://api.github.com");
```

### Breaker

#### Hystrix
[HystrixFeign](./hystrix) configures circuit breaker support provided by [Hystrix](https://github.com/Netflix/Hystrix).

To use Hystrix with Feign, add the Hystrix module to your classpath. Then use the `HystrixFeign` builder:

```java
public class Example {
  public static void main(String[] args) {
    MyService api = HystrixFeign.builder().target(MyService.class, "https://myAppProd");
  }
}
```

### Logger

#### SLF4J
[SLF4JModule](./slf4j) allows directing Feign's logging to [SLF4J](http://www.slf4j.org/), allowing you to easily use a logging backend of your choice (Logback, Log4J, etc.)

To use SLF4J with Feign, add both the SLF4J module and an SLF4J binding of your choice to your classpath.  Then, configure Feign to use the Slf4jLogger:

```java
public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                     .logger(new Slf4jLogger())
                     .logLevel(Level.FULL)
                     .target(GitHub.class, "https://api.github.com");
  }
}
```

### Decoders
`Feign.builder()` allows you to specify additional configuration such as how to decode a response.

If any methods in your interface return types besides `Response`, `String`, `byte[]` or `void`, you'll need to configure a non-default `Decoder`.

Here's how to configure JSON decoding (using the `feign-gson` extension):

```java
public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                     .decoder(new GsonDecoder())
                     .target(GitHub.class, "https://api.github.com");
  }
}
```

If you need to pre-process the response before give it to the Decoder, you can use the `mapAndDecode` builder method.
An example use case is dealing with an API that only serves jsonp, you will maybe need to unwrap the jsonp before
send it to the Json decoder of your choice:

```java
public class Example {
  public static void main(String[] args) {
    JsonpApi jsonpApi = Feign.builder()
                         .mapAndDecode((response, type) -> jsopUnwrap(response, type), new GsonDecoder())
                         .target(JsonpApi.class, "https://some-jsonp-api.com");
  }
}
```

If any methods in your interface return type `Stream`, you'll need to configure a `StreamDecoder`.

Here's how to configure Stream decoder without delegate decoder:

```java
public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
            .decoder(StreamDecoder.create((r, t) -> {
              BufferedReader bufferedReader = new BufferedReader(r.body().asReader(UTF_8));
              return bufferedReader.lines().iterator();
            }))
            .target(GitHub.class, "https://api.github.com");
  }
}
``` 

Here's how to configure Stream decoder with delegate decoder:

```java

public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
            .decoder(StreamDecoder.create((r, t) -> {
              BufferedReader bufferedReader = new BufferedReader(r.body().asReader(UTF_8));
              return bufferedReader.lines().iterator();
            }, (r, t) -> "this is delegate decoder"))
            .target(GitHub.class, "https://api.github.com");
  }
}
```

#### Multiple decoders

> This API is `@Experimental` and may change incompatibly, or be removed, in a future release.

A single client sometimes has to read more than one format &mdash; JSON for most endpoints, XML for
a legacy one, plain text for a health check. `MultiDecoder` hands each response to the first decoder
that accepts it.

Most first-party decoders already declare what they can handle, so they can simply be listed, in the
order they should be consulted:

```java
interface MixedClient {
  @RequestLine("GET /orders/{id}")
  Order order(@Param("id") String id);

  @RequestLine("GET /legacy/orders/{id}")
  Order legacyOrder(@Param("id") String id);
}

public class Example {
  public static void main(String[] args) {
    MixedClient client = Feign.builder()
                              .decoders(new GsonDecoder(), new JAXBDecoder())
                              .target(MixedClient.class, "https://foo.com");
  }
}
```

Routing is driven by what the server actually sent back, so a client that talks to endpoints
answering `application/json` and `application/xml` no longer needs one Feign instance per format.

There is no implicit fallback. A response that no decoder accepts fails with a `DecodeException`
naming the decoders that were tried and what each one wants:

```
Unable to decode 200 response (Content-Type: text/plain) as com.example.Order. Decoders tried, in order:
  - GsonDecoder
  - JAXBDecoder
Add a decoder guarded by DecoderPredicate.any() last to act as a default.
```

To get a default, pair a decoder with the predicate that accepts everything and list it **last**:

```java
Feign.builder()
     .decoders(
         new GsonDecoder(),
         new JAXBDecoder(),
         PredicatedDecoder.of(DecoderPredicate.any(), new DefaultDecoder()));
```

The same pairing works for any decoder that does not declare itself, including one you do not
control. `MultiDecoder.builder()` spells it out when a lambda reads better than a wrapper:

```java
Decoder decoder =
    MultiDecoder.builder()
        .add(new GsonDecoder())                                   // declares itself
        .add(DecoderPredicate.xmlContentType(), someXmlDecoder)    // paired
        .add((response, type) -> type == byte[].class, binaryDecoder)
        .add(DecoderPredicate.any(), new DefaultDecoder())         // the default, last
        .build();
```

Decoders are consulted in the order they were added, so put the narrowest one first.

##### Declaring your own decoder

Implement `PredicatedDecoder` and say what you handle. `canDecode` has no default: a decoder that
declares nothing would claim every response, which is rarely what its author meant.

```java
public class MyDecoder implements PredicatedDecoder {

  @Override
  public boolean canDecode(Response response, Type type) {
    return Util.isJsonContentType(response);
  }

  @Override
  public Object decode(Response response, Type type) throws IOException {
    // ...
  }
}
```

`DecoderPredicate` is the `@FunctionalInterface` here, so predicates can be lambdas. It ships with
`any()`, `jsonContentType()`, `xmlContentType()`, `contentType(mediaType)`, `emptyBody()`,
`status(codes...)` and `returnType(type)`, plus `and`/`or`/`negate` to combine them. Each one
describes itself, which is what shows up in the error message above; wrap your own lambdas in
`DecoderPredicate.describedAs("it is Tuesday", ...)` to read as well.

`PredicatedDecoder.of(predicate, decoder)` replaces whatever the decoder says about itself, so it
can widen a decoder as well as narrow it. To keep the decoder's own declaration and add to it, use
`narrowing`:

```java
// JSON responses as usual, but only when the call actually succeeded
PredicatedDecoder.narrowing(DecoderPredicate.status(200, 201), new GsonDecoder());
```

**Predicates must not read the response body.** For most clients it is a single-pass stream, so
consuming it in `canDecode` would leave nothing for the decoder that is eventually chosen. Decide
on the status, the headers and the expected type instead.

**If you wrap a decoder, forward `canDecode` to your delegate**, otherwise wrapping silently changes
what the decoder handles. `OptionalDecoder` and the metrics modules' `MeteredDecoder` forward for
exactly this reason.

### Encoders
The simplest way to send a request body to a server is to define a `POST` method that has a `String` or `byte[]` parameter without any annotations on it. You will likely need to add a `Content-Type` header.

```java
interface LoginClient {
  @RequestLine("POST /")
  @Headers("Content-Type: application/json")
  void login(String content);
}

public class Example {
  public static void main(String[] args) {
    client.login("{\"user_name\": \"denominator\", \"password\": \"secret\"}");
  }
}
```

By configuring an `Encoder`, you can send a type-safe request body. Here's an example using the `feign-gson` extension:

```java
static class Credentials {
  final String user_name;
  final String password;

  Credentials(String user_name, String password) {
    this.user_name = user_name;
    this.password = password;
  }
}

interface LoginClient {
  @RequestLine("POST /")
  void login(Credentials creds);
}

public class Example {
  public static void main(String[] args) {
    LoginClient client = Feign.builder()
                              .encoder(new GsonEncoder())
                              .target(LoginClient.class, "https://foo.com");

    client.login(new Credentials("denominator", "secret"));
  }
}
```

#### Multiple encoders

> This API is `@Experimental` and may change incompatibly, or be removed, in a future release.

A single client sometimes has to speak more than one format &mdash; JSON for most endpoints, XML for
a legacy one, plain bytes for an upload. `MultiEncoder` hands each request to the first encoder that
accepts it.

Most first-party encoders already declare what they can handle, so they can simply be listed, in the
order they should be consulted:

```java
interface MixedClient {
  @RequestLine("POST /orders")
  @Headers("Content-Type: application/json")
  void createOrder(Order order);

  @RequestLine("POST /legacy/orders")
  @Headers("Content-Type: application/xml")
  void createLegacyOrder(Order order);
}

public class Example {
  public static void main(String[] args) {
    MixedClient client = Feign.builder()
                              .encoders(new GsonEncoder(), new JAXBEncoder())
                              .target(MixedClient.class, "https://foo.com");
  }
}
```

There is no implicit fallback. A request that no encoder accepts fails with an `EncodeException`
naming the encoders that were tried and what each one wants:

```
Unable to encode java.lang.String (Content-Type: text/plain) for POST /orders. Encoders tried, in order:
  - GsonEncoder
  - JAXBEncoder
Add an encoder guarded by EncoderPredicate.any() last to act as a default.
```

To get a default, pair an encoder with the predicate that accepts everything and list it **last**:

```java
Feign.builder()
     .encoders(
         new GsonEncoder(),
         new JAXBEncoder(),
         PredicatedEncoder.of(EncoderPredicate.any(), new DefaultEncoder()));
```

The same pairing works for any encoder that does not declare itself, including one you do not
control. `MultiEncoder.builder()` spells it out when a lambda reads better than a wrapper:

```java
Encoder encoder =
    MultiEncoder.builder()
        .add(new GsonEncoder())                                    // declares itself
        .add(EncoderPredicate.xmlContentType(), someXmlEncoder)     // paired
        .add((object, bodyType, template) -> bodyType == byte[].class, binaryEncoder)
        .add(EncoderPredicate.any(), new DefaultEncoder())          // the default, last
        .build();
```

Encoders are consulted in the order they were added, so put the narrowest one first. Note that
`Content-Type: application/json` with a null body is claimed by a JSON encoder before
`EncoderPredicate.emptyBody()` gets a chance &mdash; order accordingly.

##### Declaring your own encoder

Implement `PredicatedEncoder` and say what you handle. `canEncode` has no default: an encoder that
declares nothing would claim every request, which is rarely what its author meant.

```java
public class MyEncoder implements PredicatedEncoder {

  @Override
  public boolean canEncode(Object object, Type bodyType, RequestTemplate template) {
    return Util.isJsonContentType(template);
  }

  @Override
  public void encode(Object object, Type bodyType, RequestTemplate template) {
    // ...
  }
}
```

`EncoderPredicate` is the `@FunctionalInterface` here, so predicates can be lambdas. It ships with
`any()`, `jsonContentType()`, `xmlContentType()`, `contentType(mediaType)`, `emptyBody()`,
`bodyType(type)` and `formEncoded()`, plus `and`/`or`/`negate` to combine them. Each one describes
itself, which is what shows up in the error message above; wrap your own lambdas in
`EncoderPredicate.describedAs("it is Tuesday", ...)` to read as well.

`PredicatedEncoder.of(predicate, encoder)` replaces whatever the encoder says about itself, so it
can widen an encoder as well as narrow it. To keep the encoder's own declaration and add to it, use
`narrowing`:

```java
// only this vendor content type, and only what Gson would have taken anyway
PredicatedEncoder.narrowing(
    EncoderPredicate.contentType("application/vnd.acme+json"), new GsonEncoder());
```

**If you wrap an encoder, forward `canEncode` to your delegate**, otherwise wrapping silently
changes what the encoder handles. The metrics modules' `MeteredEncoder` forwards for exactly this
reason.

##### Form encoders

`FormEncoder` and `SpringFormEncoder` wrap a delegate encoder, so they cannot honestly declare what
they handle &mdash; the delegate's applicability is unknown to them. Instead, each offers a
delegate-free flavour that does:

```java
Feign.builder()
     .encoders(
         FormEncoder.createPredicatedFormEncoder(),  // form and multipart requests only
         new JacksonEncoder());
```

It accepts form and multipart requests carrying a map or a user pojo, and leaves everything else to
the encoders registered alongside it. Constructing one directly with a `null` delegate does the same
thing: anything it cannot encode itself fails with an `EncodeException` instead of being passed on.

### @Body templates
The `@Body` annotation indicates a template to expand using parameters annotated with `@Param`. You will likely need to add a `Content-Type` header.

```java
interface LoginClient {

  @RequestLine("POST /")
  @Headers("Content-Type: application/xml")
  @Body("<login \"user_name\"=\"{user_name}\" \"password\"=\"{password}\"/>")
  void xml(@Param("user_name") String user, @Param("password") String password);

  @RequestLine("POST /")
  @Headers("Content-Type: application/json")
  // json curly braces must be escaped!
  @Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
  void json(@Param("user_name") String user, @Param("password") String password);
}

public class Example {
  public static void main(String[] args) {
    client.xml("denominator", "secret"); // <login "user_name"="denominator" "password"="secret"/>
    client.json("denominator", "secret"); // {"user_name": "denominator", "password": "secret"}
  }
}
```

### Headers
Feign supports settings headers on requests either as part of the api or as part of the client
depending on the use case.

#### Set headers using apis
In cases where specific interfaces or calls should always have certain header values set, it
makes sense to define headers as part of the api.

Static headers can be set on an api interface or method using the `@Headers` annotation.

```java
@Headers("Accept: application/json")
interface BaseApi<V> {
  @Headers("Content-Type: application/json")
  @RequestLine("PUT /api/{key}")
  void put(@Param("key") String key, V value);
}
```

Methods can specify dynamic content for static headers using variable expansion in `@Headers`.

```java
public interface Api {
   @RequestLine("POST /")
   @Headers("X-Ping: {token}")
   void post(@Param("token") String token);
}
```

In cases where both the header field keys and values are dynamic and the range of possible keys cannot
be known ahead of time and may vary between different method calls in the same api/client (e.g. custom
metadata header fields such as "x-amz-meta-\*" or "x-goog-meta-\*"), a Map parameter can be annotated
with `HeaderMap` to construct a query that uses the contents of the map as its header parameters.

```java
public interface Api {
   @RequestLine("POST /")
   void post(@HeaderMap Map<String, Object> headerMap);
}
```

These approaches specify header entries as part of the api and do not require any customizations
when building the Feign client.

#### Setting headers per target
To customize headers for each request method on a Target, a RequestInterceptor can be used. RequestInterceptors can be
shared across Target instances and are expected to be thread-safe. RequestInterceptors are applied to all request
methods on a Target.

If you need per method customization, a custom Target is required, as the a RequestInterceptor does not have access to
the current method metadata.

For an example of setting headers using a `RequestInterceptor`, see the `Request Interceptors` section.

Headers can be set as part of a custom `Target`.

```java
  static class DynamicAuthTokenTarget<T> implements Target<T> {
    public DynamicAuthTokenTarget(Class<T> clazz,
                                  UrlAndTokenProvider provider,
                                  ThreadLocal<String> requestIdProvider);

    @Override
    public Request apply(RequestTemplate input) {
      TokenIdAndPublicURL urlAndToken = provider.get();
      if (input.url().indexOf("http") != 0) {
        input.insert(0, urlAndToken.publicURL);
      }
      input.header("X-Auth-Token", urlAndToken.tokenId);
      input.header("X-Request-ID", requestIdProvider.get());

      return input.request();
    }
  }

  public class Example {
    public static void main(String[] args) {
      Bank bank = Feign.builder()
              .target(new DynamicAuthTokenTarget(Bank.class, provider, requestIdProvider));
    }
  }
```

These approaches depend on the custom `RequestInterceptor` or `Target` being set on the Feign
client when it is built and can be used as a way to set headers on all api calls on a per-client
basis. This can be useful for doing things such as setting an authentication token in the header
of all api requests on a per-client basis. The methods are run when the api call is made on the
thread that invokes the api call, which allows the headers to be set dynamically at call time and
in a context-specific manner -- for example, thread-local storage can be used to set different
header values depending on the invoking thread, which can be useful for things such as setting
thread-specific trace identifiers for requests.

#### Set zero Content-Length Header

To specify `Content-Length: 0` header when making a request with empty body, system property `sun.net.http.allowRestrictedHeaders` should be set to `true`

If not, the `Content-Length` header will not be added.

### Advanced usage

#### Base Apis
In many cases, apis for a service follow the same conventions. Feign supports this pattern via single-inheritance interfaces.

Consider the example:
```java
interface BaseAPI {
  @RequestLine("GET /health")
  String health();

  @RequestLine("GET /all")
  List<Entity> all();
}
```

You can define and target a specific api, inheriting the base methods.
```java
interface CustomAPI extends BaseAPI {
  @RequestLine("GET /custom")
  String custom();
}
```

In many cases, resource representations are also consistent. For this reason, type parameters are supported on the base api interface.

```java
@Headers("Accept: application/json")
interface BaseApi<V> {

  @RequestLine("GET /api/{key}")
  V get(@Param("key") String key);

  @RequestLine("GET /api")
  List<V> list();

  @Headers("Content-Type: application/json")
  @RequestLine("PUT /api/{key}")
  void put(@Param("key") String key, V value);
}

interface FooApi extends BaseApi<Foo> { }

interface BarApi extends BaseApi<Bar> { }
```

#### Logging
You can log the http messages going to and from the target by setting up a `Logger`.  Here's the easiest way to do that:
```java
public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                     .decoder(new GsonDecoder())
                     .logger(new Logger.JavaLogger("GitHub.Logger").appendToFile("logs/http.log"))
                     .logLevel(Logger.Level.FULL)
                     .target(GitHub.class, "https://api.github.com");
  }
}
```

> **A Note on JavaLogger**:
> Avoid using of default ```JavaLogger()``` constructor - it was marked as deprecated and will be removed soon.

The SLF4JLogger (see above) may also be of interest.

To filter out sensitive information like authorization or tokens
override methods `shouldLogRequestHeader` or `shouldLogResponseHeader`.

#### Request Interceptors
When you need to change all requests, regardless of their target, you'll want to configure a `RequestInterceptor`.
For example, if you are acting as an intermediary, you might want to propagate the `X-Forwarded-For` header.

```java
static class ForwardedForInterceptor implements RequestInterceptor {
  @Override public void apply(RequestTemplate template) {
    template.header("X-Forwarded-For", "origin.host.com");
  }
}

public class Example {
  public static void main(String[] args) {
    Bank bank = Feign.builder()
                 .decoder(accountDecoder)
                 .requestInterceptor(new ForwardedForInterceptor())
                 .target(Bank.class, "https://api.examplebank.com");
  }
}
```

Another common example of an interceptor would be authentication, such as using the built-in `BasicAuthRequestInterceptor`.

```java
public class Example {
  public static void main(String[] args) {
    Bank bank = Feign.builder()
                 .decoder(accountDecoder)
                 .requestInterceptor(new BasicAuthRequestInterceptor(username, password))
                 .target(Bank.class, "https://api.examplebank.com");
  }
}
```

#### Custom @Param Expansion
Parameters annotated with `Param` expand based on their `toString`. By
specifying a custom `Param.Expander`, users can control this behavior,
for example formatting dates.

```java
public interface Api {
  @RequestLine("GET /?since={date}") Result list(@Param(value = "date", expander = DateToMillis.class) Date date);
}
```

#### Dynamic Query Parameters
A Map parameter can be annotated with `QueryMap` to construct a query that uses the contents of the map as its query parameters.

```java
public interface Api {
  @RequestLine("GET /find")
  V find(@QueryMap Map<String, Object> queryMap);
}
```

This may also be used to generate the query parameters from a POJO object using a `QueryMapEncoder`.

```java
public interface Api {
  @RequestLine("GET /find")
  V find(@QueryMap CustomPojo customPojo);
}
```

When used in this manner, without specifying a custom `QueryMapEncoder`, the query map will be generated using member variable names as query parameter names. You can annotate a specific field of `CustomPojo` with the `@Param` annotation to specify a different name to the query parameter. The following POJO will generate query params of "/find?name={name}&number={number}&region_id={regionId}" (order of included query parameters not guaranteed, and as usual, if any value is null, it will be left out).

```java
public class CustomPojo {
  private final String name;
  private final int number;
  @Param("region_id")
  private final String regionId;

  public CustomPojo (String name, int number, String regionId) {
    this.name = name;
    this.number = number;
    this.regionId = regionId;
  }
}
```

To setup a custom `QueryMapEncoder`:

```java
public class Example {
  public static void main(String[] args) {
    MyApi myApi = Feign.builder()
                 .queryMapEncoder(new MyCustomQueryMapEncoder())
                 .target(MyApi.class, "https://api.hostname.com");
  }
}
```

When annotating objects with @QueryMap, the default encoder uses reflection to inspect provided objects Fields to expand the objects values into a query string. If you prefer that the query string be built using getter and setter methods, as defined in the Java Beans API, please use the BeanQueryMapEncoder

```java
public class Example {
  public static void main(String[] args) {
    MyApi myApi = Feign.builder()
                 .queryMapEncoder(new BeanQueryMapEncoder())
                 .target(MyApi.class, "https://api.hostname.com");
  }
}
```

### Error Handling
If you need more control over handling unexpected responses, Feign instances can
register a custom `ErrorDecoder` via the builder.

```java
public class Example {
  public static void main(String[] args) {
    MyApi myApi = Feign.builder()
                 .errorDecoder(new MyErrorDecoder())
                 .target(MyApi.class, "https://api.hostname.com");
  }
}
```

All responses that result in an HTTP status not in the 2xx range will trigger the `ErrorDecoder`'s `decode` method, allowing
you to handle the response, wrap the failure into a custom exception or perform any additional processing.
If you want to retry the request again, throw a `RetryableException`.  This will invoke the registered
`Retryer`.

#### Error responses as values
Some APIs describe a failure in the response body, using the same envelope as a success. Call
`decodeErrorResponses()` to decode that body into the method's return type instead of throwing:

```java
interface MyApi {
  @RequestLine("POST /users")
  BaseResponse createUser(NewUser user);
}

MyApi myApi = Feign.builder()
                 .decoder(new JacksonDecoder())
                 .decodeErrorResponses()
                 .target(MyApi.class, "https://api.hostname.com");
```

The error body is returned as a value only when all of these hold. Otherwise the call throws, as it does without the flag:

* the status is 400 or above, other than 404 (use `dismiss404()` for 404);
* the method returns a value. `void` methods always throw;
* the body is between 1 and 8192 bytes;
* the decoder accepts the response (`PredicatedDecoder.canDecode`), so a JSON decoder is never handed an HTML error page;
* the `ErrorDecoder` did not return a `RetryableException`, so the `Retryer` keeps working;
* the body decodes. If it doesn't, the `ErrorDecoder`'s exception is thrown with the decode failure attached as suppressed.

The flag applies to every method on the client, and the decoder sees the status the server sent. With the default
decoder and a `String` return type, the error body is returned as the string. This option is experimental.

### Retry
Feign, by default, will automatically retry `IOException`s, regardless of HTTP method, treating them as transient network
related exceptions, and any `RetryableException` thrown from an `ErrorDecoder`.  To customize this
behavior, register a custom `Retryer` instance via the builder.

The following example shows how to refresh token and retry with `ErrorDecoder` and `Retryer` when received a 401 response.

```
/* Detailed source-code truncated for AI context efficiency. */
```

`Retryer`s are responsible for determining if a retry should occur by returning either a `true` or
`false` from the method `continueOrPropagate(RetryableException e);`  A `Retryer` instance will be
created for each `Client` execution, allowing you to maintain state bewteen each request if desired.

If the retry is determined to be unsuccessful, the last `RetryException` will be thrown.  To throw the original
cause that led to the unsuccessful retry, build your Feign client with the `exceptionPropagationPolicy()` option.

#### Response Interceptor
If you need to treat what would otherwise be an error as a success and return a result rather than throw an exception then you may use a `ResponseInterceptor`.

As an example Feign includes a simple `RedirectionInterceptor` that can be used to extract the location header from redirection responses.
```java
public interface Api {
  // returns a 302 response
  @RequestLine("GET /location")
  String location();
}

public class MyApp {
  public static void main(String[] args) {
    // Configure the HTTP client to ignore redirection
    Api api = Feign.builder()
                   .options(new Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, false))
                   .responseInterceptor(new RedirectionInterceptor())
                   .target(Api.class, "https://redirect.example.com");
  }
}
```

### Metrics
By default, feign won't collect any metrics.

But, it's possible to add metric collection capabilities to any feign client.

Metric Capabilities provide a first-class Metrics API that users can tap into to gain insight into the request/response lifecycle.

> **A Note on Metrics modules**:
>
> All the metric-integrations are built in separate modules and not available in the `feign-core` module. You will need to add them to your dependencies.

#### Dropwizard Metrics 4

```java
public class MyApp {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                         .addCapability(new Metrics4Capability())
                         .target(GitHub.class, "https://api.github.com");

    github.contributors("OpenFeign", "feign");
    // metrics will be available from this point onwards
  }
}
```

#### Dropwizard Metrics 5

```java
public class MyApp {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                         .addCapability(new Metrics5Capability())
                         .target(GitHub.class, "https://api.github.com");

    github.contributors("OpenFeign", "feign");
    // metrics will be available from this point onwards
  }
}
```

#### Micrometer

```java
public class MyApp {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                         .addCapability(new MicrometerCapability())
                         .target(GitHub.class, "https://api.github.com");

    github.contributors("OpenFeign", "feign");
    // metrics will be available from this point onwards
  }
}
```

See the [micrometer module README](./micrometer/README.md) for the full list of
metrics that are published, their tags and how to use `MicrometerObservationCapability`.

#### Static and Default Methods
Interfaces targeted by Feign may have static or default methods (if using Java 8+).
These allows Feign clients to contain logic that is not expressly defined by the underlying API.
For example, static methods make it easy to specify common client build configurations; default methods can be used to compose queries or define default parameters.

```java
interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

  @RequestLine("GET /users/{username}/repos?sort={sort}")
  List<Repo> repos(@Param("username") String owner, @Param("sort") String sort);

  default List<Repo> repos(String owner) {
    return repos(owner, "full_name");
  }

  /**
   * Lists all contributors for all repos owned by a user.
   */
  default List<Contributor> contributors(String user) {
    MergingContributorList contributors = new MergingContributorList();
    for(Repo repo : this.repos(owner)) {
      contributors.addAll(this.contributors(user, repo.getName()));
    }
    return contributors.mergeResult();
  }

  static GitHub connect() {
    return Feign.builder()
                .decoder(new GsonDecoder())
                .target(GitHub.class, "https://api.github.com");
  }
}
```


### Async execution via `CompletableFuture`

Feign 10.8 introduces a new builder `AsyncFeign` that allow methods to return `CompletableFuture` instances.

```java
interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  CompletableFuture<List<Contributor>> contributors(@Param("owner") String owner, @Param("repo") String repo);
}

public class MyApp {
  public static void main(String... args) {
    GitHub github = AsyncFeign.builder()
                         .decoder(new GsonDecoder())
                         .target(GitHub.class, "https://api.github.com");

    // Fetch and print a list of the contributors to this library.
    CompletableFuture<List<Contributor>> contributors = github.contributors("OpenFeign", "feign");
    for (Contributor contributor : contributors.get(1, TimeUnit.SECONDS)) {
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }
}
```

Initial implementation include 2 async clients:
- `AsyncClient.Default`
- `AsyncApacheHttp5Client`

## Maven’s Bill of Material (BOM)

Keeping all feign libraries on the same version is essential to avoid incompatible binaries. When consuming external dependencies, can be tricky to make sure only one version is present.

With that in mind, feign build generates a module called `feign-bom` that locks the versions for all `feign-*` modules.

The Bill Of Material is a special POM file that groups dependency versions that are known to be valid and tested to work together. This will reduce the developers’ pain of having to test the compatibility of different versions and reduce the chances to have version mismatches.


[Here](https://repo1.maven.org/maven2/io/github/openfeign/feign-bom/11.9/feign-bom-11.9.pom) is one example of what feign BOM file looks like.

#### Usage

```xml
<project>

...

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-bom</artifactId>
        <version>??feign.version??</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>
```
# Form Encoder

This module adds support for encoding **application/x-www-form-urlencoded** and **multipart/form-data** forms.

## Add dependency

Include the dependency to your app:

**Maven**:

```xml
<dependencies>
  ...
  <dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form</artifactId>
    <version>4.0.0</version>
  </dependency>
  ...
</dependencies>
```

**Gradle**:

```groovy
compile 'io.github.openfeign.form:feign-form:4.0.0'
```

## Requirements

The `feign-form` extension depend on `OpenFeign` and its *concrete* versions:

- all `feign-form` releases before **3.5.0** works with `OpenFeign` **9.\*** versions;
- starting from `feign-form`'s version **3.5.0**, the module works with `OpenFeign` **10.1.0** versions and greater.

> **IMPORTANT:** there is no backward compatibility and no any gurantee that the `feign-form`'s versions after **3.5.0** work with `OpenFeign` before **10.\***. `OpenFeign` was refactored in 10th release, so the best approach - use the freshest `OpenFeign` and `feign-form` versions.

Notes:

- [spring-cloud-openfeign](https://github.com/spring-cloud/spring-cloud-openfeign) uses `OpenFeign` **9.\*** till **v2.0.3.RELEASE** and uses **10.\*** after. Anyway, the dependency already has suitable `feign-form` version, see [dependency pom](https://github.com/spring-cloud/spring-cloud-openfeign/blob/master/spring-cloud-openfeign-dependencies/pom.xml#L19), so you don't need to specify it separately;

- `spring-cloud-starter-feign` is a **deprecated** dependency and it always uses the `OpenFeign`'s **9.\*** versions.

## Usage

Add `FormEncoder` to your `Feign.Builder` like so:

```java
SomeApi github = Feign.builder()
                      .encoder(new FormEncoder())
                      .target(SomeApi.class, "http://api.some.org");
```

Moreover, you can decorate the existing encoder, for example JsonEncoder like this:

```java
SomeApi github = Feign.builder()
                      .encoder(new FormEncoder(new JacksonEncoder()))
                      .target(SomeApi.class, "http://api.some.org");
```

And use them together:

```java
interface SomeApi {

  @RequestLine("POST /json")
  @Headers("Content-Type: application/json")
  void json (Dto dto);

  @RequestLine("POST /form")
  @Headers("Content-Type: application/x-www-form-urlencoded")
  void from (@Param("field1") String field1, @Param("field2") String[] values);
}
```

You can specify two types of encoding forms by `Content-Type` header.

### application/x-www-form-urlencoded

```java
interface SomeApi {

  @RequestLine("POST /authorization")
  @Headers("Content-Type: application/x-www-form-urlencoded")
  void authorization (@Param("email") String email, @Param("password") String password);

  // Group all parameters within a POJO
  @RequestLine("POST /user")
  @Headers("Content-Type: application/x-www-form-urlencoded")
  void addUser (User user);

  class User {

    Integer id;

    String name;
  }
}
```

### multipart/form-data

```java
interface SomeApi {

  // File parameter
  @RequestLine("POST /send_photo")
  @Headers("Content-Type: multipart/form-data")
  void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") File photo);

  // byte[] parameter
  @RequestLine("POST /send_photo")
  @Headers("Content-Type: multipart/form-data")
  void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") byte[] photo);

  // FormData parameter
  @RequestLine("POST /send_photo")
  @Headers("Content-Type: multipart/form-data")
  void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") FormData photo);

  // Group all parameters within a POJO
  @RequestLine("POST /send_photo")
  @Headers("Content-Type: multipart/form-data")
  void sendPhoto (MyPojo pojo);

  class MyPojo {

    @FormProperty("is_public")
    Boolean isPublic;

    File photo;
  }
}
```

In the example above, the `sendPhoto` method uses the `photo` parameter using three different supported types.

* `File` will use the File's extension to detect the `Content-Type`;
* `byte[]` will use `application/octet-stream` as `Content-Type`;
* `FormData` will use the `FormData`'s `Content-Type` and `fileName`;
* Client's custom POJO for grouping parameters (including types above).

`FormData` is custom object that wraps a `byte[]` and defines a `Content-Type` and `fileName` like this:

```java
  FormData formData = new FormData("image/png", "filename.png", myDataAsByteArray);
  someApi.sendPhoto(true, formData);
```

### Spring MultipartFile and Spring Cloud Netflix @FeignClient support

You can also use Form Encoder with Spring `MultipartFile` and `@FeignClient`.

Include the dependencies to your project's pom.xml file:

```xml
<dependencies>
  <dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form</artifactId>
    <version>4.0.0</version>
  </dependency>
  <dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form-spring</artifactId>
    <version>4.0.0</version>
  </dependency>
</dependencies>
```

```java
@FeignClient(
    name = "file-upload-service",
    configuration = FileUploadServiceClient.MultipartSupportConfig.class
)
public interface FileUploadServiceClient extends IFileUploadServiceClient {

  public class MultipartSupportConfig {

    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;

    @Bean
    public Encoder feignFormEncoder () {
      return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }
  }
}
```

Or, if you don't need Spring's standard encoder:

```java
@FeignClient(
    name = "file-upload-service",
    configuration = FileUploadServiceClient.MultipartSupportConfig.class
)
public interface FileUploadServiceClient extends IFileUploadServiceClient {

  public class MultipartSupportConfig {

    @Bean
    public Encoder feignFormEncoder () {
      return new SpringFormEncoder();
    }
  }
}
```

Thanks to [tf-haotri-pham](https://github.com/tf-haotri-pham) for his feature, which makes use of Apache commons-fileupload library, which handles the parsing of the multipart response. The body data parts are held as byte arrays in memory.

To use this feature, include SpringManyMultipartFilesReader in the list of message converters for the Decoder and have the Feign client return an array of MultipartFile:

```java
@FeignClient(
    name = "${feign.name}",
    url = "${feign.url}"
    configuration = DownloadClient.ClientConfiguration.class
)
public interface DownloadClient {

  @RequestMapping("/multipart/download/{fileId}")
  MultipartFile[] download(@PathVariable("fileId") String fileId);

  class ClientConfiguration {

    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;

    @Bean
    public Decoder feignDecoder () {
      List<HttpMessageConverter<?>> springConverters =
            messageConverters.getObject().getConverters();

      List<HttpMessageConverter<?>> decoderConverters =
            new ArrayList<HttpMessageConverter<?>>(springConverters.size() + 1);

      decoderConverters.addAll(springConverters);
      decoderConverters.add(new SpringManyMultipartFilesReader(4096));

      HttpMessageConverters httpMessageConverters = new HttpMessageConverters(decoderConverters);

      return new SpringDecoder(new ObjectFactory<HttpMessageConverters>() {

        @Override
        public HttpMessageConverters getObject() {
          return httpMessageConverters;
        }
      });
    }
  }
}
```

---

### Overview (`annotation-error-decoder/README.md`)

Annotation Error Decoder
=========================

This module allows to annotate Feign's interfaces with annotations to generate Exceptions based on error codes

To use AnnotationErrorDecoder with Feign, add the Annotation Error Decoder module to your classpath. Then, configure
Feign to use the AnnotationErrorDecoder:

```java
GitHub github = Feign.builder()
                     .errorDecoder(
                         AnnotationErrorDecoder.builderFor(GitHub.class).build()
                     )
                     .target(GitHub.class, "https://api.github.com");
```

## Leveraging the annotations and priority order
For annotation decoding to work, the class must be annotated with `@ErrorHandling` tags or meta-annotations.
The tags are valid in both the class level as well as method level. They will be treated from 'most specific' to 
'least specific' in the following order:
* A code specific exception defined on the method 
* A code specific exception defined on the class
* The default exception of the method
* The default exception of the class

```java
@ErrorHandling(codeSpecific =
    {
        @ErrorCodes( codes = {401}, generate = UnAuthorizedException.class),
        @ErrorCodes( codes = {403}, generate = ForbiddenException.class),
        @ErrorCodes( codes = {404}, generate = UnknownItemException.class),
    },
    defaultException = ClassLevelDefaultException.class
)
interface GitHub {

    @ErrorHandling(codeSpecific =
        {
            @ErrorCodes( codes = {404}, generate = NonExistentRepoException.class),
            @ErrorCodes( codes = {502, 503, 504}, generate = RetryAfterCertainTimeException.class),
        },
        defaultException = FailedToGetContributorsException.class
    )
    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}
```
In the above example, error responses to 'contributors' would hence be mapped as follows by status codes:

| Code        | Exception                          | Reason                |
| ----------- | --------------------------------   | --------------------- |
| 401         | `UnAuthorizedException`            | from Class definition |
| 403         | `ForbiddenException`               | from Class definition |
| 404         | `NonExistenRepoException`          | from Method definition, note that the class generic exception won't be thrown here |
| 502,503,504 | `RetryAfterCertainTimeException`   | from method definition. Note that you can have multiple error codes generate the same type of exception |
| Any Other   | `FailedToGetContributorsException` | from Method default   |

For a class level default exception to be thrown, the method must not have a `defaultException` defined, nor must the error code
be mapped at either the method or class level.

If the return code cannot be mapped to any code and no default exceptions have been configured, then the decoder will
drop to a default decoder (by default, the standard one provided by feign). You can change the default drop-into decoder
as follows:

```java
GitHub github = Feign.builder()
                     .errorDecoder(
                         AnnotationErrorDecoder.builderFor(GitHub.class)
                            .withDefaultDecoder(new MyOtherErrorDecoder())
                            .build()
                     )
                     .target(GitHub.class, "https://api.github.com");
```

 
## Complex Exceptions

Any exception can be used if they have a default constructor:

```java
class DefaultConstructorException extends Exception {}
```

However, if you want to have parameters (such  as the feign.Request object or response body or response headers), you have to annotate its 
constructor appropriately (the body annotation is optional, provided there aren't paramters which will clash)

All the following examples are valid exceptions:
```java
class JustBody extends Exception {

    @FeignExceptionConstructor
    public JustBody(String body) {

    }
}
class JustRequest extends Exception {

    @FeignExceptionConstructor
    public JustRequest(Request request) {

    }
}
class RequestAndResponseBody extends Exception {

    @FeignExceptionConstructor
    public RequestAndResponseBody(Request request, String body) {

    }
}
//Headers must be of type Map<String, Collection<String>>
class BodyAndHeaders extends Exception {

    @FeignExceptionConstructor
    public BodyAndHeaders(@ResponseBody String body, @ResponseHeaders Map<String, Collection<String>> headers) {

    }
}
class RequestAndResponseBodyAndHeaders extends Exception {

    @FeignExceptionConstructor
    public RequestAndResponseBodyAndHeaders(Request request, @ResponseBody String body, @ResponseHeaders Map<String, Collection<String>> headers) {

    }
}
class JustHeaders extends Exception {

    @FeignExceptionConstructor
    public JustHeaders(@ResponseHeaders Map<String, Collection<String>> headers) {

    }
}
```

If you want to have the body decoded, you'll need to pass a decoder at construction time (just as for normal responses):

```java
GitHub github = Feign.builder()
                     .errorDecoder(
                         AnnotationErrorDecoder.builderFor(GitHub.class)
                            .withResponseBodyDecoder(new JacksonDecoder())
                            .build()
                     )
                     .target(GitHub.class, "https://api.github.com");
```

This will enable you to create exceptions where the body is a complex pojo:

```java
class ComplexPojoException extends Exception {

    @FeignExceptionConstructor
    public ComplexPojoException(GithubExceptionResponse body) {
        if (body != null) {
            // extract data
        } else {
            // fallback code
        }
    }
}
//The pojo can then be anything you'd like provided the decoder can manage it
class GithubExceptionResponse {
    public String message;
    public int githubCode;
    public List<String> urlsForHelp;
}
```

It's worth noting that at setup/startup time, the generators are checked with a null value of the body.
If you don't do the null-checker, you'll get an NPE and startup will fail.


## Inheriting from other interface definitions
You can create a client interface that inherits from a different one. However, there are some limitations that
you should be aware of (for most cases, these shouldn't be an issue):
* The inheritance is not natural java inheritance of annotations - as these don't work on interfaces
* Instead, the error looks at the class and if it finds the `@ErrorHandling` annotation, it uses that one.
* If not, it will look at *all* the interfaces the main interface `extends` - but it does so in the order the
java API gives it - so order is not guaranteed.
* If it finds the annotation in one of those parents, it uses that definition, without looking at any other
* That means that if more than one interface was extended which contained the `@ErrorHandling` annotation, we can't
really guarantee which one of the parents will be selected and you should really do handling at the child interface
  * so far, the java API seems to return in order of definition after the `extends`, but it's a really bad practice
  if you have to depend on that... so our suggestion: don't.

That means that as long as you only ever extend from a base interface (where you may decide that all 404's are "NotFoundException", for example)
then you should be ok. But if you get complex in polymorphism, all bets are off - so don't go crazy!

Example:
In the following code:
* The base `FeignClientBase` interface defines a default set of exceptions at class level
* the `GitHub1` and `GitHub2` interfaces will inherit the class-level error handling, which means that
any 401/403/404 will be handled correctly (provided the method doesn't specify a more specific exception)
* the `GitHub3` interface however, by defining its own error handling, will handle all 401's, but not the
403/404's since there's no merging/etc (not really in the plan to implement either...)
```
/* Detailed source-code truncated for AI context efficiency. */
```

## Meta-annotations
When you want to share the same configuration of one `@ErrorHandling` annotation the `@ErrorHandling` annotation 
can be moved to a meta-annotation. Then later on this meta-annotation can be used on a method or at class level to 
reduce the amount duplicated code. A meta-annotation is a special annotation that contains the `@ErrorHandling` 
annotation and possibly other annotations, e.g. Spring-Rest annotations.

There are some limitations and rules to keep in mind when using meta-annotation:
- inheritance for meta-annotations when using interface inheritance is supported and is following the same rules as for
  interface inheritance (see above)
  - `@ErrorHandling` has **precedence** over any meta-annotation when placed together on a class or method
  - a meta-annotation on a child interface (method or class) has **precedence** over the error handling defined in the 
    parent interface
- having a meta-annotation on a meta-annotation is not supported, only the annotations on a type are checked for a 
  `@ErrorHandling`
- when multiple meta-annotations with an `@ErrorHandling` annotation are present on a class or method the first one
  which is returned by java API is used to figure out the error handling, the others are not considered, so it is
  advisable to have only one meta-annotation on each method or class as the order is not guaranteed. 
- **no merging** of configurations is supported, e.g. multiple meta-annotations on the same type, meta-annotation with 
  `@ErrorHandling` on the same type

Example:

Let's assume multiple methods need to handle the response-code `404` in the same way but differently what is 
specified in the `@ErrorHandling` annotation on the class-level. In that case, to avoid also duplicate annotation definitions
on the affected methods a meta-annotation can reduce the amount of code to be written to handle this `404` differently.

In the following code the status-code `404` is handled on a class level which throws an `UnknownItemException` for all
methods inside this interface. For the methods `contributors` and `languages` a different exceptions needs to be thrown,
in this case it is a `NoDataFoundException`. The `teams`method will still use the exception defined by the class-level
error handling annotation. To simplify the code a meta-annotation can be created and be used in the interface to keep 
the interface small and readable.

```java
@ErrorHandling(
    codeSpecific = {
        @ErrorCodes(codes = {404}, generate = NoDataFoundException.class),
    },
    defaultException = GithubRemoteException.class)
@Retention(RetentionPolicy.RUNTIME)
@interface NoDataErrorHandling {
}
```

Having this meta-annotation in place it can be used to transform the interface into a much smaller one, keeping the same
behavior.
- `contributers` will throw a `NoDataFoundException` for status code `404` as defined on method level and a 
  `GithubRemoteException` for all other status codes
- `languages` will throw  a `NoDataFoundException` for status code `404` as defined on method level and a 
  `GithubRemoteException` for all other status codes
- `teams` will throw  a `UnknownItemException` for status code `404` as defined on class level and a  
  `ClassLevelDefaultException` for all other status codes

Before:
```java
@ErrorHandling(codeSpecific =
    {
        @ErrorCodes( codes = {404}, generate = UnknownItemException.class)
    },
    defaultException = ClassLevelDefaultException.class
)
interface GitHub {
    @ErrorHandling(codeSpecific =
        {
            @ErrorCodes( codes = {404}, generate = NoDataFoundException.class)
        },
        defaultException = GithubRemoteException.class
    )
    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

    @ErrorHandling(codeSpecific =
        {
            @ErrorCodes( codes = {404}, generate = NoDataFoundException.class)
        },
        defaultException = GithubRemoteException.class
    )
    @RequestLine("GET /repos/{owner}/{repo}/languages")
    Map<String, Integer> languages(@Param("owner") String owner, @Param("repo") String repo);
    
    @ErrorHandling
    @RequestLine("GET /repos/{owner}/{repo}/team")
    List<Team> languages(@Param("owner") String owner, @Param("repo") String repo);
}
```

After:
```java
@ErrorHandling(codeSpecific =
    {
        @ErrorCodes( codes = {404}, generate = UnknownItemException.class)
    },
    defaultException = ClassLevelDefaultException.class
)
interface GitHub {
    @NoDataErrorHandling
    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

    @NoDataErrorHandling
    @RequestLine("GET /repos/{owner}/{repo}/languages")
    Map<String, Integer> languages(@Param("owner") String owner, @Param("repo") String repo);
    
    @ErrorHandling
    @RequestLine("GET /repos/{owner}/{repo}/team")
    List<Team> languages(@Param("owner") String owner, @Param("repo") String repo);
}
```

---

### Overview (`apt-test-generator/README.md`)

# Feign APT test generator
This module generates mock clients for tests based on feign interfaces

## Usage

Just need to add this module to dependency list and Java [Annotation Processing Tool](https://docs.oracle.com/javase/7/docs/technotes/guides/apt/GettingStarted.html) will automatically pick up the jar and generate test clients.

There are 2 main alternatives to include this to a project:

1. Just add to classpath and java compiler should automaticaly detect and run code generation. On maven this is done like this:

```xml
        <dependency>
            <groupId>io.github.openfeign.experimental</groupId>
            <artifactId>feign-apt-test-generator</artifactId>
            <version>${feign.version}</version>
            <scope>test</scope>
        </dependency>
```

1. Use a purpose build tool that allow to pick output location and don't mix dependencies onto classpath

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Overview (`benchmark/README.md`)

Feign Benchmarks
===================

This module includes [JMH](http://openjdk.java.net/projects/code-tools/jmh/) benchmarks for Feign.

=== Building the benchmark
Install and run `mvn -Dfeign.version=8.1.0` to produce `target/benchmark` pinned to version `8.1.0`

=== Running the benchmark
Execute `target/benchmark`

---

### Overview (`example-github-with-coroutine/README.md`)

GitHub Example With Coroutine
===================

This is an example of a simple json client.

=== Building example with Gradle
Install and run `gradle` to produce `build/github`

=== Building example with Maven
Install and run `mvn` to produce `target/github`

---

### Overview (`example-github/README.md`)

GitHub Example
===================

This is an example of a simple json client.

=== Building example with Gradle
Install and run `gradle` to produce `build/github`

=== Building example with Maven
Install and run `mvn` to produce `target/github`

---

### Overview (`example-wikipedia-with-springboot/README.md`)

Wikipedia Example with Springboot
=================================

This is an example of advanced json response parsing, including pagination.

---

### Overview (`example-wikipedia/README.md`)

Wikipedia Example
===================

This is an example of advanced json response parsing, including pagination.

=== Building example with Gradle
Install and run `gradle` to produce `build/wikipedia`

=== Building example with Maven
Install and run `mvn` to produce `target/wikipedia`

---

### Overview (`fastjson2/README.md`)

Fastjson2 Codec
===================

This module adds support for encoding and decoding JSON via Fastjson2.

Add `Fastjson2Codec` to your `Feign.Builder` like so:

```java
GitHub github = Feign.builder()
                     .codec(new Fastjson2Codec())
                     .target(GitHub.class, "https://api.github.com");
```

You can also configure the encoder and decoder separately:

```java
GitHub github = Feign.builder()
                     .encoder(new Fastjson2Encoder())
                     .decoder(new Fastjson2Decoder())
                     .target(GitHub.class, "https://api.github.com");
```

If you want to customize, provide features to the `Fastjson2Codec`:

```java
GitHub github = Feign.builder()
                     .codec(new Fastjson2Codec(
                         new JSONWriter.Feature[]{JSONWriter.Feature.WriteNonStringValueAsString},
                         new JSONReader.Feature[]{JSONReader.Feature.EmptyStringAsNull}))
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`googlehttpclient/README.md`)

# Google Http Client - Feign Client

This module is a feign [Client](https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Client.java) to use the java [Google Http Client](https://github.com/googleapis/google-http-java-client).

To use this, add to your classpath (via maven, or otherwise). Then configure Feign to use the GoogleHttpClient:

```java
GitHub github = Feign.builder()
                     .client(new GoogleHttpClient())
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`graphql-apt/README.md`)

Feign GraphQL APT
===================

Annotation processor for `feign-graphql` that generates Java records from GraphQL schemas at compile time.

Given a `@GraphqlSchema`-annotated interface, this processor:

- Parses the referenced `.graphql` schema file
- Validates all `@GraphqlQuery` strings against the schema
- Generates Java records for result types, input types, and enums
- Maps custom scalars to Java types via `@Scalar` annotations

See the [feign-graphql README](../graphql/README.md) for usage examples.

## Generated Output

### Result types with inner records

Nested result types are generated as inner records scoped to each query result. This ensures each query gets exactly the fields it selects, even when different queries target the same GraphQL type.

For a query like:

```graphql
{
  starship(id: "1") {
    id name
    location { planet sector }
    specs { lengthMeters classification }
  }
}
```

The processor generates a single file with inner records:

```java
public record StarshipResult(String id, String name, Location location, Specs specs) {

  public record Location(String planet, String sector) {}

  public record Specs(Integer lengthMeters, String classification) {}

}
```

Two different queries can select different fields from the same GraphQL type without conflict:

```java
// Query 1: selects location { planet }
public record CharByPlanet(String id, Location location) {
  public record Location(String planet) {}
}

// Query 2: selects location { sector region }
public record CharByRegion(String id, Location location) {
  public record Location(String sector, String region) {}
}
```

### Multiple root fields

An operation selecting several root fields gets a record with one component per field, mirroring the operation's own selection set rather than a single field's type:

```graphql
query authorPage($authorId: ID!) {
  books(authorId: $authorId) { id title }
  reviews(authorId: $authorId) { id rating }
}
```

```java
public record AuthorPage(Optional<List<Books>> books, Optional<List<Reviews>> reviews) {

  public record Books(String id, String title) {}

  public record Reviews(String id, Integer rating) {}

}
```

With a single root field the record still mirrors that field's type, so `{ character(id: "1") { id name } }` keeps generating `record CharacterResult(String id, String name)`.

### Conflicting return type error

If two queries use the same return type name but select different fields, the processor reports compilation errors on both methods showing which fields each selects:

```
error: Conflicting return type 'CharResult': method selects [id, email] but method 'query1()' already selects [id, name]
  CharResult query2();
             ^
error: Conflicting return type 'CharResult': method selects [id, name] but method 'query2()' selects [id, email]
  CharResult query1();
             ^
```

### Input types and enums

Input types and enums are generated as top-level files since they represent the full schema type:

```java
public record CreateCharacterInput(String name, String email, Episode appearsIn) {}
```

```java
public enum Episode {
  NEWHOPE,
  EMPIRE,
  JEDI
}
```

### Deprecated schema elements

Set `generateDeprecated = false` on `@GraphqlSchema`, or `generateDeprecated = Toggle.FALSE` on a single `@GraphqlQuery`, to hide `@deprecated` schema elements. Generated types leave out deprecated fields, input fields and enum values. A query that uses a deprecated field, argument, input field or enum value fails to compile:

```
GraphQL validation error at line 1, column 22: Field 'email' is deprecated (use emails instead) and generateDeprecated is false
```

## Maven Configuration

Add as a `provided` dependency so it runs during compilation but is not included at runtime:

```xml
<dependency>
  <groupId>io.github.openfeign.experimental</groupId>
  <artifactId>feign-graphql-apt</artifactId>
  <version>${feign.version}</version>
  <scope>provided</scope>
</dependency>
```

---

### Overview (`graphql/README.md`)

Feign GraphQL
===================

This module adds support for declarative GraphQL clients using Feign. It provides a `GraphqlContract`, `GraphqlEncoder`, and `GraphqlDecoder` that transform annotated interfaces into fully functional GraphQL clients.

The companion module `feign-graphql-apt` provides compile-time type generation from GraphQL schemas, producing Java records for query results and input types.

## Dependencies

Add both modules to use schema-driven type generation:

```xml
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-graphql</artifactId>
  <version>${feign.version}</version>
</dependency>

<dependency>
  <groupId>io.github.openfeign.experimental</groupId>
  <artifactId>feign-graphql-apt</artifactId>
  <version>${feign.version}</version>
  <scope>provided</scope>
</dependency>
```

## Basic Usage

Define a GraphQL schema in `src/main/resources`:

```graphql
type Query {
  user(id: ID!): User
}

type User {
  id: ID!
  name: String!
  email: String
}
```

Annotate your Feign interface with `@GraphqlSchema` pointing to the schema file and `@GraphqlQuery` on each method with the GraphQL query string:

```java
@GraphqlSchema("my-schema.graphql")
interface UserApi {

  @GraphqlQuery("query { user(id: $id) { id name email } }")
  User getUser(@Param("id") String id);
}
```

The annotation processor generates a Java record for `User` at compile time:

```java
public record User(String id, String name, String email) {}
```

Build the client using `GraphqlCapability`, which wires the contract, encoder, decoder, and request interceptor automatically. It takes any `JsonCodec`:

```java
// Using Jackson
UserApi api = Feign.builder()
    .addCapability(new GraphqlCapability(new JacksonCodec()))
    .target(UserApi.class, "https://api.example.com/graphql");

User user = api.getUser("123");
```

Any JSON codec works the same way:

```java
// Gson
new GraphqlCapability(new GsonCodec())

// Jackson 3
new GraphqlCapability(new Jackson3Codec())

// Fastjson2
new GraphqlCapability(new Fastjson2Codec())
```

## Mutations with Variables

Methods with parameters are sent as GraphQL variables:

```java
@GraphqlSchema("my-schema.graphql")
interface UserApi {

  @GraphqlQuery("mutation($input: CreateUserInput!) { createUser(input: $input) { id name } }")
  User createUser(@Param("input") CreateUserInput input);
}
```

The processor generates a record for the input type as well:

```java
public record CreateUserInput(String name, String email) {}
```

## Subscriptions

`subscription` operations are detected from the query text and executed over the
[graphql-transport-ws](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) WebSocket
protocol instead of HTTP. The endpoint is the target URL with its scheme swapped to `ws`/`wss`, and
one connection is opened per call.

Queries, mutations and subscriptions can live on the same interface: only subscriptions are routed
to a WebSocket, everything else goes over the regular Feign client — including whichever one you
configured with `.client(...)` — with its own timeouts, retryer and interceptors unchanged.

The return type decides how many events you get and whether the call blocks:

| Return type | Events | Behaviour |
| --- | --- | --- |
| `T` | first only | blocks until the first event, then unsubscribes |
| `Optional<T>` | first only | as above, empty if the server completes without one |
| `CompletableFuture<T>` | first only | returns immediately, completes with the first event |
| `Stream<T>` | all | returns once subscribed, then blocks on each element |
| `Flow.Publisher<T>` | all | returns immediately, elements are pushed to the subscriber |

```java
@GraphqlSchema("my-schema.graphql")
interface StockApi {

  @GraphqlQuery("subscription($symbol: String!) { priceChanged(symbol: $symbol) { symbol price } }")
  Price nextPrice(@Param("symbol") String symbol);

  @GraphqlQuery("subscription($symbol: String!) { priceChanged(symbol: $symbol) { symbol price } }")
  Stream<Price> onPrice(@Param("symbol") String symbol);

  @GraphqlQuery("subscription($symbol: String!) { priceChanged(symbol: $symbol) { symbol price } }")
  Flow.Publisher<Price> publishPrice(@Param("symbol") String symbol);
}
```

The single-event forms close the subscription as soon as they have their event. The multi-event
forms hand you the lifecycle: closing the `Stream` — or cancelling the `Flow.Subscription` — sends
`complete` and closes the WebSocket, so consume a `Stream` with try-with-resources:

```java
try (var prices = api.onPrice("ACME")) {
  prices.forEach(System.out::println);
}
```

`Stream` here is the ordinary `java.util.stream.Stream`: synchronous and pull-based, with no timeout
facilities of its own. So the blocking forms — `T`, `Optional<T>` and `Stream<T>` — are bounded by
an event timeout, which defaults to **60 seconds** and applies to each event rather than to the
subscription as a whole. Override it when creating the capability:

```java
Feign.builder()
    // wait at most 5s for each event; Duration.ZERO waits indefinitely
    .addCapability(new GraphqlCapability(new JacksonCodec(), Duration.ofSeconds(5)))
    .target(StockApi.class, "https://example.com/graphql");
```

Exceeding it raises `SocketTimeoutException` from the blocking call or the stream element. A
subscription that can legitimately sit idle for longer needs `Duration.ZERO`.

`Flow.Publisher<T>` and `CompletableFuture<T>` are deliberately *not* bounded by it — their caller
already owns the deadline, via cancelling the subscription or
`get(timeout, unit)`/`orTimeout(...)`. Cancelling either one closes the underlying WebSocket.

Those two asynchronous forms each need a worker for as long as the subscription is open. They run on
a bounded daemon pool by default; pass your own to own the lifecycle:

```java
new GraphqlCapability(new JacksonCodec(), Duration.ofSeconds(5), myExecutor)
```

Reads are demand-driven — the client asks the socket for another frame only once the consumer has
taken the previous event — so a slow consumer applies backpressure to the server instead of growing
a queue in memory.

`Flow.Publisher` is `java.util.concurrent.Flow.Publisher`, so it plugs into Reactor
(`JdkFlowAdapter.flowPublisherToFlux`) or RxJava (`Flowable.fromPublisher`) without extra
dependencies here.

A server `error` message, or `errors` inside a payload, is raised as `GraphqlErrorException`.
Request headers (for example `Authorization`) are forwarded to the WebSocket handshake.

## Custom Scalars

When your schema defines custom scalars, map them to Java types using `@Scalar` on default methods:

```graphql
scalar DateTime

type Event {
  id: ID!
  name: String!
  startTime: DateTime!
}
```

```java
@GraphqlSchema("event-schema.graphql")
interface EventApi {

  @Scalar("DateTime")
  default Instant dateTime() { return null; }

  @GraphqlQuery("query { events { id name startTime } }")
  List<Event> getEvents();
}
```

The processor maps `DateTime` fields to `java.time.Instant` in the generated record:

```java
public record Event(String id, String name, Instant startTime) {}
```

## Multiple Root Fields

A single operation can select more than one root field, and all of them are decoded — one round trip instead of one call per field:

```java
@GraphqlQuery("""
    query authorPage($authorId: ID!) {
      books(authorId: $authorId) { id title }
      reviews(authorId: $authorId) { id rating }
    }
    """)
AuthorPage authorPage(String authorId);
```

The processor generates a record with one component per root field, each with its own inner record:

```java
public record AuthorPage(Optional<List<Books>> books, Optional<List<Reviews>> reviews) {

  public record Books(String id, String title) {}

  public record Reviews(String id, Integer rating) {}

}
```

When the response carries several root fields the whole `data` map binds to the return type, so every field lands on its matching component:

```json
{"data": {"books": [...], "reviews": [...]}}
```

Operations with a single root field are unaffected: that field is still unwrapped and decoded into the return type directly.

## Single Result from Array Queries

When a GraphQL query returns an array type (e.g. `[User!]`) but the Java method declares a single return type, the decoder automatically unwraps the first element:

```java
@GraphqlQuery("query topUser($limit: Int!) { topUsers(limit: $limit) { id name email } }")
User topUser(int limit);
```

This is useful when using `limit: 1` to fetch a single result from a list query. If the array is empty, `null` is returned.

## Optional Return Types

Methods can return `Optional<T>` to safely handle nullable results:

```java
@GraphqlQuery("query getUser($id: String!) { getUser(id: $id) { id name email } }")
Optional<User> findUser(String id);
```

Returns `Optional.empty()` when the data is null or missing, and `Optional.of(value)` otherwise. This also works with array unwrapping:

```java
@GraphqlQuery("query topUser($limit: Int!) { topUsers(limit: $limit) { id name email } }")
Optional<User> findTopUser(int limit);
```

## Optional Fields

When using `Optional<>` fields in records, your JSON codec must support `java.util.Optional`. For Jackson, add `jackson-datatype-jdk8` and register it:

```java
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
UserApi api = Feign.builder()
    .addCapability(new GraphqlCapability(new JacksonCodec(mapper)))
    .target(UserApi.class, "https://api.example.com/graphql");
```

By default, nullable GraphQL fields (without `!`) are wrapped in `Optional<>` in generated records:

```graphql
type User {
  id: ID!          # non-null
  name: String!    # non-null
  email: String    # nullable
}
```

```java
public record User(String id, String name, Optional<String> email) {}
```

This is controlled by `useOptional` on `@GraphqlSchema` (defaults to `true`):

```java
@GraphqlSchema(value = "schema.graphql", useOptional = false)
```

Override per method with `Toggle`:

```java
@GraphqlQuery(value = "...", useOptional = Toggle.FALSE)
```

## Type Annotations on Generated Records

Add annotations to all generated records using `typeAnnotations` (no-arg) and `rawTypeAnnotations` (with args):

```java
@GraphqlSchema(
    value = "schema.graphql",
    typeAnnotations = {Builder.class, Jacksonized.class},
    rawTypeAnnotations = {"@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)"}
)
```

Generates:

```java
@Builder
@Jacksonized
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record User(String id, String name) {}
```

**Collision rule:** when the same annotation simple name appears in both `typeAnnotations` and `rawTypeAnnotations`, the class provides only the import and the raw string is used:

```java
typeAnnotations = {Builder.class},
rawTypeAnnotations = {"@Builder(toBuilder = true)"}
// Result: import lombok.Builder; + @Builder(toBuilder = true)
```

Override per method on `@GraphqlQuery` — non-empty arrays replace class-level values.

## Import-Only Classes with `uses`

When raw annotations reference classes not in `typeAnnotations`, use `uses` to add their imports:

```java
@GraphqlSchema(
    value = "schema.graphql",
    uses = {Min.class, Max.class, Pattern.class}
)
```

These classes are added as imports to all generated files but no annotations are generated from them.

## Non-Null Field Annotations

Automatically annotate all non-null (`!`) fields with `nonNullTypeAnnotations`:

```java
@GraphqlSchema(
    value = "schema.graphql",
    nonNullTypeAnnotations = {NotNull.class}
)
```

For `name: String!` and `email: String`, generates:

```java
public record User(@NotNull String name, Optional<String> email) {}
```

Same collision rule applies with `nonNullRawTypeAnnotations`. Overridable per method on `@GraphqlQuery`.

## Field-Level Annotations with `@GraphqlField`

Apply annotations or override types on specific fields. Repeatable, works on both the interface (class-level default) and individual methods:

```java
@GraphqlSchema(value = "schema.graphql", useOptional = false)
@GraphqlField(name = "email", typeAnnotations = {Email.class})
interface UserApi {

  @GraphqlQuery("{ user(id: \"1\") { id name email } }")
  @GraphqlField(name = "name", typeAnnotations = {NotBlank.class})
  UserResult getUser();
}
```

Generates:

```java
public record UserResult(String id, @NotBlank String name, @Email String email) {}
```

### Dot Notation for Nested Fields

Use dot notation to target fields in nested records:

```java
@GraphqlField(name = "location.coordinates.latitude", typeAnnotations = {NotNull.class})
@GraphqlField(name = "location.planet", typeAnnotations = {NotBlank.class})
```

### Field Type Override

Override the Java type for a field — useful when APIs return strings for dates without declaring custom scalars:

```java
@GraphqlField(name = "createdAt", type = ZonedDateTime.class)
@GraphqlField(name = "amount", type = BigDecimal.class)
```

Combines with annotations:

```java
@GraphqlField(name = "createdAt", type = ZonedDateTime.class, typeAnnotations = {NotNull.class})
```

Class-level `@GraphqlField` applies to all methods; method-level overrides for the same field name.

## Disabling Type Generation

If you provide your own model classes, disable automatic generation:

```java
@GraphqlSchema(value = "my-schema.graphql", generateTypes = false)
interface UserApi {
  // ...
}
```

Queries are still validated against the schema at compile time.

## Error Handling

GraphQL errors in the response throw `GraphqlErrorException`:

```java
try {
  User user = api.getUser("invalid-id");
} catch (GraphqlErrorException e) {
  String operation = e.operation();
  String errors = e.errors();
}
```

---

### Overview (`gson/README.md`)

Gson Codec
===================

This module adds support for encoding and decoding JSON via the Gson library.

Add `GsonCodec` to your `Feign.Builder` like so:

```java
GitHub github = Feign.builder()
                     .codec(new GsonCodec())
                     .target(GitHub.class, "https://api.github.com");
```

You can also configure the encoder and decoder separately:

```java
GitHub github = Feign.builder()
                     .encoder(new GsonEncoder())
                     .decoder(new GsonDecoder())
                     .target(GitHub.class, "https://api.github.com");
```

### Map<String, Object> and Numbers
The default constructors of `GsonEncoder` and `GsonDecoder` decoder numbers in
`Map<String, Object>` as Integer type. This prevents reading `{"counter", "1"}`
as `Map.of("counter", 1.0)`.

To change this, please use constructors that accept a Gson object.

---

### Overview (`hc5/README.md`)

Apache Http Components 5
========================

This module directs Feign's http requests to Apache's [HttpClient 5](https://hc.apache.org/httpcomponents-client-5.0.x/index.html).

To use HttpClient with Feign, add the `feign-hc5` module to your classpath. Then, configure Feign to use the `ApacheHttp5Client`:

```java
GitHub github = Feign.builder()
                     .client(new ApacheHttp5Client())
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`http-cache/README.md`)

Feign HTTP Cache
================

A `MethodInterceptor` that performs conditional HTTP revalidation using `ETag` and
`Last-Modified` headers. On the first call, the decoded response is stored alongside
its validators. Subsequent calls send `If-None-Match` / `If-Modified-Since` headers,
and a `304 Not Modified` response short-circuits to the cached value.

Marked `@Experimental` while the underlying `feign.interceptor.MethodInterceptor` API stabilises.

```xml
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-http-cache</artifactId>
  <version>${feign.version}</version>
</dependency>
```

Usage
-----

```java
HttpCacheStore store = new InMemoryHttpCacheStore();

Api api = Feign.builder()
    .methodInterceptor(new HttpCacheInterceptor(store))
    .target(Api.class, "https://example.com");
```

Plug in a different store (Caffeine, Redis, etc.) by implementing `HttpCacheStore`.

Customising
-----------

- `keyFn`: derive the cache key from an `Invocation` (e.g. include a tenant header).
- `cacheable`: decide which `RequestTemplate`s participate. The default is `GET` /
  `HEAD` only.

```java
HttpCacheInterceptor configured = new HttpCacheInterceptor(store)
    .key(invocation -> invocation.methodMetadata().configKey()
        + "|" + invocation.requestTemplate().url()
        + "|" + invocation.requestTemplate().headers().get("X-Tenant"))
    .cacheable(template -> "GET".equalsIgnoreCase(template.method()));
```

Caveats
-------

- 304 detection relies on the configured `ErrorDecoder` raising a `FeignException`
  for non-2xx responses (the default). A custom error decoder that swallows or
  transforms 304 will break cache hits.
- The store retains the **decoded** response object. Decoding happens once, on the
  first 200; on a 304 the cached object is returned as-is. Mutations on the returned
  object propagate to subsequent callers — return immutable values from your decoder
  if that matters.
- `Cache-Control` directives beyond `no-store` are not interpreted; freshness windows
  (`max-age`, etc.) are not enforced. Every cached entry triggers revalidation.

---

### Overview (`httpclient/README.md`)

Apache HttpClient
===================

This module directs Feign's http requests to Apache's [HttpClient](https://hc.apache.org/httpcomponents-client-ga/).

To use HttpClient with Feign, add the HttpClient module to your classpath. Then, configure Feign to use the HttpClient:

```java
GitHub github = Feign.builder()
                     .client(new ApacheHttpClient())
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`hystrix/README.md`)

Hystrix
===================

This module wraps Feign's http requests in [Hystrix](https://github.com/Netflix/Hystrix/), which enables the [Circuit Breaker Pattern](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern).

To use Hystrix with Feign, add the Hystrix module to your classpath. Then, configure Feign to use the `HystrixInvocationHandler`:

```java
GitHub github = HystrixFeign.builder()
        .target(GitHub.class, "https://api.github.com");
```

For asynchronous or reactive use, return `HystrixCommand<YourType>` or `CompletableFuture<YourType>`.

For RxJava compatibility, use `rx.Observable<YourType>` or `rx.Single<YourType>`. Rx types are <a href="http://reactivex.io/documentation/observable.html">cold</a>, which means a http call isn't made until there's a subscriber.

Methods that do *not* return [`HystrixCommand`](https://netflix.github.io/Hystrix/javadoc/com/netflix/hystrix/HystrixCommand.html), `CompletableFuture`, [`rx.Observable`](http://reactivex.io/RxJava/javadoc/rx/Observable.html) or `rx.Single` are still wrapped in a `HystrixCommand`, but `execute()` is automatically called for you.

```java
interface YourApi {
  @RequestLine("GET /yourtype/{id}")
  HystrixCommand<YourType> getYourType(@Param("id") String id);

  @RequestLine("GET /yourtype/{id}")
  Observable<YourType> getYourTypeObservable(@Param("id") String id);

  @RequestLine("GET /yourtype/{id}")
  Single<YourType> getYourTypeSingle(@Param("id") String id);

  @RequestLine("GET /yourtype/{id}")
  CompletableFuture<YourType> getYourTypeCompletableFuture(@Param("id") String id);

  @RequestLine("GET /yourtype/{id}")
  YourType getYourTypeSynchronous(@Param("id") String id);
}

YourApi api = HystrixFeign.builder()
                  .target(YourApi.class, "https://example.com");

// for reactive
api.getYourType("a").toObservable

// or apply hystrix to RxJava methods
api.getYourTypeObservable("a")

// for asynchronous
api.getYourType("a").queue();

// for synchronous
api.getYourType("a").execute();

// or for a CompletableFuture
api.getYourTypeCompletableFuture("a").thenApply(o -> "b");

// or to apply hystrix to existing feign methods.
api.getYourTypeSynchronous("a");
```

### Group and Command keys

By default, Hystrix group keys match the target name, and the target name is usually the base url.
Hystrix command keys are the same as logging keys, which are equivalent to javadoc references.

For example, for the canonical GitHub example...

* the group key would be "https://api.github.com" and
* the command key would be "GitHub#contributors(String,String)"

You can use `HystrixFeign.Builder#setterFactory(SetterFactory)` to customize this, for example, to
read key mappings from configuration or annotations.

Ex.
```java
SetterFactory commandKeyIsRequestLine = (target, method) -> {
  String groupKey = target.name();
  String commandKey = method.getAnnotation(RequestLine.class).value();
  return HystrixCommand.Setter
      .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
      .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
};

api = HystrixFeign.builder()
                  .setterFactory(commandKeyIsRequestLine)
                  ...
```

### Fallback support

Fallbacks are known values, which you return when there's an error invoking an http method.
For example, you can return a cached result as opposed to raising an error to the caller. To use
this feature, pass a safe implementation of your target interface as the last parameter to `HystrixFeign.Builder.target`.

Here's an example:

```java
// When dealing with fallbacks, it is less tedious to keep interfaces small.
interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<String> contributors(@Param("owner") String owner, @Param("repo") String repo);
}

// This instance will be invoked if there are errors of any kind.
GitHub fallback = (owner, repo) -> {
  if (owner.equals("Netflix") && repo.equals("feign")) {
    return Arrays.asList("stuarthendren"); // inspired this approach!
  } else {
    return Collections.emptyList();
  }
};

GitHub github = HystrixFeign.builder()
                            ...
                            .target(GitHub.class, "https://api.github.com", fallback);
```

#### Considering the cause

The cause of the fallback is logged by default to FINE level. You can programmatically inspect
the cause by making your own `FallbackFactory`. In many cases, the cause will be a `FeignException`,
which includes the http status.

Here's an example of using `FallbackFactory`:

```java
// This instance will be invoked if there are errors of any kind.
FallbackFactory<GitHub> fallbackFactory = cause -> (owner, repo) -> {
  if (cause instanceof FeignException && ((FeignException) cause).status() == 403) {
    return Collections.emptyList();
  } else {
    return Arrays.asList("yogi");
  }
};

GitHub github = HystrixFeign.builder()
                            ...
                            .target(GitHub.class, "https://api.github.com", fallbackFactory);
```

---

### Overview (`jackson-jaxb/README.md`)

Jackson-Jaxb Codec
===================

This module adds support for encoding and decoding JSON via JAXB.

Add `JacksonJaxbJsonEncoder` and/or `JacksonJaxbJsonDecoder` to your `Feign.Builder` like so:

```java
GitHub github = Feign.builder()
                     .encoder(new JacksonJaxbJsonEncoder())
                     .decoder(new JacksonJaxbJsonDecoder())
                     .target(GitHub.class, "https://api.github.com");
```

If you want to customize the `ObjectMapper` that is used, provide it to the `JacksonJaxbJsonEncoder` and `JacksonJaxbJsonDecoder`:

```java
ObjectMapper mapper = new ObjectMapper()
        .setSerializationInclusion(JsonInclude.Include.NON_NULL)
        .configure(SerializationFeature.INDENT_OUTPUT, true)
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

GitHub github = Feign.builder()
                     .encoder(new JacksonJaxbJsonEncoder(mapper))
                     .decoder(new JacksonJaxbJsonDecoder(mapper))
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`jackson-jr/README.md`)

Jackson Jr Codec
===================

This module adds support for encoding and decoding JSON via Jackson Jr. This
is a significantly smaller and faster version of Jackson, which may be of benefit
in smaller runtime containers.

Add `JacksonJrEncoder` and/or `JacksonJrDecoder` to your `Feign.Builder` like so:

```java
GitHub github = Feign.builder()
                     .encoder(new JacksonJrEncoder())
                     .decoder(new JacksonJrDecoder())
                     .target(GitHub.class, "https://api.github.com");
```

If you want to customize the `JSON` object that is used, provide it to the `JacksonJrEncoder` and `JacksonJrDecoder`:

```java
JSON json = Json.builder()
                .register(JacksonAnnotationExtension.builder()
                    .withVisibility(JsonAutoDetect.Value.defaultVisibility())
                    .build())
                .build();

GitHub github = Feign.builder()
                     .encoder(new JacksonJrEncoder(json))
                     .decoder(new JacksonJrDecoder(json))
                     .target(GitHub.class, "https://api.github.com");
```

Customisation is also possible by passing `JacksonJrExtension` objects
into the constructor of the `JacksonJrEncoder` or `JacksonJrDecoder`:

```java
List<JacksonJrExtension> extensions = singletonList(JacksonAnnotationExtension.builder()
        .withVisibility(JsonAutoDetect.Value.defaultVisibility())
        .build());
GitHub github = Feign.builder()
                     .encoder(new JacksonJrEncoder(extensions))
                     .decoder(new JacksonJrDecoder(extensions))
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`jackson/README.md`)

Jackson Codec
===================

This module adds support for encoding and decoding JSON via Jackson.

Add `JacksonCodec` to your `Feign.Builder` like so:

```java
GitHub github = Feign.builder()
                     .codec(new JacksonCodec())
                     .target(GitHub.class, "https://api.github.com");
```

If you want to customize the `ObjectMapper` that is used, provide it to the `JacksonCodec`:

```java
ObjectMapper mapper = new ObjectMapper()
        .setSerializationInclusion(JsonInclude.Include.NON_NULL)
        .configure(SerializationFeature.INDENT_OUTPUT, true)
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

GitHub github = Feign.builder()
                     .codec(new JacksonCodec(mapper))
                     .target(GitHub.class, "https://api.github.com");
```

You can also configure the encoder and decoder separately:

```java
GitHub github = Feign.builder()
                     .encoder(new JacksonEncoder())
                     .decoder(new JacksonDecoder())
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`jackson3/README.md`)

Jackson 3 Codec
===================

This module adds support for encoding and decoding JSON via Jackson 3.

**Note:** Jackson 3 requires Java 17 or higher.

Add `Jackson3Codec` to your `Feign.Builder` like so:

```java
GitHub github = Feign.builder()
                     .codec(new Jackson3Codec())
                     .target(GitHub.class, "https://api.github.com");
```

If you want to customize the `JsonMapper` that is used, provide it to the `Jackson3Codec`:

```java
JsonMapper mapper = JsonMapper.builder()
        .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL))
        .enable(SerializationFeature.INDENT_OUTPUT)
        .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
        .build();

GitHub github = Feign.builder()
                     .codec(new Jackson3Codec(mapper))
                     .target(GitHub.class, "https://api.github.com");
```

You can also configure the encoder and decoder separately:

```java
GitHub github = Feign.builder()
                     .encoder(new Jackson3Encoder())
                     .decoder(new Jackson3Decoder())
                     .target(GitHub.class, "https://api.github.com");
```

## Migration from Jackson 2 to Jackson 3

The main differences are:

- Package changes: `com.fasterxml.jackson` → `tools.jackson` (except for `com.fasterxml.jackson.annotation`)
- GroupId changes: `com.fasterxml.jackson.core` → `tools.jackson.core`
- `ObjectMapper` is immutable and must be configured via `JsonMapper.builder()`
- Java 17 minimum requirement

---

### Overview (`jakarta/README.md`)

# Feign Jakarta
This module overrides annotation processing to instead use standard ones supplied by the Jakarta specification.  This is currently targeted at the 3.1 spec.

## Limitations
While it may appear possible to reuse the same interface across client and server, bear in mind that Jakarta resource
 annotations were not designed to be processed by clients. Finally, Jakarta is a large spec and attempts to implement
it completely would be a project larger than feign itself.  In other words, this implementation is *best efforts* and 
concedes far from 100% compatibility with server interface behavior.

## Currently Supported Annotation Processing
Feign only supports processing java interfaces (not abstract or concrete classes).

Here are a list of behaviors currently supported.
### Type Annotations
#### `@Path`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathParam` annotations.
### Method Annotations
#### `@HttpMethod` meta-annotation (present on `@GET`, `@POST`, etc.)
Sets the request method.
#### `@Path`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathParam` annotations.
#### `@Produces`
Adds all values into the `Accept` header.
#### `@Consumes`
Adds the first value as the `Content-Type` header.
### Parameter Annotations
#### `@PathParam`
Links the value of the corresponding parameter to a template variable declared in the path.
#### `@QueryParam`
Links the value of the corresponding parameter to a query parameter.  When invoked, null will skip the query param.
#### `@HeaderParam`
Links the value of the corresponding parameter to a header.
#### `@FormParam`
Links the value of the corresponding parameter to a key passed to `Encoder.Text<Map<String, Object>>.encode()`.
#### `@BeanParm`
Aggregates the above supported parameter annotations under a single value object.

---

### Overview (`java11/README.md`)

# feign-java11

This module directs Feign's http requests to Java11 [New HTTP/2 Client](https://openjdk.java.net/jeps/321) that implements HTTP/2.

To use New HTTP/2 Client with Feign, use Java SDK 11. Then, configure Feign to use the Http2Client:

```java
GitHub github = Feign.builder()
                     .client(new Http2Client())
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`jaxb/README.md`)

JAXB Codec
===================

This module adds support for encoding and decoding XML via JAXB.

Add `JAXBCodec` to your `Feign.Builder` like so:

```java
JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder()
    .withMarshallerJAXBEncoding("UTF-8")
    .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
    .build();

Response response = Feign.builder()
                         .codec(new JAXBCodec(jaxbFactory))
                         .target(Response.class, "https://apihost");
```

You can also configure the encoder and decoder separately:

```java
Response response = Feign.builder()
                         .encoder(new JAXBEncoder(jaxbFactory))
                         .decoder(new JAXBDecoder(jaxbFactory))
                         .target(Response.class, "https://apihost");
```

`JAXBDecoder` can also be created with a builder to allow overriding some default parser options:

```java
JAXBDecoder jaxbDecoder = new JAXBDecoder.Builder()
    .withJAXBContextFactory(jaxbFactory)
    .withNamespaceAware(false) // true by default
    .build();
```

Usage
===================

The feign library is available from [Maven Central](https://mvnrepository.com/artifact/io.github.openfeign/feign-jaxb).

```xml
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-jaxb</artifactId>
    <version>??feing-jaxb-version??</version>
</dependency>

```

---

### Overview (`jaxrs/README.md`)

# Feign JAXRS
This module overrides annotation processing to instead use standard ones supplied by the JAX-RS specification.  This is currently targeted at the 1.1 spec.

## Limitations
While it may appear possible to reuse the same interface across client and server, bear in mind that JAX-RS resource
 annotations were not designed to be processed by clients.  Moreover, JAX-RS 2.0 has a different package hierarchy for
client invocation.  Finally, JAX-RS is a large spec and attempts to implement it completely would be a project larger
than feign itself.  In other words, this implementation is *best efforts* and concedes far from 100% compatibility with
server interface behavior.

## Currently Supported Annotation Processing
Feign only supports processing java interfaces (not abstract or concrete classes).

Here are a list of behaviors currently supported.
### Type Annotations
#### `@Path`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathParam` annotations.
### Method Annotations
#### `@HttpMethod` meta-annotation (present on `@GET`, `@POST`, etc.)
Sets the request method.
#### `@Path`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathParam` annotations.
#### `@Produces`
Adds all values into the `Accept` header.
#### `@Consumes`
Adds the first value as the `Content-Type` header.
### Parameter Annotations
#### `@PathParam`
Links the value of the corresponding parameter to a template variable declared in the path.
#### `@QueryParam`
Links the value of the corresponding parameter to a query parameter.  When invoked, null will skip the query param.
#### `@HeaderParam`
Links the value of the corresponding parameter to a header.
#### `@FormParam`
Links the value of the corresponding parameter to a key passed to `Encoder.Text<Map<String, Object>>.encode()`.

---

### Overview (`jaxrs2/README.md`)

# Feign JAXRS 2
This module overrides annotation processing to instead use standard ones supplied by the JAX-RS specification.  This is currently targeted at the 1.1 spec.

## Limitations
While it may appear possible to reuse the same interface across client and server, bear in mind that JAX-RS resource
 annotations were not designed to be processed by clients.  Moreover, JAX-RS 2.0 has a different package hierarchy for
client invocation.  Finally, JAX-RS is a large spec and attempts to implement it completely would be a project larger
than feign itself.  In other words, this implementation is *best efforts* and concedes far from 100% compatibility with
server interface behavior.

## Currently Supported Annotation Processing
Feign only supports processing java interfaces (not abstract or concrete classes).

ISE is raised when any annotation's value is empty or null.  Ex. `Path("")` raises an ISE.

Here are a list of behaviors currently supported.
### Type Annotations
#### `@Path`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathParam` annotations.
### Method Annotations
#### `@HttpMethod` meta-annotation (present on `@GET`, `@POST`, etc.)
Sets the request method.
#### `@Path`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathParam` annotations.
#### `@Produces`
Adds all values into the `Accept` header.
#### `@Consumes`
Adds the first value as the `Content-Type` header.
### Parameter Annotations
#### `@PathParam`
Links the value of the corresponding parameter to a template variable declared in the path.
#### `@QueryParam`
Links the value of the corresponding parameter to a query parameter.  When invoked, null will skip the query param.
#### `@HeaderParam`
Links the value of the corresponding parameter to a header.
#### `@FormParam`
Links the value of the corresponding parameter to a key passed to `Encoder.Text<Map<String, Object>>.encode()`.
#### `@BeanParm`
Aggregates the above supported parameter annotations under a single value object.

---

### Overview (`jaxrs3/README.md`)

# Feign Jakarta
This module overrides annotation processing to instead use standard ones supplied by the Jakarta specification.  This is currently targeted at the 3.1 spec.

## Limitations
While it may appear possible to reuse the same interface across client and server, bear in mind that Jakarta resource
 annotations were not designed to be processed by clients. Finally, Jakarta is a large spec and attempts to implement
it completely would be a project larger than feign itself.  In other words, this implementation is *best efforts* and 
concedes far from 100% compatibility with server interface behavior.

## Currently Supported Annotation Processing
Feign only supports processing java interfaces (not abstract or concrete classes).

Here are a list of behaviors currently supported.
### Type Annotations
#### `@Path`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathParam` annotations.
### Method Annotations
#### `@HttpMethod` meta-annotation (present on `@GET`, `@POST`, etc.)
Sets the request method.
#### `@Path`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathParam` annotations.
#### `@Produces`
Adds all values into the `Accept` header.
#### `@Consumes`
Adds the first value as the `Content-Type` header.
### Parameter Annotations
#### `@PathParam`
Links the value of the corresponding parameter to a template variable declared in the path.
#### `@QueryParam`
Links the value of the corresponding parameter to a query parameter.  When invoked, null will skip the query param.
#### `@HeaderParam`
Links the value of the corresponding parameter to a header.
#### `@FormParam`
Links the value of the corresponding parameter to a key passed to `Encoder.Text<Map<String, Object>>.encode()`.
#### `@BeanParm`
Aggregates the above supported parameter annotations under a single value object.

---

### Overview (`jaxrs4/README.md`)

# Feign Jakarta JaxRS 4

Feign Jakarta JaxRS 4 module is dedicated to testing the compatibility of the JaxRS 3 module with JaxRS 4 to ensure seamless integration with the latest version.

A separated module is required due java version requirement.

---

### Overview (`jdk-json/README.md`)

JDK JSON Codec
===================

This module lets you encode and decode JSON using the JDK's built-in [Simple JSON API][JEP 540] (`jdk.incubator.json`).
You do not need to use any third party JSON libraries.

This API is an incubator module for JDK 28, so this module requires Java 28 or higher. You also must resolve the module explicitly:

```
java --add-modules jdk.incubator.json ...
```

Incubator APIs can change incompatibly between JDK releases, so expect this module to follow along.

## Dependency

```xml
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-jdk-json</artifactId>
  <version>${feign.version}</version>
</dependency>
```

## Usage

Add `JdkJsonEncoder` and/or `JdkJsonDecoder` to your `Feign.Builder`:

```java
interface GitHub {

  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  JsonArray contributors(@Param("owner") String owner, @Param("repo") String repo);

  @RequestLine("POST /repos/{owner}/{repo}/issues")
  @Headers("Content-Type: application/json")
  JsonObject createIssue(@Param("owner") String owner, @Param("repo") String repo, JsonValue issue);
}

GitHub github = Feign.builder()
    .decoder(new JdkJsonDecoder())
    .encoder(new JdkJsonEncoder())
    .target(GitHub.class, "https://api.github.com");

for (JsonValue contributor : github.contributors("OpenFeign", "feign").asList()) {
  System.out.println(contributor.get("login").asString());
}

JsonObject issue = github.createIssue("OpenFeign", "feign", JsonObject.of(Map.of(
    "title", JsonString.of("Found a bug"),
    "labels", JsonArray.of(List.of(JsonString.of("bug"))))));
```

The JDK API has no data binding, so the codec works with the JSON tree:

* `JdkJsonDecoder` decodes into `JsonValue` or any of its subtypes (`JsonObject`, `JsonArray`, `JsonString`, `JsonNumber`, `JsonBoolean`, `JsonNull`). It can also return the raw `String` body.
  If the JSON is malformed (like having duplicate member names), it throws a `DecodeException` caused by a `JsonParseException`. If the JSON type is wrong (for example, you get an array but asked for a `JsonObject`), it throws a `DecodeException`.
  `404` (if you use `dismiss404()`), `204`, and empty bodies decode to `null`.
* `JdkJsonEncoder` writes any `JsonValue` as compact JSON.

## Building

The module is part of the `jdk28-modules` Maven profile and needs a JDK 28 entry in `~/.m2/toolchains.xml`:

```
./mvnw -Pjdk28-modules -pl jdk-json -am install
```

[JEP 540]: https://openjdk.org/jeps/540

---

### Overview (`json/README.md`)

JSON-java Codec
===================

This module adds support for encoding and decoding [JSON][] via [JSON-java][].

Add `JsonEncoder` and/or `JsonDecoder` to your `Feign.Builder` like so:

```java
api = Feign.builder()
           .decoder(new JsonDecoder())
           .encoder(new JsonEncoder())
           .target(GitHub.class, "https://api");
```

[JSON]: https://www.json.org/json-en.html
[JSON-java]: https://github.com/stleary/JSON-java

---

### Overview (`micrometer/README.md`)

Micrometer
===================

This module integrates Feign with [Micrometer](https://micrometer.io/) so that
HTTP calls made through Feign clients are observable through any Micrometer-supported
monitoring system (Prometheus, Datadog, CloudWatch, etc.).

Two capabilities are provided:

* `MicrometerCapability` — publishes timers, counters and distribution summaries
  to a `MeterRegistry`.
* `MicrometerObservationCapability` — publishes Micrometer `Observation`s to an
  `ObservationRegistry` (which can in turn produce metrics and tracing spans).

Pick one — both record the same underlying call, so registering both would
double-count.

## Usage

### `MicrometerCapability`

```java
GitHub github = Feign.builder()
                     .addCapability(new MicrometerCapability())
                     .target(GitHub.class, "https://api.github.com");
```

By default, metrics are registered with `io.micrometer.core.instrument.Metrics.globalRegistry`.
Pass your own registry and/or common tags if you want explicit control:

```java
MeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);

GitHub github = Feign.builder()
                     .addCapability(new MicrometerCapability(registry, Tags.of("application", "my-app")))
                     .target(GitHub.class, "https://api.github.com");
```

### `MicrometerObservationCapability`

```java
ObservationRegistry observationRegistry = ObservationRegistry.create();

GitHub github = Feign.builder()
                     .addCapability(new MicrometerObservationCapability(observationRegistry))
                     .target(GitHub.class, "https://api.github.com");
```

To customize the tags or name of the observation, implement
`FeignObservationConvention` (or extend `DefaultFeignObservationConvention`) and
pass it to the constructor:

```java
new MicrometerObservationCapability(observationRegistry, new MyFeignObservationConvention());
```

## Metrics published by `MicrometerCapability`

The capability instruments five stages of a Feign call. Each stage emits a
timer; counters and distribution summaries are emitted where noted.

| Name | Type | Description |
| ---- | ---- | ----------- |
| `feign.Feign` | Timer | Total time spent in the Feign method invocation, including encoding, the HTTP call and decoding. Recorded by `MeteredInvocationHandleFactory`. |
| `feign.Feign.exception` | Timer | Same as above, recorded when the invocation throws (any `Throwable`). |
| `feign.Feign.http_error` | Counter | Incremented once per `FeignException` thrown by an invocation. Adds `http_status` and `error_group` tags. |
| `feign.Client` | Timer | Time spent in the underlying HTTP client for a single request. Recorded by `MeteredClient`. |
| `feign.Client.exception` | Timer | Same as above, recorded when the HTTP call throws. |
| `feign.Client.http_response_code` | Counter | Incremented once per HTTP response (including error responses). Adds `http_status`, `status_group`, `http_method` and `uri` tags. |
| `feign.AsyncClient` | Timer | Async-equivalent of `feign.Client`. Recorded by `MeteredAsyncClient`. |
| `feign.AsyncClient.exception` | Timer | Async-equivalent of `feign.Client.exception`. |
| `feign.AsyncClient.http_response_code` | Counter | Async-equivalent of `feign.Client.http_response_code`. |
| `feign.codec.Encoder` | Timer | Time spent encoding the request body. Recorded by `MeteredEncoder`. |
| `feign.codec.Encoder.response_size` | DistributionSummary | Size, in bytes, of the encoded **request** body. The metric name predates the current behavior. |
| `feign.codec.Decoder` | Timer | Time spent decoding the response body. Recorded by `MeteredDecoder`. |
| `feign.codec.Decoder.exception` | Timer | Same as above, recorded when decoding throws. |
| `feign.codec.Decoder.response_size` | DistributionSummary | Size, in bytes, of the response body read by the decoder. |

### Tags

Every metric carries the following tags, populated by `FeignMetricTagResolver`:

| Tag | Source | Example |
| --- | ------ | ------- |
| `client` | Target interface FQN | `com.example.GitHub` |
| `method` | Java method name | `contributors` |
| `host` | Host extracted from the target URL | `api.github.com` |
| `exception_name` | Exception simple name (only present on error) | `FeignException` |
| `root_cause_name` | Root cause exception simple name (only present on error) | `SocketTimeoutException` |

Additional tags are added by specific metrics:

* `feign.Client.http_response_code` / `feign.AsyncClient.http_response_code` —
  adds `http_status` (e.g. `404`), `status_group` (e.g. `4xx`), `http_method`
  (e.g. `GET`) and `uri` (the templated path, e.g. `/repos/{owner}/{repo}/contributors`).
* `feign.Feign.http_error` — adds `http_status` and `error_group` (e.g. `5xx`).
* `feign.codec.Decoder.*` — adds `uri`.

You can attach extra common tags either through `MeterRegistry.config().commonTags(...)`
or via the `MicrometerCapability(MeterRegistry, List<Tag>)` /
`MicrometerCapability(MeterRegistry, Map<String, String>)` constructors.

## Observation published by `MicrometerObservationCapability`

A single observation is produced per HTTP call:

| Name | Contextual name | Description |
| ---- | --------------- | ----------- |
| `http.client.requests` | `HTTP {method}` (e.g. `HTTP GET`) | One observation per HTTP request issued by the Feign client. The observation is stopped after the response is received or an exception is signalled. |

The default convention (`DefaultFeignObservationConvention`) attaches the
following low-cardinality key values:

| Key | Value |
| --- | ----- |
| `http.method` | The HTTP method (`GET`, `POST`, ...). `UNKNOWN` if the request is null. |
| `http.url` | The templated URL of the method. |
| `http.status_code` | The response status code, or `CLIENT_ERROR` if no response was received. |
| `clientName` | The target interface FQN. |

The following key names are also declared on `FeignObservationDocumentation` and
are available for custom conventions to populate: `http.scheme`, `net.peer.host`,
`net.peer.port`.

Whatever observation handlers are registered on the `ObservationRegistry`
(for example `DefaultMeterObservationHandler` for metrics, a tracing handler
for spans) decide what is ultimately emitted.

---

### Overview (`moshi/README.md`)

Moshi Codec
===================

This module adds support for encoding and decoding JSON via the Moshi library.

Add `MoshiCodec` to your `Feign.Builder` like so:

```java
GitHub github = Feign.builder()
                     .codec(new MoshiCodec())
                     .target(GitHub.class, "https://api.github.com");
```

You can also configure the encoder and decoder separately:

```java
GitHub github = Feign.builder()
                     .encoder(new MoshiEncoder())
                     .decoder(new MoshiDecoder())
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`okhttp/README.md`)

OkHttp
===================

This module directs Feign's http requests to [OkHttp](http://square.github.io/okhttp/), which enables SPDY and better network control.

To use OkHttp with Feign, add the OkHttp module to your classpath. Then, configure Feign to use the OkHttpClient:

```java
GitHub github = Feign.builder()
                     .client(new OkHttpClient())
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`reactive/README.md`)

Reactive Streams Wrapper
---

This module wraps Feign's http requests in a [Reactive Streams](https://reactive-streams.org) 
Publisher, enabling the use of Reactive Stream `Publisher` return types.  Supported Reactive Streams implementations are:
 
* [Reactor](https://projectreactor.io/) (`Mono` and `Flux`)
* [ReactiveX (RxJava)](https://reactivex.io) (`Flowable` only)

To use these wrappers, add the `feign-reactive-wrappers` module, and your desired `reactive-streams` 
implementation to your classpath.  Then configure Feign to use the reactive streams wrappers.

```
/* Detailed source-code truncated for AI context efficiency. */
```

Considerations
---

These wrappers are not *reactive all the way down*, given that Feign generated requests are
synchronous.  Requests still block, but execution is controlled by the `Publisher` and their 
related `Scheduler`.  While this may not be ideal in terms of a fully reactive application, providing these
wrappers provide an intermediate upgrade path for Feign.

### Streaming 

Methods that return `java.util.streams` Types are not supported.  Responses are read fully, 
the wrapped in the appropriate reactive wrappers.

### Iterable and Collections responses

Due to the Synchronous nature of Feign requests, methods that return `Iterable` types must specify the collection 
in the `Publisher`.  For `Reactor` types, this limits the use of `Flux` as a response type.

---

### Overview (`ribbon/README.md`)

# Ribbon
This module includes a feign `Target` and `Client` adapter to take advantage of [Ribbon](https://github.com/Netflix/ribbon).

## Conventions
This integration relies on the Feign `Target.url()` being encoded like `https://myAppProd` where `myAppProd` is the ribbon client or loadbalancer name and `myAppProd.ribbon.listOfServers` configuration is set.

### RibbonClient
Adding `RibbonClient` overrides URL resolution of Feign's client, adding smart routing and resiliency capabilities provided by Ribbon.

#### Usage
instead of 
```java
MyService api = Feign.builder().target(MyService.class, "https://myAppProd-1234567890.us-east-1.elb.amazonaws.com");
```
do
```java
MyService api = Feign.builder().client(RibbonClient.create()).target(MyService.class, "https://myAppProd");
```
### LoadBalancingTarget
Using or extending `LoadBalancingTarget` will enable dynamic url discovery via ribbon including incrementing server request counts.

#### Usage
instead of 
```java
MyService api = Feign.builder().target(MyService.class, "https://myAppProd-1234567890.us-east-1.elb.amazonaws.com");
```
do
```java
MyService api = Feign.builder().target(LoadBalancingTarget.create(MyService.class, "https://myAppProd"));
```

---

### Overview (`sax/README.md`)

Sax Decoder
===================

This module adds support for decoding xml via SAX.

Add this to your object graph like so:

```java
api = Feign.builder()
           .decoder(SAXDecoder.builder()
                              .registerContentHandler(UserIdHandler.class)
                              .build())
           .target(Api.class, "https://apihost");
```

---

### Overview (`slf4j/README.md`)

SLF4J
===================

This module allows directing Feign's logging to [SLF4J](http://www.slf4j.org/), allowing you to easily use a logging backend of your choice (Logback, Log4J, etc.)

To use SLF4J with Feign, add both the SLF4J module and an SLF4J binding of your choice to your classpath.  Then, configure Feign to use the Slf4jLogger:

```java
GitHub github = Feign.builder()
                     .logger(new Slf4jLogger())
                     .logLevel(Level.FULL)
                     .target(GitHub.class, "https://api.github.com");
```

---

### Overview (`soap-jakarta/README.md`)

SOAP Codec
===================

This module adds support for encoding and decoding SOAP Body objects via JAXB and SOAPMessage. It also provides SOAPFault decoding capabilities by wrapping them into the original `javax.xml.ws.soap.SOAPFaultException`, so that you'll only need to catch `SOAPFaultException` in order to handle SOAPFault.

Add `SOAPCodec` to your `Feign.Builder` like so:

```java
public interface MyApi {

    @RequestLine("POST /getObject")
    @Headers({
      "SOAPAction: getObject",
      "Content-Type: text/xml"
    })
    MyJaxbObjectResponse getObject(MyJaxbObjectRequest request);

 }

 ...

 JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder()
     .withMarshallerJAXBEncoding("UTF-8")
     .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
     .build();

 api = Feign.builder()
     .codec(new SOAPCodec(jaxbFactory))
     .target(MyApi.class, "http://api");

 ...

 try {
    api.getObject(new MyJaxbObjectRequest());
 } catch (SOAPFaultException faultException) {
    log.info(faultException.getFault().getFaultString());
 }

```

You can also configure the encoder and decoder separately:

```java
api = Feign.builder()
     .encoder(new SOAPEncoder(jaxbFactory))
     .decoder(new SOAPDecoder(jaxbFactory))
     .target(MyApi.class, "http://api");
```

Because a SOAP Fault can be returned as well with a 200 http code than a 4xx or 5xx HTTP error code (depending on the used API), you may also use `SOAPErrorDecoder` in your API configuration, in order to be able to catch `SOAPFaultException` in case of SOAP Fault. Add it, like below:

```java
api = Feign.builder()
     .codec(new SOAPCodec(jaxbFactory))
     .errorDecoder(new SOAPErrorDecoder())
     .target(MyApi.class, "http://api");
```

In certain situations the declarations on the SOAP envelope are not inherited by JAXB when reading the documents.  This is particularly
troublesome when it is not possible to correct the XML at the source.

To account for this situation, use the `useFirstChild` option on the `SOAPDecoder` builder.  This will instruct JAX be to use `SOAPBody#getFirstChild()`
instead of `SOAPBody#extractContentAsDocument()`.  This will allow users to supply a `package-info.java` to manage the element namespaces
explicitly and define what should occur if the namespace declarations are missing.

---

### Overview (`soap/README.md`)

SOAP Codec
===================

This module adds support for encoding and decoding SOAP Body objects via JAXB and SOAPMessage. It also provides SOAPFault decoding capabilities by wrapping them into the original `javax.xml.ws.soap.SOAPFaultException`, so that you'll only need to catch `SOAPFaultException` in order to handle SOAPFault.

Add `SOAPCodec` to your `Feign.Builder` like so:

```java
public interface MyApi {

    @RequestLine("POST /getObject")
    @Headers({
      "SOAPAction: getObject",
      "Content-Type: text/xml"
    })
    MyJaxbObjectResponse getObject(MyJaxbObjectRequest request);

 }

 ...

 JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder()
     .withMarshallerJAXBEncoding("UTF-8")
     .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
     .build();

 api = Feign.builder()
     .codec(new SOAPCodec(jaxbFactory))
     .target(MyApi.class, "http://api");

 ...

 try {
    api.getObject(new MyJaxbObjectRequest());
 } catch (SOAPFaultException faultException) {
    log.info(faultException.getFault().getFaultString());
 }

```

You can also configure the encoder and decoder separately:

```java
api = Feign.builder()
     .encoder(new SOAPEncoder(jaxbFactory))
     .decoder(new SOAPDecoder(jaxbFactory))
     .target(MyApi.class, "http://api");
```

Because a SOAP Fault can be returned as well with a 200 http code than a 4xx or 5xx HTTP error code (depending on the used API), you may also use `SOAPErrorDecoder` in your API configuration, in order to be able to catch `SOAPFaultException` in case of SOAP Fault. Add it, like below:

```java
api = Feign.builder()
     .codec(new SOAPCodec(jaxbFactory))
     .errorDecoder(new SOAPErrorDecoder())
     .target(MyApi.class, "http://api");
```

In certain situations the declarations on the SOAP envelope are not inherited by JAXB when reading the documents.  This is particularly
troublesome when it is not possible to correct the XML at the source.

To account for this situation, use the `useFirstChild` option on the `SOAPDecoder` builder.  This will instruct JAX be to use `SOAPBody#getFirstChild()`
instead of `SOAPBody#extractContentAsDocument()`.  This will allow users to supply a `package-info.java` to manage the element namespaces
explicitly and define what should occur if the namespace declarations are missing.

---

### Overview (`spring/README.md`)

# Feign Spring
This module overrides OpenFeign/feign annotation processing to instead use standard ones supplied by the spring annotations specification.


## Currently Supported Annotation Processing
Feign only supports processing java interfaces (not abstract or concrete classes).

ISE is raised when any annotation's value is empty or null.  Ex. `Path("")` raises an ISE.

Here are a list of behaviors currently supported.
### Type Annotations
#### `@RequestMapping`
Appends the ```value``` to `Target.url()`.  Can have tokens corresponding to `@PathVariable` annotations.
The ```method``` sets the request method.
The ```produces``` adds the first value as the `Accept` header.
The ```consume``` adds the first value as the `Content-Type` header.
### Method Annotations
#### `@RequestMapping`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathVariable` annotations.
The method sets the request method.
#### `@GetMapping`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathVariable` annotations. 
Sets the `GET` request method. 
#### `@PostMapping`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathVariable` annotations.
Sets the `POST` request method. 
#### `@PutMapping`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathVariable` annotations.
Sets the `PUT` request method. 
#### `@DeleteMapping`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathVariable` annotations.
Sets the `DELETE` request method. 
#### `@PatchMapping`
Appends the value to `Target.url()`.  Can have tokens corresponding to `@PathVariable` annotations.
Sets the `PATCH` request method. 
### Parameter Annotations
#### `@PathVariable`
Links the value of the corresponding parameter to a template variable declared in the path.
#### `@RequestParam`
Links the value of the corresponding parameter to a query parameter.  When invoked, null will skip the query param.

---

### Overview (`spring4/README.md`)

# Feign Spring4

The module was moved to [spring](../spring) and renamed to **Feign Spring**.

---

### Overview (`validation-jakarta/README.md`)

Feign Validation (Jakarta)
==========================

Validates the body argument and any `@Valid`-annotated parameters of a Feign-built
interface using Jakarta Bean Validation (`jakarta.validation`) **before** the HTTP
request is dispatched. Constraint violations surface as
`jakarta.validation.ConstraintViolationException`.

Marked `@Experimental` while the underlying `feign.interceptor.MethodInterceptor` API stabilises.

For the legacy `javax.validation` namespace, use the sibling `feign-validation` module.

```xml
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-validation-jakarta</artifactId>
  <version>${feign.version}</version>
</dependency>
```

Usage
-----

```java
Api api = Feign.builder()
    .methodInterceptor(BeanValidationMethodInterceptor.usingDefaultFactory())
    .target(Api.class, "https://example.com");
```

You can pass an explicit `Validator` and validation groups:

```java
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Feign.builder()
    .methodInterceptor(new BeanValidationMethodInterceptor(validator, Create.class))
    .target(Api.class, "https://example.com");
```

---

### Overview (`validation/README.md`)

Feign Validation
================

Validates the body argument and any `@Valid`-annotated parameters of a Feign-built
interface using JSR-303 / Bean Validation 2.0 (`javax.validation`) **before** the HTTP
request is dispatched. Constraint violations surface as
`javax.validation.ConstraintViolationException`.

Marked `@Experimental` while the underlying `feign.interceptor.MethodInterceptor` API stabilises.

For the Jakarta namespace, see the sibling module `feign-validation-jakarta`.

```xml
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-validation</artifactId>
  <version>${feign.version}</version>
</dependency>
```

Usage
-----

```java
Api api = Feign.builder()
    .methodInterceptor(BeanValidationMethodInterceptor.usingDefaultFactory())
    .target(Api.class, "https://example.com");
```

You can pass an explicit `Validator` and validation groups:

```java
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Feign.builder()
    .methodInterceptor(new BeanValidationMethodInterceptor(validator, Create.class))
    .target(Api.class, "https://example.com");
```

What gets validated
-------------------

- The method's body argument (the parameter Feign treats as `bodyIndex`) — always.
- Any other parameter annotated with `@Valid` — useful for headers, query parameters
  carrying complex objects, etc.

Null arguments are skipped. Methods with no parameters pass through.

---

### Overview (`vertx/README.md`)

# Feign Vertx

Implementation of Feign on Vertx. Brings you the best of two worlds together : 
concise syntax of Feign to write client side API on fast, asynchronous and
non-blocking WebClient of Vertx.

## Installation

### With Maven

```xml
<dependencies>
    ...
    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-vertx</artifactId>
    </dependency>
    ...
</dependencies>
```

### With Gradle

```groovy
compile group: 'io.github.openfeign', name: 'feign-vertx'
```

## Compatibility

Feign                  | Vertx
---------------------- | ----------------------
14.x                   | 4.x - 5.x 

## Usage

Write Feign API as usual, but every method of interface must return
`io.vertx.core.Future`.

```java
@Headers({ "Accept: application/json" })
interface IcecreamServiceApi {

  @RequestLine("GET /icecream/flavors")
  Future<Collection<Flavor>> getAvailableFlavors();

  @RequestLine("GET /icecream/mixins")
  Future<Collection<Mixin>> getAvailableMixins();

  @RequestLine("POST /icecream/orders")
  @Headers("Content-Type: application/json")
  Future<Bill> makeOrder(IceCreamOrder order);

  @RequestLine("GET /icecream/orders/{orderId}")
  Future<IceCreamOrder> findOrder(@Param("orderId") int orderId);
  
  @RequestLine("POST /icecream/bills/pay")
  @Headers("Content-Type: application/json")
  Future<Void> payBill(Bill bill);
}
```
Build the client :

```java
WebClient webClient = WebClient.create(vertx);  // create Vert.x WebClient

/* Create instance of your API */
IcecreamServiceApi icecreamApi = VertxFeign
    .builder()
    .webClient(webClient) // provide WebClient instance
    .encoder(new JacksonEncoder())
    .decoder(new JacksonDecoder())
    .target(IcecreamServiceApi.class, "https://www.icecream.com");
    
/* Execute requests asynchronously */
Future<Collection<Flavor>> flavorsFuture = icecreamApi.getAvailableFlavors();
Future<Collection<Mixin>> mixinsFuture = icecreamApi.getAvailableMixins();
```

---

### Overview (`src/docs/README.md`)

Overview map was generated using PlantUML: https://plantuml.com/mindmap-diagram

---

--- METRICS ---
- Files Extracted: 45
- Estimated Token Budget: ~37231 tokens
- Canonical Reference: https://codewiki.google/github.com/OpenFeign/feign
