Skip to content

About

Official Java SDK for ReqKey API key validation, credit enforcement, and request analytics

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

ReqKey Java SDK

The official Java SDK for ReqKey API-key validation, atomic credit metering, consumer rate limits, and correlated request analytics.

The SDK targets Java 17 and keeps all protocol and middleware behavior in one framework-neutral core. Framework modules only translate native request and response objects into that core model.

Modules

Artifact Use it for
com.reqkey:reqkey-core Plain Java, the reusable JDK HTTP client, and custom integrations
com.reqkey:reqkey-servlet Jakarta Servlet 6 filters
com.reqkey:reqkey-jaxrs JAX-RS / Jakarta REST 3.1 request and response filters
com.reqkey:reqkey-spring-boot-starter Spring Boot 3.5 servlet auto-configuration
com.reqkey:reqkey-examples Compilable examples; not intended as an application dependency

Quarkus and Micronaut do not have separate modules in this first release. Their JAX-RS or Servlet support can use the existing adapters without adding another copy of the ReqKey lifecycle.

Installation

The project is ready to produce normal Maven source, Javadoc, and binary JARs, but the artifacts have not been uploaded to Maven Central yet. Install the current snapshot into your local Maven repository:

git clone https://github.com/Req-Key/reqkey-java.git
cd reqkey-java
./mvnw install

Then add only the module your application needs:

<dependency>
  <groupId>com.reqkey</groupId>
  <artifactId>reqkey-core</artifactId>
  <version>0.1.0-SNAPSHOT</version>
</dependency>

Replace reqkey-core with reqkey-servlet, reqkey-jaxrs, or reqkey-spring-boot-starter as appropriate. Framework APIs are kept out of the core artifact.

Plain Java client

HttpReqKeyClient is thread-safe and should normally be created once and reused for the life of the process. It uses java.net.http.HttpClient; the only runtime dependency in core is Jackson for JSON.

import com.reqkey.HttpReqKeyClient;
import com.reqkey.ReqKeyConfig;
import com.reqkey.ValidationRequest;
import com.reqkey.VerificationResult;

ReqKeyConfig config = ReqKeyConfig.builder(System.getenv("REQKEY_PROJECT_KEY"))
    .build();

try (HttpReqKeyClient reqKey = new HttpReqKeyClient(config)) {
    VerificationResult decision = reqKey.verify(
        ValidationRequest.builder("consumer_key_...")
            .apiId("api_payments")
            .credits(2)
            .resource("/payments")
            .build()
    );

    if (!decision.isAllowed()) {
        System.out.println("Denied: " + decision.getReason().value());
    }
}

ReqKeyConfig.fromEnvironment() reads REQKEY_PROJECT_KEY and falls back to the legacy REQKEY_ROOT_KEY variable. Use ReqKeyConfig.builderFromEnvironment() when environment credentials should be combined with custom timeouts, base URLs, or retry settings.

Direct ingestion accepts request and response metadata:

reqKey.ingest(AnalyticsEvent.builder()
    .requestId(decision.getRequestId())
    .apiId("api_payments")
    .method("POST")
    .endpoint("/payments")
    .path("/payments?currency=USD")
    .statusCode(201)
    .latencyMs(38L)
    .consumerId("consumer_123")
    .build());

Ingestion requires a request ID, an API ID, or both. Request and response bodies are truncated to 1,000 Unicode characters before transmission.

Spring Boot

Add reqkey-spring-boot-starter and configure application.yml:

reqkey:
  project-key: ${REQKEY_PROJECT_KEY}
  api-id: api_payments
  mode: both
  enabled: true

  key-location: header
  key-name: X-StartupName-Key
  key-scheme: raw
  credits: 1

  exclude-paths:
    - /health
    - /docs/*
  skip-methods:
    - OPTIONS

  failure-mode: closed
  timeout: 2s
  connect-timeout: 2s

  capture-query-parameters: false
  capture-request-headers: false
  capture-response-headers: false
  capture-response-body: false
  capture-client-ip: false

The starter registers one Servlet filter. A controller can access a successful decision through request attributes:

@PostMapping("/payments")
Map<String, Object> create(HttpServletRequest request) {
    VerificationResult decision = (VerificationResult)
        request.getAttribute(ReqKeyAttributes.DECISION);
    return Map.of(
        "created", true,
        "creditsRemaining", decision.getCreditsRemaining()
    );
}

The related attribute names are:

  • ReqKeyAttributes.DECISION
  • ReqKeyAttributes.REQUEST_ID
  • ReqKeyAttributes.ERROR for a fail-open validation service error

The starter backs off when reqkey.enabled=false or reqkey.api-id is absent. It also backs off from any user-provided ReqKeyClient, ReqKeyMiddlewareConfig, ReqKeyProcessor, or named filter registration, making advanced customization possible through ordinary Spring beans.

Jakarta Servlet

Register the filter programmatically or through your container's normal filter registration mechanism:

ReqKeyMiddlewareConfig middleware = ReqKeyMiddlewareConfig.builder("api_payments")
    .keyName("X-StartupName-Key")
    .credits(request -> request.getPath().equals("/reports") ? 5 : 1)
    .excludePath("/health")
    .build();

ReqKeyFilter filter = new ReqKeyFilter(
    ReqKeyConfig.fromEnvironment(),
    middleware
);

FilterRegistration.Dynamic registration = servletContext.addFilter("reqkey", filter);
registration.addMappingForUrlPatterns(
    EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC),
    false,
    "/*"
);

The filter supports synchronous and asynchronous Servlet requests. Analytics for async requests runs from the completion listener rather than when the initial dispatcher thread returns. Text response capture is bounded and streamed through to the real response; compressed response bodies are not captured.

JAX-RS

Register one feature through your JAX-RS runtime. A portable provider wrapper works with classpath scanning and Application.getClasses():

@Provider
public final class PaymentsReqKeyFeature implements Feature {
    @Override
    public boolean configure(FeatureContext context) {
        ReqKeyMiddlewareConfig middleware = ReqKeyMiddlewareConfig.builder("api_payments")
            .keyName("Authorization")
            .keyScheme(KeyScheme.BEARER)
            .excludePath("/health")
            .build();
        return new ReqKeyFeature(ReqKeyConfig.fromEnvironment(), middleware)
            .configure(context);
    }
}

The feature installs an authentication-priority request filter and a response header/analytics filter. Portable JAX-RS response filters run before entity serialization, so response-body analytics are captured only when the response entity is already a String. Status, latency, headers, request ID, API ID, method, endpoint, path, user agent, and consumer identity remain available for all response entity types.

Shared middleware configuration

Every adapter uses ReqKeyMiddlewareConfig.

Builder option Default Purpose
mode(Mode) BOTH VALIDATE, INGEST, or correlated BOTH
enabled(boolean) true Bypass all ReqKey work when false
keyLocation(KeyLocation) HEADER HEADER, QUERY, or COOKIE
keyName(String) X-API-Key Incoming header, query parameter, or cookie name
keyScheme(KeyScheme) RAW Raw value or an Authorization-style Bearer value
apiKeyExtractor(...) — Custom extraction for nonstandard request sources
credits(int) 1 Static credit cost
credits(CreditResolver) — Request-aware dynamic credit cost
excludePath(String) none Exact path or trailing-* prefix pattern
skipMethods(Collection) OPTIONS Methods excluded from validation and analytics
protectWhen(Predicate) all selected paths Complete request-selection override
endpointResolver(Function) request path Resource/endpoint identifier sent to ReqKey
requestIdResolver(Function) — Correlation ID for ingest-only mode
consumerNameResolver(Function) — Optional trusted display name for analytics
clientIpResolver(Function) peer/fallback Trusted proxy-specific client IP override
onError(MiddlewareErrorHandler) no-op Safe validation/ingestion service failure callback
ingestDeniedRequests(boolean) true Record missing, invalid, exhausted, forbidden, and rate-limited requests in BOTH mode
failureMode(FailureMode) CLOSED Deny or allow when ReqKey itself is unavailable
errorMessage(...) built in Customer-facing denial message override
captureQueryParameters(boolean) false Include sanitized query values and path query string
captureRequestHeaders(boolean) false Include sanitized request headers
captureResponseHeaders(boolean) false Include sanitized response headers
captureResponseBody(boolean) false Include up to 1,000 textual characters where supported
captureClientIp(boolean) false Include peer/client IP metadata
captureUserAgent(boolean) true Include the User-Agent value
excludedHeader(s) sensitive defaults Add headers that must never be captured
loggingEnabled(boolean) true Enable SDK diagnostic logging
logger(ReqKeyLogger) java.util.logging Route SDK diagnostics into another logging system
logSuccessfulValidations(boolean) false Emit debug-level success decisions

Query and cookie locations intentionally accept only KeyScheme.RAW.

Custom extraction and endpoint cost

ReqKeyMiddlewareConfig config = ReqKeyMiddlewareConfig.builder("api_images")
    .apiKeyExtractor(request -> request.firstHeader("X-Custom-Key"))
    .credits(request -> {
        if (request.getMethod().equals("POST") && request.getPath().equals("/images")) {
            return 5;
        }
        return request.getPath().startsWith("/reports/") ? 2 : 1;
    })
    .endpointResolver(request -> request.getPath())
    .build();

Credit costs must be non-negative. Invalid static or dynamic values fail before the network call.

Request lifecycle and failure behavior

In BOTH mode the adapters:

  1. Select the request and extract the consumer key.
  2. Call /key/validate and atomically deduct the configured credits.
  3. Stop missing, invalid, exhausted, forbidden, or rate-limited requests.
  4. Expose an allowed decision to the application.
  5. Collect bounded response metadata and call /ingest with the validation request ID.

Validation fails closed by default. FailureMode.OPEN allows the application to run only when ReqKey has a transport, timeout, authentication, or service failure. Explicit access decisions are always enforced.

Ingestion failures never replace a completed application response. The MiddlewareErrorHandler receives a provider-neutral event containing the operation, method, path, request ID, response status, and typed error. It never contains project credentials, consumer API keys, query values, headers, or bodies.

HTTP timeouts and retries

RetryPolicy retry = RetryPolicy.builder()
    .maxAttempts(3)
    .initialBackoff(Duration.ofMillis(100))
    .maxBackoff(Duration.ofSeconds(1))
    .retryIngestion(true)
    .retryValidation(false)
    .retryServerErrors(true)
    .build();

ReqKeyConfig clientConfig = ReqKeyConfig.builder(projectKey)
    .timeout(Duration.ofSeconds(2))
    .connectTimeout(Duration.ofSeconds(2))
    .retryPolicy(retry)
    .build();

The default is one attempt. Ingestion may use the configured retry count. Validation retries remain disabled unless explicitly enabled because a validation request can deduct credits and safe replay requires server-side idempotency. Retries are bounded, use exponential backoff, and apply to transport failures, timeouts, and optionally HTTP 5xx responses.

Spring Boot exposes the same settings under reqkey.retry:

reqkey:
  retry:
    max-attempts: 3
    initial-backoff: 100ms
    max-backoff: 1s
    ingestion: true
    validation: false
    server-errors: true

Errors and validation decisions

Direct client service failures use typed unchecked exceptions:

  • ReqKeyConfigurationException
  • ReqKeyTransportException
  • ReqKeyTimeoutException
  • ReqKeyApiException
  • ReqKeyAuthenticationException

Normal consumer denials are VerificationResult values rather than exceptions. Stable reasons are VALID, INVALID_KEY, INSUFFICIENT_CREDITS, FORBIDDEN, RATE_LIMITED, and DENIED.

Framework denial responses contain a stable JSON error identifier and a customizable message. Rate-limited responses preserve Retry-After.

Successful framework responses include available validation metadata:

  • X-ReqKey-Request-ID
  • X-ReqKey-Credits-Limit
  • X-ReqKey-Credits-Remaining
  • X-ReqKey-Validation-Time-Ms

Analytics privacy defaults

The default event contains the request ID, API ID, method, normalized endpoint, response status, latency, user agent, and consumer key in the dedicated apiKey identity field. Query values, captured headers, response bodies, and client IPs are opt-in.

Authorization, cookies, Set-Cookie, proxy authorization, common API-key headers, and the configured consumer-key header are always removed from captured headers. When a key comes from the query string, that parameter is removed from both queryParams and the recorded path.

Only trust forwarding headers through clientIpResolver when your deployment proxy overwrites them. Without an explicit resolver, framework-provided peer addresses take precedence and common proxy headers are only a fallback when no peer address exists.

Testing

Run all unit and framework integration tests:

./mvnw verify

Run one module and its dependencies:

./mvnw -pl reqkey-servlet -am test
./mvnw -pl reqkey-jaxrs -am test
./mvnw -pl reqkey-spring-boot-starter -am test

The test suite covers the live HTTP contract through an in-process server, header/query/cookie and Bearer extraction, dynamic credit costs, decision and error mapping, body limits, timeout behavior, retry safety, fail-open and fail-closed handling, analytics redaction, and each framework adapter.

Publishing readiness

The parent POM includes project metadata, SCM coordinates, license details, and a release profile that attaches source and Javadoc JARs:

./mvnw -Prelease clean verify

Repository credentials, signing keys, and a Maven Central publishing plugin are intentionally not embedded. Those organization-specific settings can be added when the first public release is ready without changing SDK consumers.

See the complete examples in reqkey-examples.

About

Official Java SDK for ReqKey API key validation, credit enforcement, and request analytics

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages