JAX-RS annotation integration module for Netflix's Feign HTTP client library
npx @tessl/cli install tessl/maven-com-netflix-feign--feign-jaxrs@7.6.0Feign JAX-RS is a module that enables the use of standard JAX-RS annotations (like @GET, @POST, @Path, @PathParam) with Netflix's Feign HTTP client library instead of Feign's native annotations. This allows developers familiar with JAX-RS to leverage Feign's declarative HTTP client capabilities while maintaining consistency with existing JAX-RS-based code.
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-jaxrs</artifactId>
<version>7.6.0</version>
</dependency>Or Gradle:
compile 'com.netflix.feign:feign-jaxrs:7.6.0'import feign.jaxrs.JAXRSModule;
import feign.Contract;import feign.Feign;
import feign.jaxrs.JAXRSModule;
import javax.ws.rs.*;
import java.util.List;
// Define your API interface using JAX-RS annotations
@Path("/repos")
interface GitHubAPI {
@GET
@Path("/{owner}/{repo}/contributors")
List<Contributor> getContributors(@PathParam("owner") String owner,
@PathParam("repo") String repo);
}
// Configure Feign with JAX-RS module
@Module(includes = JAXRSModule.class)
class MyModule {
// Additional configuration
}
// Create client instance
GitHubAPI github = Feign.create(GitHubAPI.class, "https://api.github.com", new MyModule());
// Use the client
List<Contributor> contributors = github.getContributors("netflix", "feign");The feign-jaxrs module consists of two main components:
The module acts as a bridge between JAX-RS annotation semantics and Feign's internal request building mechanism, translating standard JAX-RS annotations into Feign's MethodMetadata format.
The JAXRSModule class provides dependency injection configuration for integrating JAX-RS annotation processing with Feign.
@dagger.Module(library = true, overrides = true)
public final class JAXRSModule {
@Provides
Contract provideContract();
}Module constants:
static final String ACCEPT = "Accept";
static final String CONTENT_TYPE = "Content-Type";The JAXRSContract processes JAX-RS annotations and converts them into Feign request metadata.
public static final class JAXRSContract extends Contract.BaseContract {
@Override
public MethodMetadata parseAndValidatateMetadata(Method method);
@Override
protected void processAnnotationOnMethod(MethodMetadata data,
Annotation methodAnnotation,
Method method);
@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data,
Annotation[] annotations,
int paramIndex);
}Core contract methods:
@Path: Defines the base path for all methods in the interface.
@Path("/api/v1/users")
interface UserAPI {
// All methods inherit /api/v1/users as base path
}HTTP Method Annotations: Define the HTTP method for requests.
@GET // HTTP GET method
@POST // HTTP POST method
@PUT // HTTP PUT method
@DELETE // HTTP DELETE method@Path: Defines method-specific path segments.
@GET
@Path("/profile") // Appends to base path
User getProfile();@Produces: Sets the Accept header for content negotiation.
@GET
@Produces("application/json") // Sets Accept: application/json
User getUser();@Consumes: Sets the Content-Type header for request body.
@POST
@Consumes("application/json") // Sets Content-Type: application/json
void createUser(User user);@PathParam: Links method parameters to path template variables.
@GET
@Path("/users/{userId}")
User getUser(@PathParam("userId") String userId);@QueryParam: Links parameters to HTTP query parameters.
@GET
User searchUsers(@QueryParam("name") String name,
@QueryParam("limit") Integer limit);@HeaderParam: Links parameters to custom HTTP headers.
@GET
User getUser(@HeaderParam("Authorization") String authToken,
@HeaderParam("X-Request-ID") String requestId);@FormParam: Links parameters to form data fields.
@POST
@Consumes("application/x-www-form-urlencoded")
void login(@FormParam("username") String username,
@FormParam("password") String password);The JAX-RS contract throws IllegalStateException in the following cases:
// These will throw IllegalStateException:
@Path("") // Empty path
@PathParam("") String param // Empty path param name
@QueryParam(null) String query // Null query param name
@Produces({}) Response get() // Empty produces array
@GET @POST void invalidMethod() // Multiple HTTP methods// Main module class
@dagger.Module(library = true, overrides = true)
public final class JAXRSModule {
static final String ACCEPT = "Accept";
static final String CONTENT_TYPE = "Content-Type";
@Provides
Contract provideContract();
}
// Contract implementation
public static final class JAXRSContract extends Contract.BaseContract {
@Override
public MethodMetadata parseAndValidatateMetadata(Method method);
@Override
protected void processAnnotationOnMethod(MethodMetadata data,
Annotation methodAnnotation,
Method method);
@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data,
Annotation[] annotations,
int paramIndex);
}import javax.ws.rs.*;
import java.util.List;
@Path("/api/v1")
@Produces("application/json")
public interface UserService {
@GET
@Path("/users")
List<User> getAllUsers(@QueryParam("limit") Integer limit,
@QueryParam("offset") Integer offset);
@GET
@Path("/users/{id}")
User getUserById(@PathParam("id") String userId);
@POST
@Path("/users")
@Consumes("application/json")
User createUser(User user);
@PUT
@Path("/users/{id}")
@Consumes("application/json")
User updateUser(@PathParam("id") String userId, User user);
@DELETE
@Path("/users/{id}")
void deleteUser(@PathParam("id") String userId);
@POST
@Path("/auth/login")
@Consumes("application/x-www-form-urlencoded")
AuthToken login(@FormParam("username") String username,
@FormParam("password") String password);
@GET
@Path("/users/me")
User getCurrentUser(@HeaderParam("Authorization") String bearerToken);
}import dagger.Module;
import dagger.Provides;
import feign.Logger;
import feign.jaxrs.JAXRSModule;
@Module(includes = {JAXRSModule.class, GsonModule.class})
public class ApiModule {
@Provides
Logger.Level provideLogLevel() {
return Logger.Level.BASIC;
}
@Provides
Logger provideLogger() {
return new Logger.ErrorLogger();
}
}import feign.Feign;
public class ApiClient {
private final UserService userService;
public ApiClient(String baseUrl) {
this.userService = Feign.create(UserService.class, baseUrl, new ApiModule());
}
public void demonstrateUsage() {
// Get all users with pagination
List<User> users = userService.getAllUsers(10, 0);
// Get specific user
User user = userService.getUserById("123");
// Create new user
User newUser = new User("john", "john@example.com");
User created = userService.createUser(newUser);
// Update user
created.setEmail("newemail@example.com");
User updated = userService.updateUser(created.getId(), created);
// Authenticate
AuthToken token = userService.login("john", "password123");
// Get current user with auth header
User currentUser = userService.getCurrentUser("Bearer " + token.getAccessToken());
// Delete user
userService.deleteUser(created.getId());
}
}