or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.quarkus/quarkus-resteasy-reactive@3.15.x

docs

examples

advanced-patterns.mdcommon-scenarios.mdedge-cases.md
index.md
tile.json

tessl/maven-io-quarkus--quarkus-resteasy-reactive

tessl install tessl/maven-io-quarkus--quarkus-resteasy-reactive@3.15.0

A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive capabilities in cloud-native environments.

common-scenarios.mddocs/examples/

Common Scenarios

Real-world examples of typical Quarkus REST use cases.

CRUD API

Complete CRUD implementation with validation and error handling:

@Path("/api/products")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ProductResource {

    @Inject
    ProductService productService;

    @GET
    public Uni<List<Product>> list(
        @RestQuery Optional<String> category,
        @RestQuery @DefaultValue("0") int page,
        @RestQuery @DefaultValue("20") int size
    ) {
        return productService.findAll(category, page, size);
    }

    @GET
    @Path("/{id}")
    public Uni<RestResponse<Product>> get(@RestPath Long id) {
        return productService.findById(id)
            .onItem().transform(product -> 
                product != null 
                    ? RestResponse.ok(product)
                    : RestResponse.notFound()
            );
    }

    @POST
    public Uni<RestResponse<Product>> create(@Valid CreateProductRequest request) {
        return productService.create(request)
            .onItem().transform(product -> 
                RestResponse.status(201, product)
            );
    }

    @PUT
    @Path("/{id}")
    public Uni<RestResponse<Product>> update(
        @RestPath Long id,
        @Valid UpdateProductRequest request
    ) {
        return productService.update(id, request)
            .onItem().transform(product ->
                product != null
                    ? RestResponse.ok(product)
                    : RestResponse.notFound()
            );
    }

    @DELETE
    @Path("/{id}")
    public Uni<RestResponse<Void>> delete(@RestPath Long id) {
        return productService.delete(id)
            .onItem().transform(deleted ->
                deleted
                    ? RestResponse.noContent()
                    : RestResponse.notFound()
            );
    }

    @ServerExceptionMapper
    public RestResponse<ErrorResponse> handleValidation(ValidationException ex) {
        return RestResponse.status(400, new ErrorResponse(ex.getMessage()));
    }
}

File Upload and Download

Handle file uploads with validation:

@Path("/api/files")
public class FileResource {

    @Inject
    FileService fileService;

    @POST
    @Path("/upload")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Blocking
    public Response upload(@RestForm FileUpload file) throws IOException {
        // Validate file size
        if (file.size() > 10 * 1024 * 1024) {
            return Response.status(413)
                .entity("File too large (max 10MB)")
                .build();
        }

        // Validate content type
        if (!file.contentType().startsWith("image/")) {
            return Response.status(400)
                .entity("Only image files allowed")
                .build();
        }

        // Save file
        byte[] content = Files.readAllBytes(file.uploadedFile());
        String fileId = fileService.save(file.fileName(), content);

        return Response.status(201)
            .entity(Map.of("fileId", fileId))
            .build();
    }

    @GET
    @Path("/download/{id}")
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    public Response download(@RestPath String id) {
        FileData file = fileService.getFile(id);

        if (file == null) {
            return Response.status(404).build();
        }

        return Response.ok(file.getContent())
            .header("Content-Disposition", 
                "attachment; filename=\"" + file.getName() + "\"")
            .header("Content-Type", file.getContentType())
            .build();
    }
}

Pagination and Filtering

Implement paginated API with filtering:

public class PagedResult<T> {
    public List<T> items;
    public int page;
    public int pageSize;
    public long totalItems;
    public int totalPages;
}

@Path("/api/orders")
public class OrderResource {

    @Inject
    OrderService orderService;

    @GET
    public Uni<PagedResult<Order>> list(
        @RestQuery Optional<String> status,
        @RestQuery Optional<Long> customerId,
        @RestQuery Optional<LocalDate> fromDate,
        @RestQuery Optional<LocalDate> toDate,
        @RestQuery @DefaultValue("0") int page,
        @RestQuery @DefaultValue("20") int size,
        @RestQuery @DefaultValue("createdAt") String sortBy,
        @RestQuery @DefaultValue("desc") String sortOrder
    ) {
        OrderFilter filter = new OrderFilter(
            status, customerId, fromDate, toDate
        );
        
        return orderService.findPaginated(filter, page, size, sortBy, sortOrder);
    }
}

Server-Sent Events

Real-time notifications with SSE:

@Path("/api/notifications")
public class NotificationResource {

    @Inject
    NotificationService notificationService;

    @GET
    @Path("/stream")
    @Produces(MediaType.SERVER_SENT_EVENTS)
    @RestStreamElementType(MediaType.APPLICATION_JSON)
    @Authenticated
    public Multi<Notification> stream(@Context SecurityContext ctx) {
        String userId = ctx.getUserPrincipal().getName();
        return notificationService.streamForUser(userId);
    }

    @POST
    @Path("/send")
    @RolesAllowed("admin")
    public Uni<Response> send(SendNotificationRequest request) {
        return notificationService.sendToUser(
            request.userId, 
            request.message
        ).onItem().transform(sent -> Response.ok().build());
    }
}

REST Client Integration

Call external APIs:

@Path("/api/weather")
public class WeatherResource {

    @Inject
    @RestClient
    WeatherApiClient weatherClient;

    @GET
    @Path("/{city}")
    public Uni<WeatherResponse> getWeather(@RestPath String city) {
        return weatherClient.getWeather(city)
            .onFailure().retry().atMost(3)
            .onFailure().recoverWithItem(
                ex -> new WeatherResponse("unavailable")
            );
    }
}

@RegisterRestClient(configKey = "weather-api")
public interface WeatherApiClient {
    
    @GET
    @Path("/weather")
    @ClientQueryParam(name = "appid", value = "${weather.api.key}")
    Uni<WeatherResponse> getWeather(@QueryParam("q") String city);
}

Caching

Implement HTTP caching:

@Path("/api/articles")
public class ArticleResource {

    @Inject
    ArticleService articleService;

    @GET
    @Path("/{id}")
    @Cache(maxAge = 3600, mustRevalidate = true)
    public Response get(@RestPath Long id, @Context Request request) {
        Article article = articleService.findById(id);

        if (article == null) {
            return Response.status(404).build();
        }

        // ETag support
        EntityTag etag = new EntityTag(article.getVersion());
        Response.ResponseBuilder builder = request.evaluatePreconditions(etag);

        if (builder != null) {
            // Client has current version
            return builder.build();
        }

        return Response.ok(article)
            .tag(etag)
            .lastModified(article.getUpdatedAt())
            .build();
    }
}

Batch Operations

Handle batch requests:

@Path("/api/products")
public class ProductResource {

    @Inject
    ProductService productService;

    @POST
    @Path("/batch")
    public Uni<BatchResult> createBatch(List<CreateProductRequest> requests) {
        List<Uni<Product>> unis = requests.stream()
            .map(req -> productService.create(req)
                .onFailure().recoverWithItem((Product) null))
            .toList();

        return Uni.combine().all().unis(unis)
            .combinedWith(products -> {
                List<Product> created = products.stream()
                    .filter(Objects::nonNull)
                    .toList();
                
                int failed = products.size() - created.size();
                
                return new BatchResult(created, failed);
            });
    }
}

public class BatchResult {
    public List<Product> created;
    public int failed;
    
    public BatchResult(List<Product> created, int failed) {
        this.created = created;
        this.failed = failed;
    }
}

Webhook Handler

Receive and process webhooks:

@Path("/api/webhooks")
public class WebhookResource {

    @Inject
    WebhookService webhookService;

    @POST
    @Path("/github")
    public Uni<Response> handleGitHub(
        @RestHeader("X-GitHub-Event") String event,
        @RestHeader("X-Hub-Signature-256") String signature,
        String payload
    ) {
        // Verify signature
        if (!webhookService.verifySignature(payload, signature)) {
            return Uni.createFrom().item(Response.status(401).build());
        }

        // Process event
        return webhookService.processGitHubEvent(event, payload)
            .onItem().transform(processed -> Response.ok().build())
            .onFailure().recoverWithItem(
                ex -> Response.status(500).build()
            );
    }
}

Rate Limiting

Implement rate limiting with filters:

@ApplicationScoped
public class RateLimitFilter {

    private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();

    @ServerRequestFilter
    public Optional<Response> rateLimit(
        UriInfo uriInfo,
        @Context SecurityContext ctx
    ) {
        String userId = ctx.getUserPrincipal().getName();
        RateLimiter limiter = limiters.computeIfAbsent(
            userId,
            k -> RateLimiter.create(100.0) // 100 requests per second
        );

        if (!limiter.tryAcquire()) {
            return Optional.of(
                Response.status(429)
                    .entity("Rate limit exceeded")
                    .build()
            );
        }

        return Optional.empty();
    }
}

Health Checks

Custom health checks:

@ApplicationScoped
public class ApiHealthCheck implements HealthCheck {

    @Inject
    @RestClient
    ExternalApiClient apiClient;

    @Override
    public HealthCheckResponse call() {
        try {
            apiClient.ping().await().atMost(Duration.ofSeconds(5));
            return HealthCheckResponse.up("external-api");
        } catch (Exception e) {
            return HealthCheckResponse.down("external-api");
        }
    }
}

Next Steps

  • Review Advanced Patterns
  • Check Edge Cases
  • Explore Reference Documentation