Jakarta REST JAX-RS Interview Questions and Answers
Master Jakarta REST and JAX-RS with production-ready interview questions covering REST resources, HTTP methods, path and query parameters, content negotiation, JSON handling, exception mappers, filters, interceptors, validation, async processing, and senior-level API best practices.
Introduction
Jakarta REST provides the standard Jakarta EE API for building RESTful web services.
It was previously known as JAX-RS and is commonly used to expose HTTP APIs using annotations such as:
@Path@GET@POST@PUT@PATCH@DELETE@Produces@Consumes@PathParam@QueryParam
Jakarta REST simplifies HTTP API development by allowing developers to focus on resources, request models, response models, and business workflows instead of manually handling low-level Servlet operations.
A production-ready Jakarta REST API should address:
- Resource modeling
- HTTP semantics
- Validation
- Error handling
- Authentication and authorization
- Content negotiation
- Pagination
- Idempotency
- Observability
- Performance
- API versioning
- Security
This guide covers the 15 most frequently asked Jakarta REST and JAX-RS interview questions with code examples, Mermaid diagrams, production scenarios, common mistakes, and senior-level recommendations.
Q1. What is Jakarta REST?
Answer
Jakarta REST is the Jakarta EE specification for building RESTful HTTP services.
It provides annotation-based APIs for:
- Resource paths
- HTTP method mapping
- Parameter extraction
- Request-body conversion
- Response creation
- Exception mapping
- Filters
- Interceptors
- Content negotiation
- Async processing
- Client APIs
Basic Resource
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/customers")
public class CustomerResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getCustomers() {
return """
[
{
"id": 101,
"name": "Venu"
}
]
""";
}
}
Request Flow
flowchart TD
A[HTTP Client] --> B[Jakarta REST Runtime]
B --> C[Match Resource Path]
C --> D[Match HTTP Method]
D --> E[Convert Request Data]
E --> F[Invoke Resource Method]
F --> G[Convert Result to HTTP Response]
G --> A
Why Interviewers Ask This
Interviewers want to verify that you understand Jakarta REST as a standard specification for REST APIs rather than a standalone application server.
Production Example
A banking platform may expose:
- Customer APIs
- Account APIs
- Payment APIs
- Transaction-history APIs
- Authentication callbacks
Common Mistake
Calling Jakarta REST only a JSON framework.
Senior Follow-up
Jakarta REST defines the API contract, while the runtime implementation provides request dispatching, serialization integration, filtering, and lifecycle behavior.
Q2. What was JAX-RS?
Answer
JAX-RS was the earlier name for the Java API for RESTful Web Services.
The specification is now known as Jakarta REST.
Older code commonly used the javax.ws.rs namespace.
Modern Jakarta EE code uses the jakarta.ws.rs namespace.
Namespace Migration
Older:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
Modern:
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
Evolution
flowchart LR
A[JAX-RS] --> B[Java EE REST Specification]
B --> C[Transferred to Jakarta EE]
C --> D[Jakarta REST]
D --> E[jakarta.ws.rs Namespace]
Migration Considerations
- Update imports
- Update dependencies
- Verify runtime compatibility
- Verify JSON provider compatibility
- Review filters and interceptors
- Test client libraries
- Check third-party extensions
Common Mistake
Assuming only package names change during migration.
Senior Follow-up
A complete migration should validate runtime, dependency, namespace, serialization, and deployment compatibility.
Q3. How do you create a REST resource?
Answer
A REST resource is a CDI-managed or container-managed class annotated with @Path.
Resource Example
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/orders")
@RequestScoped
@Produces(MediaType.APPLICATION_JSON)
public class OrderResource {
private final OrderService orderService;
@Inject
public OrderResource(
OrderService orderService
) {
this.orderService = orderService;
}
@GET
public List<OrderResponse> findOrders() {
return orderService.findOrders();
}
}
Application Configuration
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/api")
public class RestApplication
extends Application {
}
The final URL may be:
/api/orders
Resource Registration Flow
flowchart TD
A[Application Deployment] --> B[Scan @ApplicationPath]
B --> C[Scan @Path Resources]
C --> D[Register Resource Methods]
D --> E[API Ready]
Best Practices
- Keep resource classes thin.
- Delegate business logic to services.
- Return DTOs.
- Validate request models.
- Use correct HTTP status codes.
- Avoid returning entities directly.
Common Mistake
Placing transactions, SQL, and business workflows directly inside the resource class.
Senior Follow-up
The resource layer should translate between HTTP semantics and application use cases.
Q4. What does @Path do?
Answer
@Path defines the URI path for a resource class or resource method.
Class-Level Path
@Path("/customers")
public class CustomerResource {
}
Method-Level Path
@GET
@Path("/{customerId}")
public CustomerResponse findCustomer(
@PathParam("customerId")
Long customerId
) {
return customerService.findCustomer(
customerId
);
}
Final Path
/customers/{customerId}
Nested Path Design
flowchart LR
A[/customers] --> B[Customer Collection]
A --> C[/customers/{id}]
C --> D[Single Customer]
C --> E[/customers/{id}/orders]
E --> F[Customer Orders]
Resource-Oriented Path Examples
Good:
GET /customers/101
GET /customers/101/orders
POST /customers
DELETE /customers/101
Less ideal:
POST /getCustomer
POST /deleteCustomer
POST /createCustomer
Common Mistake
Using verbs in every URL instead of modeling resources.
Senior Follow-up
Paths should model stable business resources, while HTTP methods express the action.
Q5. What are @GET, @POST, @PUT, @PATCH, and @DELETE?
Answer
These annotations map resource methods to HTTP methods.
GET
Retrieves a resource.
@GET
@Path("/{id}")
public CustomerResponse findById(
@PathParam("id")
Long id
) {
return customerService.findById(id);
}
POST
Creates a resource or starts a non-idempotent operation.
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createCustomer(
CreateCustomerRequest request
) {
Long id =
customerService.create(request);
return Response
.status(Response.Status.CREATED)
.entity(
new CreateCustomerResponse(id)
)
.build();
}
PUT
Replaces a complete resource and should normally be idempotent.
@PUT
@Path("/{id}")
public CustomerResponse replaceCustomer(
@PathParam("id") Long id,
ReplaceCustomerRequest request
) {
return customerService.replace(
id,
request
);
}
PATCH
Applies a partial update.
@PATCH
@Path("/{id}")
public CustomerResponse updateCustomer(
@PathParam("id") Long id,
UpdateCustomerRequest request
) {
return customerService.update(
id,
request
);
}
DELETE
Deletes a resource.
@DELETE
@Path("/{id}")
public Response deleteCustomer(
@PathParam("id") Long id
) {
customerService.delete(id);
return Response
.noContent()
.build();
}
HTTP Semantics
flowchart TD
A[HTTP Method] --> B[GET]
A --> C[POST]
A --> D[PUT]
A --> E[PATCH]
A --> F[DELETE]
B --> G[Read]
C --> H[Create or Process]
D --> I[Replace]
E --> J[Partial Update]
F --> K[Delete]
Idempotency
| Method | Usually Idempotent |
|---|---|
| GET | Yes |
| PUT | Yes |
| DELETE | Yes |
| POST | No |
| PATCH | Depends on operation |
Common Mistake
Using POST for every operation.
Senior Follow-up
Correct HTTP semantics improve caching, retry behavior, observability, and client interoperability.
Q6. @PathParam vs @QueryParam?
Answer
@PathParam extracts a value from the URI path.
@QueryParam extracts a value from the query string.
Path Parameter
@GET
@Path("/{customerId}")
public CustomerResponse findCustomer(
@PathParam("customerId")
Long customerId
) {
return customerService.findCustomer(
customerId
);
}
Example:
GET /customers/101
Query Parameter
@GET
public List<CustomerSummary> searchCustomers(
@QueryParam("status")
CustomerStatus status,
@QueryParam("page")
@DefaultValue("0")
int page,
@QueryParam("size")
@DefaultValue("20")
int size
) {
return customerService.search(
status,
page,
size
);
}
Example:
GET /customers?status=ACTIVE&page=0&size=20
Selection Guide
| Path Parameter | Query Parameter |
|---|---|
| Identifies resource | Filters or modifies result |
| Usually required | Often optional |
| Part of resource identity | Part of search or paging |
Example /orders/10 |
Example ?status=OPEN |
Decision Flow
flowchart TD
A[Request Value] --> B{Identifies one resource?}
B -->|Yes| C[Use Path Parameter]
B -->|No| D{Filter sort or pagination?}
D -->|Yes| E[Use Query Parameter]
D -->|No| F[Consider Header or Request Body]
Common Mistake
Using query parameters to identify every resource.
Senior Follow-up
Keep path identity stable and use query parameters for filtering, sorting, pagination, and optional behavior.
Q7. What are @Consumes and @Produces?
Answer
@Consumes defines which request media types a resource accepts.
@Produces defines which response media types it can return.
JSON Resource
@Path("/claims")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class ClaimResource {
}
Multiple Response Types
@GET
@Produces({
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML
})
public CustomerResponse findCustomer() {
return customerService.findCustomer();
}
Content Negotiation
The client may send:
Accept: application/json
The server selects a compatible representation.
Content Flow
sequenceDiagram
participant Client
participant Runtime
participant Resource
participant Provider
Client->>Runtime: POST with Content-Type application/json
Runtime->>Provider: Deserialize JSON
Provider-->>Runtime: Request DTO
Runtime->>Resource: Invoke method
Resource-->>Runtime: Response DTO
Runtime->>Provider: Serialize response
Provider-->>Client: application/json
Common Media Types
application/jsonapplication/xmltext/plaintext/csvapplication/octet-streammultipart/form-data
Common Mistake
Returning JSON without setting the correct media type.
Senior Follow-up
Content negotiation should remain explicit, predictable, and documented through OpenAPI.
Q8. How is JSON serialization handled?
Answer
Jakarta REST integrates with message body readers and writers.
For JSON, common standards include:
- Jakarta JSON Binding
- Jakarta JSON Processing
A runtime may also integrate other JSON providers.
DTO Example
public record CustomerResponse(
Long id,
String name,
String email
) {
}
Resource
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public CustomerResponse findCustomer(
@PathParam("id")
Long id
) {
return customerService.findCustomer(id);
}
The provider serializes the DTO into JSON.
Serialization Flow
flowchart TD
A[Resource Returns DTO] --> B[MessageBodyWriter]
B --> C[JSON Provider]
C --> D[JSON Response Body]
D --> E[HTTP Client]
Deserialization
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createCustomer(
CreateCustomerRequest request
) {
// request created from JSON
}
Risks
- Returning entities can trigger lazy loading.
- Bidirectional relationships can recurse.
- Sensitive fields may be exposed.
- Unknown properties may cause failures.
- Date formats may be inconsistent.
- Large payloads can consume memory.
Common Mistake
Returning JPA entities directly and relying on the JSON serializer to determine the API contract.
Senior Follow-up
Use dedicated DTOs and explicit serialization rules to keep persistence models separate from external contracts.
Q9. What is the Response class?
Answer
jakarta.ws.rs.core.Response provides full control over an HTTP response.
It can define:
- Status
- Headers
- Entity body
- Cookies
- Location
- Cache metadata
- Content type
Created Response
URI location =
uriInfo
.getAbsolutePathBuilder()
.path(
customerId.toString()
)
.build();
return Response
.created(location)
.entity(
new CreateCustomerResponse(
customerId
)
)
.build();
No Content
return Response
.noContent()
.build();
Not Found
return Response
.status(Response.Status.NOT_FOUND)
.entity(
new ErrorResponse(
"CUSTOMER_NOT_FOUND",
"Customer does not exist"
)
)
.build();
Response Construction
flowchart TD
A[Resource Method] --> B[Choose Status]
B --> C[Add Headers]
C --> D[Add Entity]
D --> E[Build Response]
E --> F[Jakarta REST Runtime]
F --> G[HTTP Response]
Common Status Codes
| Status | Meaning |
|---|---|
| 200 | Successful read |
| 201 | Resource created |
| 202 | Accepted for processing |
| 204 | Successful with no body |
| 400 | Invalid request |
| 401 | Authentication required |
| 403 | Authenticated but forbidden |
| 404 | Resource not found |
| 409 | Conflict |
| 422 | Semantically invalid input |
| 500 | Unexpected server error |
Common Mistake
Returning HTTP 200 for every outcome.
Senior Follow-up
Status codes are part of the API contract and should describe transport-level outcome accurately.
Q10. How do exception mappers work?
Answer
Exception mappers convert Java exceptions into consistent HTTP responses.
They implement:
ExceptionMapper<T>
Custom Exception
public class CustomerNotFoundException
extends RuntimeException {
public CustomerNotFoundException(
Long customerId
) {
super(
"Customer not found: "
+ customerId
);
}
}
Exception Mapper
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
@Provider
public class CustomerNotFoundMapper
implements ExceptionMapper<
CustomerNotFoundException
> {
@Override
public Response toResponse(
CustomerNotFoundException exception
) {
ErrorResponse error =
new ErrorResponse(
"CUSTOMER_NOT_FOUND",
exception.getMessage()
);
return Response
.status(
Response.Status.NOT_FOUND
)
.entity(error)
.build();
}
}
Exception Flow
sequenceDiagram
participant Client
participant Resource
participant Service
participant Mapper
Client->>Resource: GET /customers/999
Resource->>Service: find customer
Service-->>Resource: Throw CustomerNotFoundException
Resource->>Mapper: Resolve exception mapper
Mapper-->>Client: 404 structured error
Global Fallback Mapper
@Provider
public class UnexpectedExceptionMapper
implements ExceptionMapper<Throwable> {
@Override
public Response toResponse(
Throwable exception
) {
String errorId =
UUID.randomUUID()
.toString();
return Response
.serverError()
.entity(
new ErrorResponse(
"INTERNAL_ERROR",
"Unexpected error",
errorId
)
)
.build();
}
}
Common Mistake
Returning raw exception messages and stack traces to clients.
Senior Follow-up
Log internal details with a correlation ID, but return safe, stable, client-oriented error responses.
Q11. What are Jakarta REST filters?
Answer
Filters intercept request or response processing.
Types include:
ContainerRequestFilterContainerResponseFilter- Client request filters
- Client response filters
Request Filter
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter
implements ContainerRequestFilter {
@Override
public void filter(
ContainerRequestContext context
) {
String authorization =
context.getHeaderString(
"Authorization"
);
if (
authorization == null
|| authorization.isBlank()
) {
context.abortWith(
Response
.status(
Response.Status.UNAUTHORIZED
)
.entity(
new ErrorResponse(
"UNAUTHORIZED",
"Authentication required"
)
)
.build()
);
}
}
}
Response Filter
@Provider
public class SecurityHeaderFilter
implements ContainerResponseFilter {
@Override
public void filter(
ContainerRequestContext request,
ContainerResponseContext response
) {
response
.getHeaders()
.putSingle(
"X-Content-Type-Options",
"nosniff"
);
}
}
Filter Chain
sequenceDiagram
participant Client
participant RequestFilter
participant Resource
participant ResponseFilter
Client->>RequestFilter: HTTP request
RequestFilter->>Resource: Continue request
Resource-->>ResponseFilter: Resource response
ResponseFilter-->>Client: Final response
Common Uses
- Authentication
- Authorization
- Correlation IDs
- Logging
- CORS
- Metrics
- Security headers
- Tenant resolution
- Request normalization
Common Mistake
Putting business authorization rules entirely inside generic request filters.
Senior Follow-up
Use filters for protocol-level concerns and domain services for business-specific authorization.
Q12. What are reader and writer interceptors?
Answer
Reader and writer interceptors wrap request-body deserialization and response-body serialization.
Interfaces:
ReaderInterceptorWriterInterceptor
Writer Interceptor Example
@Provider
public class ResponseTimingInterceptor
implements WriterInterceptor {
@Override
public void aroundWriteTo(
WriterInterceptorContext context
) throws IOException,
WebApplicationException {
long start =
System.nanoTime();
try {
context.proceed();
} finally {
long duration =
System.nanoTime()
- start;
System.out.println(
"Serialization duration: "
+ duration
);
}
}
}
Processing Pipeline
flowchart TD
A[HTTP Request Body] --> B[Reader Interceptor]
B --> C[MessageBodyReader]
C --> D[Resource Method]
D --> E[Writer Interceptor]
E --> F[MessageBodyWriter]
F --> G[HTTP Response Body]
Filter vs Interceptor
| Filter | Reader/Writer Interceptor |
|---|---|
| Works around request/response flow | Works around entity-body conversion |
| Can inspect headers and URI | Focuses on body read/write |
| Can abort request | Can transform serialization pipeline |
| Authentication and CORS | Compression, signing, body tracing |
Common Mistake
Using an interceptor for logic unrelated to body processing.
Senior Follow-up
Filters handle HTTP request-response concerns; interceptors handle message entity serialization and deserialization.
Q13. How is Bean Validation integrated with Jakarta REST?
Answer
Jakarta REST integrates with Jakarta Bean Validation.
Constraints can be applied to:
- Request DTO fields
- Method parameters
- Path parameters
- Query parameters
- Return values
Request DTO
public record CreateCustomerRequest(
@NotBlank
@Size(max = 100)
String name,
@NotBlank
@Email
String email
) {
}
Resource
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createCustomer(
@Valid
CreateCustomerRequest request
) {
Long customerId =
customerService.create(request);
return Response
.status(Response.Status.CREATED)
.entity(
new CreateCustomerResponse(
customerId
)
)
.build();
}
Parameter Validation
@GET
public List<CustomerSummary> search(
@QueryParam("page")
@Min(0)
int page,
@QueryParam("size")
@Min(1)
@Max(100)
int size
) {
return customerService.search(
page,
size
);
}
Validation Flow
sequenceDiagram
participant Client
participant Runtime
participant Validator
participant Resource
Client->>Runtime: HTTP request
Runtime->>Validator: Validate request data
alt Valid
Validator->>Resource: Invoke resource method
Resource-->>Client: Success response
else Invalid
Validator-->>Client: Constraint violation response
end
Best Practices
- Validate structure at the API boundary.
- Validate business rules in the service layer.
- Return field-level errors.
- Keep messages stable and client-friendly.
- Avoid exposing internal validator class names.
Common Mistake
Using Bean Validation as a replacement for business-rule validation.
Senior Follow-up
Bean Validation handles declarative input constraints; business invariants require domain or service validation.
Q14. How do asynchronous endpoints work?
Answer
Jakarta REST supports asynchronous response processing.
A resource method can suspend the response and complete it later.
Async Example
import jakarta.ws.rs.container.AsyncResponse;
import jakarta.ws.rs.container.Suspended;
@GET
@Path("/reports/{id}")
public void generateReport(
@PathParam("id")
Long reportId,
@Suspended
AsyncResponse asyncResponse
) {
reportService
.generateAsync(reportId)
.whenComplete(
(report, error) -> {
if (error != null) {
asyncResponse.resume(
error
);
return;
}
asyncResponse.resume(
report
);
}
);
}
Timeout
asyncResponse.setTimeout(
30,
TimeUnit.SECONDS
);
Async Lifecycle
sequenceDiagram
participant Client
participant RequestThread
participant AsyncTask
participant Database
Client->>RequestThread: GET report
RequestThread->>AsyncTask: Suspend response
RequestThread-->>RequestThread: Release request thread
AsyncTask->>Database: Generate report
Database-->>AsyncTask: Result
AsyncTask-->>Client: Resume HTTP response
Important Considerations
- Configure timeouts.
- Handle errors.
- Avoid losing security context.
- Avoid using request-scoped objects after request context ends.
- Ensure thread execution is container-managed.
- Add cancellation where possible.
- Do not assume async means non-blocking.
Common Mistake
Starting unmanaged threads manually.
Senior Follow-up
Use managed executors and understand context propagation, timeout, cancellation, and back-pressure.
Q15. What are senior-level Jakarta REST best practices?
Answer
Senior developers should design Jakarta REST APIs around clear resource models, stable contracts, security, and operational predictability.
Best Practices
- Use resource-oriented paths.
- Use correct HTTP methods.
- Preserve idempotency semantics.
- Return accurate status codes.
- Use request and response DTOs.
- Validate input at the boundary.
- Keep resource classes thin.
- Use service-layer transactions.
- Use exception mappers for consistent errors.
- Apply filters for protocol-level concerns.
- Protect sensitive data.
- Add pagination and result limits.
- Use deterministic sorting.
- Support correlation IDs.
- Document APIs with OpenAPI.
- Version APIs deliberately.
- Use idempotency keys for retryable POST operations.
- Avoid returning entities.
- Apply authentication and authorization consistently.
- Monitor latency, error rates, and payload size.
- Load test large responses and file operations.
API Design Flow
flowchart TD
A[Business Capability] --> B[Identify Resource]
B --> C[Choose HTTP Method]
C --> D[Define Request DTO]
D --> E[Define Response DTO]
E --> F[Define Validation]
F --> G[Define Error Contract]
G --> H[Apply Security]
H --> I[Document API]
I --> J[Load Test and Monitor]
Senior Follow-up
A production API is more than a resource method. It includes semantics, security, validation, observability, compatibility, and failure behavior.
Jakarta REST Architecture
flowchart TD
A[API Client] --> B[HTTP Connector]
B --> C[Request Filters]
C --> D[Jakarta REST Runtime]
D --> E[Resource Method]
E --> F[Application Service]
F --> G[Repository]
G --> H[Database]
E --> I[Response DTO]
I --> J[Writer Interceptor]
J --> K[MessageBodyWriter]
K --> L[Response Filters]
L --> A
REST Request Lifecycle
sequenceDiagram
participant Client
participant Filter
participant Runtime
participant Validator
participant Resource
participant Service
participant Mapper
Client->>Filter: HTTP request
Filter->>Runtime: Continue
Runtime->>Validator: Validate parameters and DTO
Validator->>Resource: Invoke method
Resource->>Service: Execute use case
alt Success
Service-->>Resource: Result
Resource-->>Runtime: Response DTO
Runtime-->>Client: HTTP success
else Exception
Service-->>Mapper: Throw exception
Mapper-->>Client: Structured error
end
Resource Design Example
@Path("/orders")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@RequestScoped
public class OrderResource {
private final OrderService orderService;
@Context
private UriInfo uriInfo;
@Inject
public OrderResource(
OrderService orderService
) {
this.orderService = orderService;
}
@GET
public List<OrderSummary> searchOrders(
@QueryParam("status")
OrderStatus status,
@QueryParam("page")
@DefaultValue("0")
@Min(0)
int page,
@QueryParam("size")
@DefaultValue("20")
@Min(1)
@Max(100)
int size
) {
return orderService.search(
status,
page,
size
);
}
@GET
@Path("/{orderId}")
public OrderResponse findOrder(
@PathParam("orderId")
Long orderId
) {
return orderService.findOrder(
orderId
);
}
@POST
public Response createOrder(
@Valid
CreateOrderRequest request
) {
Long orderId =
orderService.createOrder(
request
);
URI location =
uriInfo
.getAbsolutePathBuilder()
.path(
orderId.toString()
)
.build();
return Response
.created(location)
.entity(
new CreateOrderResponse(
orderId
)
)
.build();
}
@DELETE
@Path("/{orderId}")
public Response deleteOrder(
@PathParam("orderId")
Long orderId
) {
orderService.deleteOrder(
orderId
);
return Response
.noContent()
.build();
}
}
HTTP Method Decision Matrix
| Requirement | Method |
|---|---|
| Retrieve resource | GET |
| Create resource | POST |
| Replace complete resource | PUT |
| Partially update resource | PATCH |
| Delete resource | DELETE |
| Check metadata | HEAD |
| Discover supported methods | OPTIONS |
Status Code Decision Matrix
| Scenario | Status |
|---|---|
| Successful read | 200 |
| Resource created | 201 |
| Accepted for async work | 202 |
| Successful delete | 204 |
| Invalid syntax or parameters | 400 |
| Missing authentication | 401 |
| Insufficient permission | 403 |
| Missing resource | 404 |
| Duplicate or stale update | 409 |
| Unsupported media type | 415 |
| Validation failure | 400 or 422 |
| Rate limit exceeded | 429 |
| Unexpected failure | 500 |
| Temporary downstream failure | 502 or 503 |
Error Response Model
public record ErrorResponse(
String code,
String message,
String correlationId,
List<FieldError> fieldErrors
) {
}
public record FieldError(
String field,
String message
) {
}
Example JSON
{
"code": "VALIDATION_FAILED",
"message": "Request validation failed",
"correlationId": "d984f991-cc32-4dd6",
"fieldErrors": [
{
"field": "email",
"message": "must be a valid email"
}
]
}
Pagination Response
public record PageResponse<T>(
List<T> content,
int page,
int size,
long totalElements,
int totalPages
) {
}
Pagination Flow
flowchart TD
A[GET /orders?page=2&size=20] --> B[Validate Page Size]
B --> C[Execute Paged Query]
C --> D[Execute Count Query]
D --> E[Build Page Response]
E --> F[Return Metadata and Content]
Best Practices
- Set maximum page size.
- Use stable ordering.
- Consider keyset pagination for large data.
- Avoid returning unbounded collections.
- Document default page behavior.
Header Injection
@GET
public Response findOrders(
@HeaderParam("X-Correlation-ID")
String correlationId
) {
return Response.ok().build();
}
Common Header Uses
- Correlation ID
- Authorization
- Idempotency key
- Locale
- Tenant ID
- Conditional request headers
- API version headers
Cookie Parameter
@GET
public Response getPreferences(
@CookieParam("language")
Cookie languageCookie
) {
return Response.ok().build();
}
Avoid using cookies for sensitive application data unless securely designed.
Form Parameters
@POST
@Consumes(
MediaType.APPLICATION_FORM_URLENCODED
)
public Response login(
@FormParam("username")
String username,
@FormParam("password")
String password
) {
return Response.ok().build();
}
For modern APIs, JSON request DTOs are usually easier to version and validate.
Matrix Parameters
@GET
@Path("/reports")
public Response getReport(
@MatrixParam("format")
String format
) {
return Response.ok().build();
}
Example:
/reports;format=summary
Matrix parameters are less commonly used than query parameters.
Subresource Locator
@Path("/customers")
public class CustomerResource {
@Path("/{customerId}/orders")
public CustomerOrderResource
customerOrders(
@PathParam("customerId")
Long customerId
) {
return new CustomerOrderResource(
customerId
);
}
}
Subresource Flow
flowchart LR
A[/customers/101/orders] --> B[CustomerResource]
B --> C[Subresource Locator]
C --> D[CustomerOrderResource]
Use subresources when they improve resource organization without creating excessive complexity.
Name Binding
Name binding applies a filter or interceptor only to selected resources.
Binding Annotation
@NameBinding
@Retention(RUNTIME)
@Target({
TYPE,
METHOD
})
public @interface Audited {
}
Filter
@Provider
@Audited
public class AuditFilter
implements ContainerRequestFilter {
@Override
public void filter(
ContainerRequestContext context
) {
// Audit selected endpoint
}
}
Resource
@POST
@Audited
public Response createPayment() {
return Response.ok().build();
}
Conditional Requests with ETag
EntityTag entityTag =
new EntityTag(
customer.getVersion()
.toString()
);
Response.ResponseBuilder builder =
request.evaluatePreconditions(
entityTag
);
if (builder != null) {
return builder.build();
}
return Response
.ok(customerResponse)
.tag(entityTag)
.build();
Conditional Request Flow
sequenceDiagram
participant Client
participant API
Client->>API: GET with If-None-Match
API->>API: Compare ETag
alt Unchanged
API-->>Client: 304 Not Modified
else Changed
API-->>Client: 200 with new ETag
end
ETags can improve caching and support optimistic concurrency.
Idempotency Key Pattern
For retryable POST operations such as payments:
Idempotency-Key: 8df8b968-4512
Flow
flowchart TD
A[POST Payment] --> B[Read Idempotency Key]
B --> C{Key Seen Before?}
C -->|Yes| D[Return Previous Result]
C -->|No| E[Process Payment]
E --> F[Store Result by Key]
F --> G[Return Response]
This prevents duplicate processing after client retries.
Async Job Pattern
For long-running tasks:
POST /reports
Response:
202 Accepted
Location: /reports/jobs/abc123
Client polls:
GET /reports/jobs/abc123
Flow
sequenceDiagram
participant Client
participant API
participant Worker
Client->>API: POST report request
API->>Worker: Submit job
API-->>Client: 202 with job location
Client->>API: GET job status
API-->>Client: RUNNING
Worker->>API: Mark completed
Client->>API: GET job status
API-->>Client: COMPLETED with result link
This is often safer than keeping one HTTP connection open for very long work.
File Upload Resource
@POST
@Path("/documents")
@Consumes(
MediaType.MULTIPART_FORM_DATA
)
public Response uploadDocument() {
return Response
.status(Response.Status.CREATED)
.build();
}
Best Practices
- Limit request size.
- Validate file type.
- Scan untrusted files.
- Generate safe file names.
- Stream to storage.
- Apply authorization.
- Avoid loading large files fully into memory.
- Record audit metadata.
File Download Resource
@GET
@Path("/documents/{id}")
@Produces(
MediaType.APPLICATION_OCTET_STREAM
)
public Response download(
@PathParam("id")
Long documentId
) {
StreamingOutput stream =
output -> documentService
.writeTo(
documentId,
output
);
return Response
.ok(stream)
.header(
"Content-Disposition",
"attachment; filename=\"document.pdf\""
)
.build();
}
Security Annotations
@GET
@Path("/{id}")
@RolesAllowed({
"ADMIN",
"CUSTOMER_SUPPORT"
})
public CustomerResponse findCustomer(
@PathParam("id")
Long id
) {
return customerService.findCustomer(id);
}
Use both:
- Coarse-grained role checks
- Fine-grained business authorization
Example:
A customer may have USER role but should access only their own account.
CORS Handling
A response filter may configure CORS headers.
response
.getHeaders()
.putSingle(
"Access-Control-Allow-Origin",
allowedOrigin
);
Important
Do not blindly use:
Access-Control-Allow-Origin: *
for credentialed or sensitive APIs.
API Versioning Strategies
| Strategy | Example |
|---|---|
| URL version | /api/v1/customers |
| Header version | Accept-Version: 1 |
| Media type version | application/vnd.company.v1+json |
| Backward-compatible evolution | Add optional fields |
Versioning Flow
flowchart TD
A[API Change] --> B{Backward Compatible?}
B -->|Yes| C[Evolve Existing Version]
B -->|No| D[Create New Version]
D --> E[Migration and Deprecation Plan]
Common Jakarta REST Mistakes
- Returning entities directly.
- Using POST for every operation.
- Returning 200 for errors.
- Ignoring validation.
- Exposing stack traces.
- Hardcoding JSON strings.
- Returning unbounded lists.
- Mixing persistence and HTTP logic.
- Using filters for business workflows.
- Ignoring idempotency.
- Failing to set media types.
- Not validating path and query parameters.
- Ignoring API versioning.
- Logging sensitive request bodies.
- Using async without timeout handling.
- Creating unmanaged threads.
- Returning inconsistent error formats.
- Treating authentication as authorization.
- Ignoring payload size and latency.
- Failing to monitor downstream failures.
Senior Jakarta REST Checklist
Resource-Oriented Path
│
▼
Correct HTTP Method
│
▼
Validated Request DTO
│
▼
Thin Resource Method
│
▼
Transactional Service
│
▼
Stable Response DTO
│
▼
Accurate Status Code
│
▼
Consistent Error Contract
│
▼
Security and Observability
Interview Quick Revision
| Concept | Purpose |
|---|---|
| Jakarta REST | Standard REST API specification |
| JAX-RS | Previous name |
@Path |
URI mapping |
@GET |
Retrieve data |
@POST |
Create or process |
@PUT |
Replace resource |
@PATCH |
Partial update |
@DELETE |
Delete resource |
@PathParam |
Path value |
@QueryParam |
Query-string value |
@Consumes |
Accepted request type |
@Produces |
Response type |
Response |
Full HTTP response control |
| Exception Mapper | Exception-to-response conversion |
| Filter | Request-response interception |
Key Takeaways
- Jakarta REST is the Jakarta EE standard for building annotation-driven RESTful HTTP services.
- JAX-RS is the former name of Jakarta REST.
@Pathdefines resource URIs, while HTTP method annotations define operations.- Resource paths should model nouns and business resources rather than action-oriented verbs.
@PathParamidentifies resources, while@QueryParamhandles filtering, sorting, pagination, and optional behavior.@Consumesand@Producesdefine supported request and response media types.- JSON conversion is handled by message body readers and writers.
- API DTOs should remain separate from JPA entities.
Responseprovides control over status codes, headers, cookies, locations, and response bodies.- Exception mappers create consistent, safe HTTP error contracts.
- Request and response filters are appropriate for protocol-level concerns such as authentication, CORS, logging, and correlation IDs.
- Reader and writer interceptors wrap request-body and response-body conversion.
- Jakarta Bean Validation integrates with resource parameters and request DTOs.
- Async endpoints require timeout, error, cancellation, context, and thread-management planning.
- Long-running operations often benefit from a 202 Accepted job model rather than one long HTTP request.
- Pagination, maximum result sizes, and stable sorting are essential for collection endpoints.
- Correct HTTP semantics improve retries, caching, client behavior, and observability.
- Idempotency keys are important for retryable non-idempotent operations such as payments.
- Senior developers design Jakarta REST APIs as stable, secure, observable, versioned, and production-ready contracts.