Agent skill
quarkus
Quarkus framework guardrails, patterns, and best practices for AI-assisted development. Use when working with Quarkus projects, or when the user mentions Quarkus. Provides CDI, RESTEasy, Panache, GraalVM native, and reactive guidelines.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/quarkus-ar4mirez-samuel
Metadata
Additional technical details for this skill
- author
- samuel
- version
- 1.0
- category
- framework
- language
- java
- extensions
- .java
SKILL.md
Quarkus Guide
Applies to: Quarkus 3.x, Java 17+, Cloud-Native Microservices, GraalVM Native, Kubernetes
Core Principles
- Kubernetes-Native: Designed for containers, fast startup, low memory footprint
- Developer Joy: Live reload via Dev Services, Dev UI, continuous testing
- Unified Reactive/Imperative: Choose per-endpoint, both coexist in one app
- Build-Time Optimization: Extensions process metadata at build time, not runtime
- GraalVM First-Class: Native compilation with millisecond startup and minimal RSS
- Standards-Based: MicroProfile, Jakarta EE, Vert.x, SmallRye under the hood
Guardrails
Project & Dependencies
- Use Quarkus BOM for version management (never pin individual Quarkus artifact versions)
- Add extensions via
./mvnw quarkus:add-extensionrather than editing pom.xml manually - Run
./mvnw quarkus:devfor live reload; never restart manually during development - Use Dev Services (auto-provisioned containers) for databases, Kafka, Redis in dev/test
- Keep
application.propertiesprofile-aware:%dev.,%test.,%prod.prefixes
CDI (Contexts and Dependency Injection)
- Use
@ApplicationScopedfor stateless services (one instance per app) - Use
@RequestScopedonly when you need per-request state - Prefer constructor injection or
@Injectfields (constructor preferred for testability) - Avoid
@Dependentscope unless you need a new instance per injection point - Use
@Startupfor beans that must initialize eagerly at boot - Register beans for native with
@RegisterForReflectiononly when reflection is required - Produce CDI beans via
@Producesmethods for third-party objects
RESTEasy Reactive Resources
- Use RESTEasy Reactive (
quarkus-resteasy-reactive), not classic RESTEasy - Return
Uni<T>orMulti<T>for non-blocking I/O; plain types for blocking is acceptable - Keep resource classes thin: validate input, delegate to service, map response
- Use
@Validon request parameters for Bean Validation - Annotate with
@Produces(APPLICATION_JSON)and@Consumes(APPLICATION_JSON) - Use
@Pathversioning:/api/v1/resource - Add OpenAPI annotations (
@Operation,@APIResponse) on every endpoint - Secure endpoints with
@RolesAllowed,@Authenticated, or@PermitAll
Panache ORM
- Active Record (
extends PanacheEntity): good for simple CRUD entities - Repository (
implements PanacheRepository<T>): good for complex queries, better testability - Use
Uni<T>return types withhibernate-reactive-panachefor non-blocking DB access - Add custom finder methods on entity or repository (e.g.,
findByEmail) - Use
@WithTransactionon service methods that write, not on resources - Set
quarkus.hibernate-orm.database.generation=validateand manage schema via Flyway - Always provide
@PrePersist/@PreUpdatefor audit timestamps
Configuration
- Use
application.propertieswith profile prefixes:%dev.,%test.,%prod. - Inject config values with
@ConfigProperty(name = "key", defaultValue = "fallback") - Group related config with
@ConfigMappinginterfaces - Never hardcode secrets; use
${ENV_VAR:default}placeholder syntax - Enable Dev Services for local containers:
quarkus.devservices.enabled=true
Error Handling
- Create domain exceptions:
ResourceNotFoundException,DuplicateResourceException - Register JAX-RS
@Providerclasses implementingExceptionMapper<T> - Return structured error bodies:
{ title, status, detail, timestamp } - Map
ConstraintViolationExceptionto 400 with per-field error details - Log at WARN for client errors, ERROR for unexpected failures
Native Build Compatibility
- Avoid runtime reflection (use
@RegisterForReflectionwhen unavoidable) - Avoid dynamic proxies and runtime bytecode generation
- Test native build regularly:
./mvnw verify -Pnative - Use
@BuildStepextensions for build-time processing - Prefer compile-time DI and annotation processors (MapStruct, Panache)
- Use
quarkus.native.container-build=trueto build without local GraalVM
Project Structure
myproject/
├── src/main/java/com/example/
│ ├── resource/ # JAX-RS endpoints (thin controllers)
│ │ ├── UserResource.java
│ │ └── AuthResource.java
│ ├── service/ # Business logic
│ │ └── UserService.java
│ ├── repository/ # Panache repositories (optional if using active record)
│ │ └── UserRepository.java
│ ├── entity/ # JPA entities with Panache
│ │ └── User.java
│ ├── dto/ # Request/response records
│ │ ├── UserRequest.java
│ │ └── UserResponse.java
│ ├── mapper/ # MapStruct mappers (compile-time, CDI-scoped)
│ │ └── UserMapper.java
│ ├── exception/ # Domain exceptions + ExceptionMappers
│ │ ├── ResourceNotFoundException.java
│ │ ├── DuplicateResourceException.java
│ │ └── ExceptionMappers.java
│ └── health/ # MicroProfile Health checks
│ └── DatabaseHealthCheck.java
├── src/main/resources/
│ ├── application.properties # Config with profile prefixes
│ └── db/migration/ # Flyway SQL migrations
│ └── V1__create_users.sql
├── src/main/docker/
│ ├── Dockerfile.jvm
│ └── Dockerfile.native
├── src/test/java/com/example/
│ ├── resource/ # @QuarkusTest integration tests
│ │ └── UserResourceTest.java
│ └── service/ # Unit tests with @InjectMock
│ └── UserServiceTest.java
└── pom.xml
resource/-- Thin REST endpoints; input validation and response mapping onlyservice/-- All business logic; transactional boundaries live hererepository/-- Data access (skip if active record pattern suffices)entity/-- JPA entities; keep domain logic minimal, push to servicedto/-- Java records for request/response; use Bean Validation annotationsmapper/-- MapStruct interfaces withcomponentModel = "cdi"exception/-- Domain exceptions and JAX-RS ExceptionMapper providershealth/-- MicroProfile HealthCheck implementations
CDI Pattern
@ApplicationScoped
public class UserService {
@Inject
UserRepository userRepository;
@Inject
UserMapper userMapper;
@WithTransaction
public Uni<UserResponse> createUser(UserRequest request) {
return userRepository.existsByEmail(request.email())
.flatMap(exists -> {
if (exists) {
return Uni.createFrom().failure(
new DuplicateResourceException("User", "email", request.email()));
}
User user = userMapper.toEntity(request);
return userRepository.persist(user).map(userMapper::toResponse);
});
}
public Uni<UserResponse> getUserById(Long id) {
return userRepository.findById(id)
.onItem().ifNull().failWith(
() -> new ResourceNotFoundException("User", "id", id))
.map(userMapper::toResponse);
}
}
RESTEasy Resource Pattern
@Path("/api/v1/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Users", description = "User management APIs")
public class UserResource {
@Inject
UserService userService;
@POST
@Operation(summary = "Create a new user")
@APIResponse(responseCode = "201", description = "User created")
public Uni<Response> createUser(@Valid UserRequest request) {
return userService.createUser(request)
.map(user -> Response
.created(URI.create("/api/v1/users/" + user.id()))
.entity(user).build());
}
@GET
@Path("/{id}")
@RolesAllowed({"USER", "ADMIN"})
@SecurityRequirement(name = "jwt")
@Operation(summary = "Get user by ID")
public Uni<UserResponse> getUserById(@PathParam("id") Long id) {
return userService.getUserById(id);
}
}
Panache Entity Pattern
@Entity
@Table(name = "users")
public class User extends PanacheEntity {
@Column(nullable = false, unique = true)
public String email;
@Column(nullable = false)
public String name;
@Column(name = "created_at", updatable = false)
public LocalDateTime createdAt;
@PrePersist
void onCreate() {
createdAt = LocalDateTime.now();
}
// Panache active record queries
public static Uni<User> findByEmail(String email) {
return find("email", email).firstResult();
}
public static Uni<List<User>> findActive() {
return list("active", true);
}
}
DTO Records with Validation
public record UserRequest(
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
String email,
@NotBlank(message = "Password is required")
@Size(min = 8, max = 100, message = "Password must be 8-100 characters")
String password,
@NotBlank(message = "Name is required")
@Size(min = 2, max = 100)
String name
) {}
public record UserResponse(
Long id, String email, String name,
String role, boolean active, LocalDateTime createdAt
) {}
Exception Mapper Pattern
@Provider
public class ResourceNotFoundMapper
implements ExceptionMapper<ResourceNotFoundException> {
@Override
public Response toResponse(ResourceNotFoundException e) {
return Response.status(Response.Status.NOT_FOUND)
.entity(Map.of(
"title", "Resource Not Found",
"status", 404,
"detail", e.getMessage(),
"timestamp", Instant.now().toString()))
.build();
}
}
Configuration
# Application
quarkus.application.name=myproject
quarkus.http.port=8080
# Reactive datasource
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${DB_USERNAME:postgres}
quarkus.datasource.password=${DB_PASSWORD:postgres}
quarkus.datasource.reactive.url=vertx-reactive:postgresql://localhost:5432/mydb
# JDBC (for Flyway migrations)
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/mydb
# Hibernate
quarkus.hibernate-orm.database.generation=validate
quarkus.hibernate-orm.log.sql=true
# Flyway
quarkus.flyway.migrate-at-start=true
quarkus.flyway.locations=db/migration
# Dev Services
%dev.quarkus.datasource.devservices.enabled=true
%dev.quarkus.datasource.devservices.image-name=postgres:15-alpine
# Logging
quarkus.log.level=INFO
quarkus.log.category."com.example".level=DEBUG
%prod.quarkus.log.console.json=true
# JWT
mp.jwt.verify.publickey.location=publicKey.pem
mp.jwt.verify.issuer=https://example.com
# OpenAPI / Swagger
quarkus.smallrye-openapi.path=/api-docs
quarkus.swagger-ui.always-include=true
# Health
quarkus.smallrye-health.root-path=/health
Testing Overview
Standards
- Use
@QuarkusTestfor integration tests (full CDI + HTTP stack) - Use
@InjectMockto mock CDI beans in integration tests - Use
@TestSecurity(user = "test", roles = "ADMIN")to bypass auth in tests - Use REST-assured for HTTP endpoint assertions
- Use
@QuarkusTestResourcewith Testcontainers for DB integration tests - Test native with
./mvnw verify -Pnative(runs@NativeImageTestclasses) - Use
@DisplayNamefor readable test names describing behavior
Integration Test
@QuarkusTest
@DisplayName("UserResource")
class UserResourceTest {
@Test
@DisplayName("POST /api/v1/users - should create user")
void shouldCreateUser() {
given()
.contentType(ContentType.JSON)
.body(new UserRequest("[email protected]", "Password1!", "Test"))
.when()
.post("/api/v1/users")
.then()
.statusCode(201)
.body("id", notNullValue())
.body("email", equalTo("[email protected]"));
}
@Test
@TestSecurity(user = "admin", roles = "ADMIN")
@DisplayName("GET /api/v1/users - admin can list users")
void adminCanListUsers() {
given()
.when()
.get("/api/v1/users")
.then()
.statusCode(200);
}
}
Commands
# Create project
mvn io.quarkus.platform:quarkus-maven-plugin:3.6.0:create \
-DprojectGroupId=com.example \
-DprojectArtifactId=myproject \
-Dextensions="resteasy-reactive-jackson,hibernate-reactive-panache,reactive-pg-client"
# Dev mode (live reload + Dev Services)
./mvnw quarkus:dev
# Run tests
./mvnw test
# Build JVM jar
./mvnw package
# Build native binary
./mvnw package -Pnative
# Build native in container (no local GraalVM)
./mvnw package -Pnative -Dquarkus.native.container-build=true
# Verify native (integration tests against native binary)
./mvnw verify -Pnative
# List available extensions
./mvnw quarkus:list-extensions
# Add extension
./mvnw quarkus:add-extension -Dextensions="openapi"
# Build container image
./mvnw package -Dquarkus.container-image.build=true
# Deploy to Kubernetes
./mvnw package -Dquarkus.kubernetes.deploy=true
# Lint / format
./mvnw compile # Runs annotation processors
mvn checkstyle:check # If checkstyle configured
Best Practices Summary
Do
- Use Dev Services for automatic local containers
- Use Panache for simpler data access patterns
- Use reactive types (
Uni,Multi) for I/O-bound operations - Use
@WithTransactionon service methods (not resources) - Configure for native compilation early; test regularly
- Use health checks (
@Liveness,@Readiness,@Wellness) - Use profile-based configuration (
%dev.,%test.,%prod.) - Use MapStruct (compile-time) over reflection-based mappers
Do Not
- Mix blocking calls in reactive pipelines (use
@Blockingannotation if needed) - Use runtime reflection without
@RegisterForReflection - Hardcode configuration values (use
@ConfigPropertyor@ConfigMapping) - Use synchronous JDBC APIs with reactive datasources
- Ignore native build compatibility until deployment time
- Put business logic in resource classes
Advanced Topics
For detailed patterns and examples, see:
- references/patterns.md -- Reactive pipelines, Hibernate Reactive, testing strategies, GraalVM native, messaging, security, observability
External References
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?