MicroProfile
Eclipse MicroProfile rules and best practices for Claude Code.
5.5k
5 TEMPLATES
MicroProfile Core Rules
# CLAUDE.md — MicroProfile Core ## What MicroProfile is - A set of cloud-native specs that complement Jakarta EE: Config, Health, Metrics, OpenAPI, REST Client, Fault Tolerance, JWT, OpenTracing. - Implemented by Quarkus (SmallRye), Open Liberty, Helidon, Payara Micro, WildFly. - Use it on top of Jakarta EE for the "operability" layer that Jakarta EE proper doesn't cover. ## Configuration (MicroProfile Config) - `@ConfigProperty(name = "...")` to inject config values into beans: ```java @Inject @ConfigProperty(name = "app.email.from") String fromAddress; ``` - Sources by precedence: system properties → env vars → `microprofile-config.properties` → user-defined sources. - For grouped, type-safe config, use `@ConfigMapping` (Quarkus / SmallRye) or roll a `@ApplicationScoped` config bean. - Default values: `@ConfigProperty(name = "app.timeout", defaultValue = "30")`. ## Health (MicroProfile Health) - `/health` for combined, `/health/live` for liveness, `/health/ready` for readiness. - One `HealthCheck` per dependency: ```java @Liveness @ApplicationScoped public class AppLive implements HealthCheck { public HealthCheckResponse call() { return HealthCheckResponse.up("app"); } } ``` - Liveness checks must not depend on external systems (DB, Redis). They check process health only. - Readiness checks include external systems. Failure means "remove from load balancer". ## Metrics (MicroProfile Metrics / Micrometer) - MicroProfile Metrics API → SmallRye Metrics or Micrometer (Quarkus 3 prefers Micrometer). - Standard metrics: HTTP requests, JVM, GC, threads. Custom: ```java @Counted(name = "checkout_attempts") public void checkout(...) { ... } ``` - Tag with low-cardinality dimensions (`endpoint`, `status`, `tenant_id` only if bounded). Never `user_id`. - Expose at `/metrics` in Prometheus format. ## REST Client - Type-safe HTTP clients defined as interfaces (see the dedicated REST Client guide). ## Fault tolerance - Retries, circuit breaker, fallback, timeout, bulkhead — declarative annotations on methods (see the dedicated Fault Tolerance guide). ## OpenAPI - `@OpenAPIDefinition` on the application class for global metadata. - Most documentation is auto-generated from JAX-RS + Bean Validation. - See dedicated OpenAPI guide for details. ## JWT - `@LoginConfig(authMethod = "MP-JWT")` on the application class enables JWT auth. - Inject claims with `@Inject @Claim("groups") Instance<String> groups`. - See the dedicated JWT auth guide. ## Don't - Don't reach for a separate config library when MicroProfile Config is on the platform. Use what's there. - Don't mix MicroProfile Metrics and Micrometer in the same project — pick one. - Don't use `Liveness` checks that depend on the DB. The pod will be restarted unnecessarily on DB blips. - Don't expose `/metrics` to the public internet without auth.MicroProfile Fault Tolerance
# CLAUDE.md — MicroProfile Fault Tolerance ## Annotations - `@Retry` — retry the method on failure. - `@Timeout` — fail fast if the method runs too long. - `@CircuitBreaker` — open the circuit on cascading failure. - `@Bulkhead` — limit concurrent invocations. - `@Fallback` — alternative path on failure. - `@Asynchronous` — run on a separate thread pool. Apply on methods that call external systems (HTTP, DB, message broker). Don't put them on every method "just in case". ## Retry ```java @Retry(maxRetries = 3, delay = 200, jitter = 100, retryOn = IOException.class) ChargeResponse charge(ChargeRequest req) { ... } ``` - Always set `delay` and `jitter` — synchronized retries cause thundering herds. - Specify `retryOn` for transient errors only. Don't retry on `IllegalArgumentException` or domain failures. - `abortOn = NotFoundException.class` — give up immediately for non-retryable errors. ## Timeout ```java @Timeout(value = 5, unit = ChronoUnit.SECONDS) ChargeResponse charge(...) { ... } ``` - The annotation works by interrupting the thread. The method must respect interruption. - Timeout must be less than downstream's read timeout, or the client gives up before the server responds. ## Circuit breaker ```java @CircuitBreaker( requestVolumeThreshold = 10, // window of last 10 requests failureRatio = 0.5, // 50% failure → open delay = 5000 // half-open after 5s ) ChargeResponse charge(...) { ... } ``` - Three states: closed (normal), open (failing fast), half-open (testing recovery). - Use for downstreams that occasionally fail hard. Pointless for fast, reliable calls. - Combine with `@Fallback` for graceful degradation when the circuit is open. ## Bulkhead ```java @Bulkhead(value = 10) // max 10 concurrent calls ChargeResponse charge(...) { ... } ``` - Limits resource exhaustion when one slow downstream blocks all threads. - Pair with `@Asynchronous` for queue-based bulkhead with `waitingTaskQueue`. ## Fallback ```java @Retry(maxRetries = 2) @Fallback(fallbackMethod = "cachedCharge") ChargeResponse charge(ChargeRequest req) { ... } ChargeResponse cachedCharge(ChargeRequest req) { return cache.lookup(req); } ``` - The fallback method must have the same signature. - For class-based fallbacks, implement `FallbackHandler<T>`. - Don't use fallback to hide bugs. It's for graceful degradation, not error suppression. ## Stacking - Order: `@Bulkhead` → `@Timeout` → `@CircuitBreaker` → `@Retry` → `@Fallback`. - Retry is outermost: each retry counts as a fresh circuit-breaker call. - Fallback is innermost: only invoked after all other strategies fail. ## Don't - Don't retry POST requests without idempotency keys. You'll create duplicates. - Don't set `maxRetries=Integer.MAX_VALUE`. Bound it. - Don't use `@Asynchronous` for short calls. The thread-pool overhead exceeds the benefit. - Don't use one big `@CircuitBreaker` for many distinct downstreams. One per logical dependency.MicroProfile REST Client
# CLAUDE.md — MicroProfile REST Client ## Type-safe clients - Define an interface annotated with `@RegisterRestClient`: ```java @RegisterRestClient(configKey = "stripe-api") @Path("/v1") public interface StripeApi { @POST @Path("/charges") @Consumes(JSON) @Produces(JSON) ChargeResponse charge(ChargeRequest req); } ``` - Inject as `@RestClient StripeApi stripe`. - Same JAX-RS annotations as resources — `@Path`, `@GET`, `@POST`, `@Header`, `@QueryParam`. ## Configuration - Configure base URL by `configKey`: ``` stripe-api/mp-rest/url=https://api.stripe.com stripe-api/mp-rest/connectTimeout=2000 stripe-api/mp-rest/readTimeout=5000 ``` - Override per environment via standard Config sources. - Don't hardcode URLs in `@RegisterRestClient(baseUri = "...")`. Use `configKey` and override via config. ## Headers - Static headers via `@ClientHeaderParam`: ```java @ClientHeaderParam(name = "Authorization", value = "Bearer ${stripe.api.key}") public interface StripeApi { ... } ``` - Dynamic headers via `@HeaderParam` on a method parameter, or via a `ClientHeadersFactory` for cross-cutting (request id, tracing). ## Async - Return `CompletionStage<T>` for non-blocking calls: ```java CompletionStage<ChargeResponse> chargeAsync(ChargeRequest req); ``` - Pair with `@Asynchronous` (MicroProfile Fault Tolerance) for thread-pool isolation. - For reactive stacks (Quarkus + Mutiny), return `Uni<T>` instead. ## Error handling - 4xx/5xx responses throw `WebApplicationException` by default. Catch and translate to domain exceptions. - Custom exception mapping via `ResponseExceptionMapper`: ```java public class StripeErrorMapper implements ResponseExceptionMapper<StripeException> { public StripeException toThrowable(Response r) { return new StripeException(r.readEntity(StripeError.class)); } } ``` - Register via `@RegisterProvider(StripeErrorMapper.class)` on the client interface. ## Filtering / interceptors - `@RegisterProvider(MyClientFilter.class)` on the interface. - Use for: request id propagation, retry hooks, request/response logging. - Avoid logging full bodies in production filters — sanitize or sample. ## Resilience - Combine with **MicroProfile Fault Tolerance**: ```java @Retry(maxRetries = 3, delay = 200) @Timeout(value = 5, unit = ChronoUnit.SECONDS) @CircuitBreaker(requestVolumeThreshold = 4) ChargeResponse charge(ChargeRequest req); ``` - Timeout < downstream's read timeout. Otherwise the client gives up before the server responds. ## Testing - For unit tests, mock the interface as you would any CDI bean. - For integration tests, use **WireMock** or **MockServer** to stand up a fake HTTP target. - Don't hit real third-party APIs in tests. Sandbox/mock environments only. ## Don't - Don't share a single client interface across many unrelated services. One client per upstream. - Don't put auth tokens in `@ClientHeaderParam(value = "Bearer xxx")` literals — use config. - Don't return `Response` from REST Client methods. Type-safe deserialization is the point. - Don't create a new client instance per call. Inject — the runtime caches.MicroProfile JWT Authentication
# CLAUDE.md — MicroProfile JWT Authentication ## Setup - Add the MP-JWT spec to your runtime (Quarkus: `quarkus-smallrye-jwt`; Open Liberty: bundled). - On the application class: ```java @LoginConfig(authMethod = "MP-JWT", realmName = "myapp") @ApplicationPath("/") public class App extends Application {} ``` - Configure the public key for verification: ``` mp.jwt.verify.publickey.location=https://auth.example.com/.well-known/jwks.json mp.jwt.verify.issuer=https://auth.example.com mp.jwt.verify.audiences=myapp ``` ## Securing endpoints - Use Jakarta Security annotations: ```java @GET @RolesAllowed("user") public List<Order> myOrders() { ... } @POST @RolesAllowed("admin") public void deleteUser(@PathParam("id") String id) { ... } ``` - `@PermitAll` on public endpoints — be explicit so future developers don't break it accidentally. - `@DenyAll` on placeholder methods you haven't authorized yet. ## Reading claims - Inject the full token: `@Inject JsonWebToken jwt`. - Inject specific claims: ```java @Inject @Claim("email") String email; @Inject @Claim("groups") Set<String> groups; @Inject @Claim("sub") String userId; ``` - Standard claims: `iss`, `sub`, `aud`, `exp`, `iat`, `groups`. Custom claims work the same way. ## Roles - The `groups` claim is mapped to roles by default. Configure the claim name if your provider uses a different name: ``` mp.jwt.claim.roles.path=resource_access.myapp.roles ``` - Don't trust the token blindly. Validate signature, issuer, audience, expiry. The library does this; just configure correctly. ## Token issuance - MP-JWT specifies the **verification** side — issuance is up to your auth provider (Keycloak, Auth0, Okta, AWS Cognito). - For dev, use **smallrye-jwt-build** to issue tokens locally. Never use it in production. - Pin the JWKS URL to a fixed location — don't accept arbitrary issuers. ## Token transport - Standard `Authorization: Bearer <token>` header. - For browser apps, store in HttpOnly Secure cookies. The MP-JWT extension can read from cookies if configured: ``` mp.jwt.token.header=Cookie mp.jwt.token.cookie=jwt ``` - Never store JWTs in `localStorage`. ## Refresh - MP-JWT doesn't define refresh. Implement at the auth provider — clients send refresh tokens to the auth server, get fresh access tokens. - Keep access tokens short-lived (15 min). Refresh tokens longer-lived but revocable server-side. ## Don't - Don't put sensitive data (PII, plan info) in JWT claims. The token is base64url-decodable by anyone. - Don't accept tokens without verifying the signature. - Don't use `@RolesAllowed` and ignore object-level authorization. JWT proves identity; ownership checks happen in the service. - Don't issue long-lived (months) access tokens to "avoid" refresh complexity.MicroProfile OpenAPI
# CLAUDE.md — MicroProfile OpenAPI ## Setup - Add the MP-OpenAPI implementation: SmallRye OpenAPI in Quarkus, Open Liberty's bundled implementation, etc. - Spec is exposed at `/openapi`. Many runtimes also expose Swagger UI at `/openapi/ui` or similar. - Most documentation generates from JAX-RS + Bean Validation. Annotate only when the inferred docs are wrong or insufficient. ## Application metadata ```java @OpenAPIDefinition( info = @Info( title = "My API", version = "1.0.0", contact = @Contact(name = "Team", email = "team@example.com"), license = @License(name = "Apache 2.0", url = "https://www.apache.org/licenses/LICENSE-2.0") ), servers = { @Server(url = "https://api.example.com", description = "Production"), @Server(url = "https://staging.example.com", description = "Staging") } ) @ApplicationPath("/") public class App extends Application {} ``` ## Operation annotations - `@Operation(summary = "...", description = "...")` on resource methods. - `@APIResponse(responseCode = "200", description = "...", content = @Content(...))` for documented responses. - `@APIResponses` to group multiple. Always document at least the success status and the most common error. - `@Parameter(description = "...", example = "abc-123")` on path/query params for clarity. ## Schemas - DTOs are auto-discovered from JAX-RS method signatures. - Customize with `@Schema` on classes or fields: ```java @Schema(description = "Order item with quantity and product reference") record OrderItem( @Schema(required = true, example = "prod_123") String productId, @Schema(minimum = "1", maximum = "100", example = "1") int quantity ) {} ``` - Bean Validation annotations (`@NotBlank`, `@Size`) flow through automatically — don't duplicate as `@Schema(required = true)` everywhere. ## Tags - Group endpoints with `@Tag(name = "Users", description = "User management")`. - One tag per resource class — keeps the rendered UI navigable. - Order tags via `@OpenAPIDefinition(tags = { @Tag(name = "..."), ... })` for consistent display. ## Security schemes ```java @SecurityScheme( securitySchemeName = "bearer", type = SecuritySchemeType.HTTP, scheme = "bearer", bearerFormat = "JWT" ) @OpenAPIDefinition(security = @SecurityRequirement(name = "bearer")) ``` - Apply per operation with `@SecurityRequirement` if the global default doesn't fit. - Public endpoints: `@SecurityRequirements` (empty) to override. ## Examples - Concrete examples make the spec actually usable for client developers: ```java @APIResponse(responseCode = "200", content = @Content( schema = @Schema(implementation = User.class), examples = @ExampleObject(name = "user", value = "{\"id\":\"1\",\"email\":\"a@b\"}") )) ``` ## Workflow - Generate the spec at build time (`mvn package` produces `META-INF/openapi.yaml`). - Diff the spec in CI to catch unintended API changes. - Publish to a developer portal (Swagger Hub, Stoplight, your own). ## Don't - Don't write the OpenAPI spec by hand alongside annotations. Pick one source of truth. - Don't expose `/openapi` to the public internet without auth if your spec leaks internal details. - Don't use `@Schema(implementation = Object.class)` to escape strict typing — fix the type. - Don't ship a spec that doesn't match the implementation. Validate in tests.
Have a CLAUDE.md template that works for you?
Send it in — we’ll credit you and publish it under the right tags.