Skip to content

Providers, Filters & Interceptors

Quarkus 3.9+ Providers, Filters & Interceptors

Section titled “Quarkus 3.9+ Providers, Filters & Interceptors”

The easiest way to understand these concepts is to think about an airport.

  • Resource (@Path) → The airplane that takes passengers to their destination.
  • Request Filter → Security check before boarding.
  • Reader Interceptor → Staff inspecting your luggage before loading it.
  • Resource Method → The flight itself.
  • Writer Interceptor → Staff preparing luggage before unloading.
  • Response Filter → Immigration/customs before you leave the airport.
  • Providers → Airport services that help everything work.

flowchart TD

A[HTTP Request]

A --> B[ContainerRequestFilter]
B --> C[ParamConverterProvider]
C --> D[MessageBodyReader]
D --> E[ReaderInterceptor]
E --> F["@GET / @POST Resource Method"]
F --> G[WriterInterceptor]
G --> H[MessageBodyWriter]
H --> I[ContainerResponseFilter]
I --> J[HTTP Response]

A Provider is any class that extends or customizes the REST framework.

Think of it like installing a plugin in VS Code.

Instead of changing Quarkus itself, you provide additional behavior.

Examples:

ProviderPurpose
ExceptionMapperHandle exceptions
Request FilterInspect requests
Response FilterInspect responses
ReaderInterceptorRead request body
WriterInterceptorWrite response body
MessageBodyReaderJSON → Java
MessageBodyWriterJava → JSON
ParamConverterProviderConvert URL parameters
ContextResolverConfigure Jackson
DynamicFeatureRegister providers conditionally

Most providers are annotated with

@Provider

Runs before the REST endpoint executes.

Think of it as airport security.

Person arrives
Security checks passport
Allowed to board

flowchart LR

A[HTTP Request]
-->B[Request Filter]
-->C[REST Endpoint]

@Provider
public class LoggingFilter
implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext ctx) {
System.out.println(
ctx.getMethod() +
" " +
ctx.getUriInfo().getPath());
}
}

Calling

GET /users

Console

GET users

✅ Authentication

Is user logged in?

✅ Authorization

Can user access this API?

✅ Logging

Who called this API?

✅ Rate limiting

Has this client exceeded requests?

✅ Correlation IDs

Generate request ID

Runs after the endpoint finishes but before the response is sent.


flowchart LR

A[REST Endpoint]
-->B[Response Filter]
-->C[HTTP Response]

@Provider
public class HeaderFilter
implements ContainerResponseFilter {
@Override
public void filter(
ContainerRequestContext req,
ContainerResponseContext res) {
res.getHeaders()
.add("X-App", "Quarkus");
}
}

Client receives

HTTP 200
X-App: Quarkus

✅ Add security headers

X-Frame-Options
Strict-Transport-Security

✅ Add CORS headers

Access-Control-Allow-Origin

✅ Add custom headers

X-Request-ID

✅ Log response status

200
404
500

Runs while reading the request body.

Imagine someone opens every package before it enters a warehouse.


flowchart LR

A[JSON Request]
-->B[ReaderInterceptor]
-->C[Java Object]

@Provider
public class RequestLogger
implements ReaderInterceptor {
@Override
public Object aroundReadFrom(
ReaderInterceptorContext ctx)
throws IOException {
System.out.println("Reading request");
return ctx.proceed();
}
}

✅ Decrypt request body

Encrypted JSON
Decrypt
Java Object

✅ Scan uploads

Virus Scan

✅ Validate payload

Reject invalid data

✅ Log request body


Runs while writing the response body.

Think of it as wrapping a gift before shipping.


flowchart LR

A[Java Object]
-->B[WriterInterceptor]
-->C[JSON Response]

@Provider
public class ResponseLogger
implements WriterInterceptor {
@Override
public void aroundWriteTo(
WriterInterceptorContext ctx)
throws IOException {
System.out.println("Sending response");
ctx.proceed();
}
}

✅ Encrypt response

Java Object
Encrypt
Client

✅ Compress response

gzip

✅ Digital signature

Sign response

✅ Log response body


Converts incoming data into Java objects.

JSON
User class

flowchart LR

A[JSON]
-->B[MessageBodyReader]
-->C[User Object]

Incoming JSON

{
"name":"Ali"
}

Automatically becomes

User user
@POST
public void save(User user) {
}

Normally Jackson handles this automatically.


Almost never.

Create one only when supporting

  • CSV
  • XML
  • Binary
  • Protobuf
  • Custom formats

Opposite of MessageBodyReader.

Converts Java into JSON.


flowchart LR

A[Java Object]
-->B[MessageBodyWriter]
-->C[JSON]

return new User("Ali");

Produces

{
"name":"Ali"
}

Only when returning

  • CSV
  • XML
  • PDF
  • Binary
  • Custom formats

Handles exceptions globally.


flowchart LR

A[Exception]
-->B[ExceptionMapper]
-->C[HTTP Response]

@Provider
public class GlobalExceptionMapper
implements ExceptionMapper<Exception>{
@Override
public Response toResponse(Exception ex){
return Response.status(500)
.entity("Something went wrong")
.build();
}
}

Always.

Instead of

500 Internal Server Error

Return

{
"message":"User not found",
"code":"USR-001"
}

This gives every API a consistent error format.


Converts URL parameters into Java objects.


flowchart LR

A["/users/HIGH"]
-->B[ParamConverter]
-->C[Priority.HIGH]

/priority/HIGH

Automatically becomes

Priority.HIGH

Useful for custom types.

Examples

Money
Currency
UUID
OrderId
CustomerId
Email

Provides configuration to another provider.

Usually used with Jackson.


flowchart LR

A[Jackson]
-->B[ContextResolver]
-->C[Custom ObjectMapper]

@Provider
public class ObjectMapperResolver
implements ContextResolver<ObjectMapper>{
@Override
public ObjectMapper getContext(Class<?> type){
ObjectMapper mapper=new ObjectMapper();
mapper.findAndRegisterModules();
return mapper;
}
}

Customize JSON serialization.

Examples

Pretty JSON
Date formats
Ignore nulls
Custom serializers

Registers filters only where needed.


flowchart TD

A[Incoming Request]

A --> B{Admin API?}

B -->|Yes| C[Register Auth Filter]

B -->|No| D[Skip Filter]

Instead of applying authentication to every endpoint,

Only apply it to

/admin
/private

Not

/health
/login
/public

flowchart LR

A[HTTP Request]

A --> B[Request Filter]

B --> C[Reader Interceptor]

C --> D[REST Method]

D --> E[Writer Interceptor]

E --> F[Response Filter]

F --> G[HTTP Response]
ComponentWorks OnMain PurposeTypical Uses
Request FilterHTTP RequestHeaders & request metadataAuthentication, logging, authorization, rate limiting
Reader InterceptorRequest BodyRequest payloadDecryption, validation, body logging
Resource MethodBusiness LogicProcess the requestCRUD operations, service calls
Writer InterceptorResponse BodyResponse payloadEncryption, compression, signing
Response FilterHTTP ResponseHeaders & metadataCORS, security headers, response logging

flowchart TD

A[Need to customize request/response?]

A --> B{What do you need?}

B -->|Authentication| C[ContainerRequestFilter]

B -->|Logging Headers| C

B -->|Modify Request Body| D[ReaderInterceptor]

B -->|Modify Response Body| E[WriterInterceptor]

B -->|Add Response Headers| F[ContainerResponseFilter]

B -->|Handle Exceptions| G[ExceptionMapper]

B -->|Custom JSON Format| H[ContextResolver]

B -->|Custom Request Format| I[MessageBodyReader]

B -->|Custom Response Format| J[MessageBodyWriter]

B -->|Convert URL Parameters| K[ParamConverterProvider]

B -->|Apply Only to Specific APIs| L[DynamicFeature]
ComponentHow Often?Typical Real-world Scenario
⭐⭐⭐⭐⭐ ExceptionMapperEvery projectConsistent API error responses
⭐⭐⭐⭐⭐ ContainerRequestFilterEvery projectJWT authentication, authorization, request logging
⭐⭐⭐⭐ ContainerResponseFilterVery commonCORS, security headers, request IDs
⭐⭐⭐ ReaderInterceptorSometimesDecrypting or validating request bodies
⭐⭐⭐ WriterInterceptorSometimesEncrypting or compressing responses
⭐⭐ ContextResolverOccasionallyGlobal Jackson configuration
⭐⭐ ParamConverterProviderOccasionallyDomain-specific URL parameter conversion
MessageBodyReaderRareSupporting CSV, XML, Protobuf, or other custom input formats
MessageBodyWriterRareProducing CSV, XML, PDF, or other custom output formats
DynamicFeatureRareConditionally applying filters or interceptors

flowchart TD

A[HTTP Request]

A --> B[ContainerRequestFilter]
B --> C[ParamConverterProvider]
C --> D[MessageBodyReader]
D --> E[ReaderInterceptor]
E --> F[Resource Method]

F --> G[WriterInterceptor]
G --> H[MessageBodyWriter]
H --> I[ContainerResponseFilter]
I --> J[HTTP Response]

F -. Exception .-> K[ExceptionMapper]
K --> I

Don’t try to learn everything at once. A practical progression is:

  1. ContainerRequestFilter – understand authentication, authorization, and request logging.
  2. ContainerResponseFilter – learn how to add headers and handle CORS.
  3. ExceptionMapper – create consistent error responses for your APIs.
  4. ReaderInterceptor and WriterInterceptor – understand request and response body processing.
  5. MessageBodyReader and MessageBodyWriter – learn how Quarkus converts between Java objects and HTTP payloads.
  6. ParamConverterProvider, ContextResolver, and DynamicFeature – advanced customization you’ll encounter less frequently but should recognize when reading production code.

If you’re becoming a professional Quarkus developer, these are the provider-related concepts you’ll encounter most often in enterprise applications.