Upgrade to RESTHeart v9
RESTHeart v9 introduces significant new features, improvements, and changes focused on security, performance, developer experience, and standards compliance.
This page summarizes the new features and provides guidance on upgrading from previous versions.
Summary
Core
| Feature | Description |
|---|---|
Java 25 LTS Migration |
Upgraded to Java 25 LTS for enhanced performance, security, and long-term support |
Consolidate Docker Images to GraalVM Only |
Simplified Docker deployment with two variants: GraalVM-based (full plugin support) and native binary (optimized for Kubernetes) |
Enhanced RESTHeart Plugin Logging |
Comprehensive debug-level logging with tree-style visual grouping and tracing IDs for better observability |
Distributed Tracing Headers Support |
Automatic capture and display of tracing headers in request logs for distributed tracing integration with Zipkin, Jaeger, and OpenTelemetry |
Custom Metrics Support for Monitoring Plugin |
Programmatic API for registering and collecting application-specific metrics (counters, gauges, histograms, summaries) |
Refactor Cache Implementation |
Migrated from |
GraalVM Native Image Support |
RESTHeart officially listed as framework tested with GraalVM native image |
Optimize CORS Headers |
Reduced default CORS headers to minimum required set for improved security, requiring services to explicitly expose additional headers |
MongoService
| Feature | Description |
|---|---|
Improve Aggregation Pipeline Security |
Stage and operator blacklisting to prevent dangerous operations and JavaScript execution in aggregation pipelines |
Remove Deprecated Map-Reduce Pipeline Type |
Eliminated support for MongoDB’s deprecated map-reduce functionality in favor of aggregation pipelines |
Cleanup Bad Request Response |
Simplified error responses by removing redundant exception metadata |
GraphQLService
| Feature | Description |
|---|---|
Simplify GraphQL App Descriptor |
Streamlined configuration with optional URI field and automatic URI generation from |
Security
| Feature | Description |
|---|---|
Simplify Authentication with OAuth 2.0-Compatible /token Endpoint |
Dedicated |
Add |
New intercept point for custom handling of failed authentication/authorization attempts |
Add Request Body Predicates |
Support for |
New Permission Variables - @rnd and @qparams |
Cryptographically secure random string generation and query parameter access in ACL system |
Add Request Attached Parameters to Account Properties |
Transfer request parameters to account properties after successful authentication for enhanced authorization |
Details
Java 25 LTS Migration
RESTHeart v9 requires Java 25 LTS, released on September 16, 2025.
Note that Java 25 is a Long-Term Support (LTS) release.
Key Changes:
-
Updated to Java 25 across the entire codebase
-
GraalVM configuration refactored from separate config files to unified
reachability-metadata.jsonformat -
Updated
undertow-coreandwildfly-commonversions for Java 25 native image compatibility -
Docker base image updated to
eclipse-temurin:25-jre -
Upgraded
Dockerfile.graalvmto version 25-graalce
Consolidate Docker Images to GraalVM Only
RESTHeart v9 consolidates Docker image distribution from three variants to two, eliminating the OpenJDK variant.
New Image Strategy:
-
softinstigate/restheart:latest(GraalVM-based)-
Supports dynamic JAR and JavaScript plugin loading
-
~256MB minimum RAM
-
2-3 second startup time
-
Complete plugin functionality enabled by default
-
-
softinstigate/restheart:latest-native(Native binary)-
Optimized for Kubernetes environments
-
~64MB minimum RAM
-
<100ms startup time
-
JavaScript-only plugin support
-
Benefits:
-
Reduced maintenance overhead
-
Eliminated user confusion regarding image selection
-
Improved default plugin functionality
-
Maintained optimization for Kubernetes deployments
Enhanced RESTHeart Plugin Logging
RESTHeart v9 introduces comprehensive debug-level logging across all plugin types for improved observability.
Enhanced Logging Coverage:
-
Interceptors (pre-auth, post-auth, post-request, async post-request)
-
Services, Providers, Initializers
-
Authentication Mechanisms, Authenticators, Authorizers, Token Managers
Key Features:
-
Tree-style visual log grouping for better readability
-
Tracing IDs added to log messages for request tracking
-
Reusable security handlers to reduce redundant initialization logs
-
Logs moved to TRACE level to reduce DEBUG-level verbosity
-
Fixed alignment and visual grouping of authentication/authorization logs
This enhancement makes it easier to:
-
Debug issues in the request processing pipeline
-
Monitor performance and identify bottlenecks
-
Understand authentication and authorization decisions
-
Troubleshoot interceptor chains
Distributed Tracing Headers Support
RESTHeart v9 simplifies distributed tracing integration by automatically displaying configured tracing headers in request logs without requiring custom Logback configuration.
Problems Addressed:
Previously, displaying tracing headers in logs required: 1. Custom Logback configuration files 2. Manual pattern modifications with MDC variables 3. Additional setup friction for teams integrating with tracing systems
New Approach:
Simply configure the headers you want to track in restheart.yml:
logging:
tracing-headers:
- x-b3-traceid # Zipkin
- x-request-id # Custom
- uber-trace-id # Jaeger
Key Features:
-
Automatic display of all configured tracing headers in logs
-
Echo captured headers back in HTTP responses
-
Fallback to auto-generated trace IDs when headers are absent
-
Headers remain accessible in SLF4J MDC for custom logging
-
Performance-optimized with cached HttpString instances
Log Output:
When tracing headers are present:
[x-b3-traceid: 7f8a3d2c, x-request-id: abc123] ⚬ GET http://localhost:8080/ping => status=200 elapsed=56ms
When no headers are present (fallback):
[trace-id: 0001] ⚬ GET http://localhost:8080/ping => status=200 elapsed=56ms
Benefits:
-
Zero custom configuration required
-
Seamless integration with Zipkin, Jaeger, and OpenTelemetry
-
First configured header becomes primary trace ID
-
Request correlation across distributed systems
-
No performance overhead for requests without headers
See the Auditing documentation for complete configuration details.
Custom Metrics Support for Monitoring Plugin
RESTHeart v9 extends the monitoring plugin to allow registration and collection of custom application-specific metrics.
Supported Metric Types:
-
Counter - Monotonically increasing values (e.g., requests processed)
-
Gauge - Current value that can increase or decrease (e.g., queue size)
-
Histogram - Distribution of values (e.g., request durations)
-
Summary - Similar to histogram with configurable quantiles
Usage Example:
@RegisterPlugin(
name = "customMetricsInit",
description = "Registers custom metrics",
enabledByDefault = true)
public class CustomMetricsInitializer implements Initializer {
@Override
public void init() {
// Register a counter
Metrics.registerCounter(
"custom_requests_total",
"Total number of custom requests",
"endpoint", "method"
);
// Register a gauge
Metrics.registerGauge(
"cache_size",
"Current cache size",
() -> getCacheSize()
);
}
}
Key Features:
-
Register metrics at startup using Initializers
-
Update metrics during request handling in Services/Interceptors
-
Expose metrics at
/metricsendpoint in Prometheus format -
Support for custom labels
-
Configuration via
restheart.yml
Refactor Cache Implementation from AsyncLoadingCache to LoadingCache
RESTHeart v9 migrates from Caffeine’s AsyncLoadingCache to LoadingCache.
Rationale:
With recent Java improvements, blocking operations no longer pin virtual threads to platform threads, eliminating the original need for the async variant.
Benefits:
-
Reduced overhead from unnecessary virtual thread creation
-
Simpler, more straightforward codebase
-
Improved performance with less context switching
-
Enhanced debugging and reasoning about synchronous operations
-
Proper virtual thread release during blocking operations
GraalVM Native Image Support
RESTHeart is now officially listed on GraalVM’s framework compatibility page as a tested framework that fully supports native image compilation.
Implementation Highlights:
-
Automatic reflection configuration of plugins for native images
-
Streamlined plugin development for native builds
-
Unified reachability metadata configuration format
-
Explicit initialization of PluginsClassloader for native image execution
CORS Headers Optimization
RESTHeart v9 implements CORS (Cross-Origin Resource Sharing) headers following the principle of least privilege, using minimal defaults that services can extend as needed.
Default CORS Headers:
RESTHeart provides secure, minimal defaults for CORS headers:
-
Allow Headers:
Authorization,Content-Type,X-Requested-With,No-Auth-Challenge -
Expose Headers: Empty by default - services explicitly declare what they expose
-
Allow Methods:
GET,PUT,POST,PATCH,DELETE -
Allow Origin:
*(any origin) -
Allow Credentials:
true
Customizing CORS Behavior:
Services customize CORS behavior by overriding methods in the CORSHeaders interface:
-
accessControlExposeHeaders()- Declares which response headers are exposed to browser clients -
accessControlAllowMethods()- Specifies HTTP methods supported by the service -
accessControlAllowHeaders()- Lists which request headers are allowed -
accessControlAllowOrigin()- Restricts allowed origins (defaults to*) -
corsEnabled()- Disables CORS entirely for internal APIs
Example - Service exposing response headers:
@RegisterPlugin(
name = "resourceService",
description = "Service that creates resources")
public class ResourceService implements JsonService {
@Override
public void handle(JsonRequest req, JsonResponse res) {
if (req.isPost()) {
var id = createResource(req.getContent());
res.getHeaders().add(HttpString.tryFromString("Location"),
"/resources/" + id);
res.setStatusCode(HttpStatus.SC_CREATED);
}
}
@Override
public String accessControlExposeHeaders() {
// Make Location header accessible to browser clients
return "Location, ETag";
}
}
Key Point:
Response headers like Location, ETag, or custom headers are not accessible to browser JavaScript unless explicitly exposed via accessControlExposeHeaders(). Services that set such headers must override this method.
See the CORS Handling documentation for complete details and examples.
Improve Aggregation Pipeline Security with Stage and Operator Blacklisting
RESTHeart v9 addresses a security gap where dangerous MongoDB operators could bypass protections when used in aggregation pipelines.
Security Controls:
-
Stage-Level Blocking - Restricts dangerous pipeline stages:
-
$out- Writing to collections -
$merge- Merging data into collections -
$lookup- Cross-collection joins -
$graphLookup- Graph traversal operations -
$unionWith- Combining collections
-
-
Operator-Level Blocking - Prevents JavaScript-executing operators:
-
$where- JavaScript filter expressions -
$function- Custom JavaScript functions -
$accumulator- JavaScript-based aggregation
-
-
Cross-Database Validation - Ensures lookup operations only reference collections in the same database
-
JavaScript Execution Control - Comprehensive blocking of all JavaScript-executing operators
Configuration Example:
mongo:
aggregationSecurity:
blacklistedStages:
- $out
- $merge
- $lookup
- $graphLookup
- $unionWith
blacklistedOperators:
- $where
- $function
- $accumulator
allowCrossDatabaseOperations: false
allowJavaScriptExecution: false
Error Handling:
Security violations return HTTP 403 Forbidden with specific violation types:
-
BLACKLISTED_STAGE -
BLACKLISTED_OPERATOR -
CROSS_DATABASE_ACCESS -
CROSS_DATABASE_OUTPUT -
JAVASCRIPT_EXECUTION
Note: Feature is enabled by default with conservative settings.
Remove Deprecated Map-Reduce Pipeline Type
RESTHeart v9 eliminates support for MongoDB’s map-reduce pipeline functionality.
Breaking Change: Users must migrate to aggregation pipelines before upgrading to v9.
Rationale:
-
MongoDB deprecated map-reduce starting in version 5.0
-
Map-reduce aggregations are inherently difficult to secure
-
RESTHeart maintained this feature through v7 and v8 for backward compatibility
-
Reduces code complexity and maintenance burden
Cleanup Bad Request Response from MongoService
RESTHeart v9 simplifies error responses when MongoDB requests fail due to invalid request bodies.
Before:
{
"exception": "org.restheart.exchange.BadRequestException",
"exception message": "Invalid JSON. JSON reader was expecting a value but found 'x1'.",
"http status code": 400,
"http status description": "Bad Request",
"message": "Invalid JSON. JSON reader was expecting a value but found 'x1'."
}
After:
{
"http status code": 400,
"http status description": "Bad Request",
"message": "Invalid JSON. JSON reader was expecting a value but found 'x1'."
}
The response now contains only essential information without redundant exception metadata.
Simplify GraphQL App Descriptor
RESTHeart v9 streamlines the GraphQL App Description document structure in MongoDB.
Key Changes:
-
Remove
descriptor.name- The_idfield becomes the sole identifier -
Make
descriptor.urioptional - Auto-generates URIs following pattern/{_id}when omitted -
Enforce string type for
_id- Required when no custom URI is specified -
Prevent URI collisions - Validation prevents
_idvalues from matching existing URIs and vice versa
Minimal Configuration Example:
{
"_id": "user-api"
}
This automatically generates the URI /user-api.
Custom URI Example:
{
"_id": "user-api",
"descriptor": {
"uri": "/v1/users"
}
}
Migration: Remove descriptor.name from existing GraphQL app definition documents.
Simplify Authentication with OAuth 2.0-Compatible /token Endpoint
RESTHeart v9 replaces the approach of allowing authentication on any secure endpoint with dedicated /token endpoints following OAuth 2.0 conventions.
Problems Addressed:
-
Security concerns - Token generation was possible on any endpoint (e.g.,
/users?set-auth-cookie) -
Performance issues - TokenInjector ran on EVERY authenticated request with 2-5ms cache lookup overhead
-
Design clarity - Previous system was non-intuitive and violated REST principles
New Endpoints:
-
POST /token
-
OAuth 2.0-style endpoint
-
Supports HTTP Basic Auth and form-based credentials
-
Returns
access_token,token_type, andexpires_in
-
-
POST /token/cookie
-
Sets HttpOnly cookies without exposing tokens in response bodies
-
Enhanced security for browser-based applications
-
Example Request:
# Using Basic Auth
curl -X POST http://localhost:8080/token \
-u username:password
# Using form data (OAuth 2.0 style)
curl -X POST http://localhost:8080/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password&username=admin&password=secret"
Example Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 900
}
Performance Improvement:
85% performance improvement for token-based authentication (347ms → 50ms for 100 requests)
Configuration Changes:
-
Default JWT authentication is now enabled
-
RND tokens are disabled by default
-
Cookie handlers are enabled by default
Add REQUEST_AFTER_FAILED_AUTH InterceptPoint
RESTHeart v9 introduces a new intercept point that activates when authentication or authorization fails.
Use Cases:
-
Protection against brute force attacks
-
Customized error responses for auth failures
-
Security event logging
-
Rate limiting based on failed attempts
Example Implementation:
@RegisterPlugin(
name = "failedAuthLogger",
description = "Logs failed authentication attempts",
interceptPoint = InterceptPoint.REQUEST_AFTER_FAILED_AUTH)
public class FailedAuthLoggerInterceptor implements WildcardInterceptor {
@Override
public void handle(Request<?> request, Response<?> response) throws Exception {
var account = request.getAuthenticatedAccount();
var clientIp = request.getRemoteAddress();
if (account == null) {
LOGGER.warn("Authentication failed from IP: {}", clientIp);
} else {
LOGGER.warn("Authorization failed for user: {} from IP: {}",
account.getPrincipal(), clientIp);
}
}
@Override
public boolean resolve(Request<?> request, Response<?> response) {
return true; // Intercept all failed auth attempts
}
}
The intercept point executes after auth failures but before sending error responses to clients, allowing custom handling and logging.
Add Request Body Predicates
RESTHeart v9 implements support for @request.body variables in the ACL predicate system.
Key Features:
-
Access to complete request bodies and nested properties via dot notation
-
Array element indexing (e.g.,
@request.body.roles.0) -
Support for BsonRequest, JsonRequest, and MongoRequest types
-
Integration with Undertow comparison operators:
equals(),greater-than(),less-than(),regex()
Configuration Example:
permissions:
- role: user
predicate: >
path-prefix('/transactions') and
method(POST) and
less-than(@request.body.amount, 1000)
This example restricts users to creating transactions with amounts less than 1000.
Nested Property Access:
permissions:
- role: user
predicate: >
path-prefix('/orders') and
equals(@request.body.payment.method, 'credit_card')
Notable Behavior:
Missing properties and non-JSON/BSON requests resolve to null, implementing fail-safe denial semantics.
New Permission Variables - @rnd and @qparams
RESTHeart v9 adds two new variables to the Access Control List (ACL) system.
@rnd(bits) - Cryptographically Secure Random String Generation
Generates random strings using java.security.SecureRandom, encoded as hexadecimal.
Use Cases:
-
Secure user registration with automatic OTP generation
-
API key generation for service accounts
-
Temporary access tokens for password reset workflows
-
Multi-factor authentication with verification codes
Example - User Registration with OTP:
permissions:
- role: $unauthenticated
predicate: path('/users') and method(POST)
mongo:
mergeRequest:
otp: "@rnd(32)"
verified: false
role: "pending"
@qparams['key'] - Query Parameter Access
Retrieves query parameter values from request URLs for authorization decisions.
Example - OTP Verification:
permissions:
- role: pending
predicate: >
path('/users/{userid}/verify') and
method(PATCH) and
equals(@user._id, '{userid}') and
equals(@user.otp, @qparams['otp'])
mongo:
mergeRequest:
verified: true
role: "user"
Complete Workflow:
-
User registers → System generates OTP and stores it
-
User receives OTP (via email/SMS)
-
User calls verify endpoint with OTP as query parameter
-
System validates OTP using
@qparams['otp'] -
Upon success, user role is escalated and account is verified
Add Request Attached Parameters to Account Properties in MongoRealmAuthenticator
RESTHeart v9 enhances MongoRealmAuthenticator to automatically include specified request parameters as account properties after successful authentication.
Problem:
Interceptors running at BEFORE_AUTH can attach parameters using request.attachParam(key, value), but these parameters were not carried over to authenticated account properties.
Solution:
Add an attached-props configuration option:
mongoRealmAuthenticator:
enabled: true
users-db: restheart
users-collection: users
attached-props:
- tenantId
- organizationId
- host
Behaviors:
-
Only listed parameters are copied (backward compatible)
-
Missing parameters are silently skipped
-
Parameters are transferred after successful authentication
Use Cases:
-
Custom attributes - Attach organizational metadata or user-specific data accessible throughout request lifecycle
-
Dynamic authorization - Use account properties in security predicates without additional database lookups
Example - Multi-tenant Authorization:
An interceptor at BEFORE_AUTH extracts tenant ID from request and attaches it:
@Override
public void handle(Request<?> request, Response<?> response) {
var tenantId = extractTenantId(request);
request.attachParam("tenantId", tenantId);
}
After authentication, the tenant ID is available in account properties and can be used in ACL predicates:
permissions:
- role: user
predicate: equals(@user.tenantId, @request.tenantId)
Upgrade Guide
Prerequisites
-
Java 25 LTS must be installed
-
MongoDB 5.0 or later is recommended
-
Review deprecated features list
Breaking Changes
-
JWT Configuration Restructuring
-
JWT Now Default: JWT authentication is now enabled by default (previously RND tokens were default)
-
Centralized Configuration: New
jwtConfigProvidercomponent consolidates JWT settings (key, algorithm, issuer, audience) into a single source of truth, eliminating duplication acrossjwtAuthenticationMechanismandjwtTokenManager -
Secure Key Generation: JWT key defaults changed from
"secret"tonull, enabling automatic secure random key generation -
Cookie Handling Enabled:
authCookieHandlerandauthCookieSetterare now enabled by default -
Cookie TTL Unit Change: Cookie TTL configuration unit changed from seconds (
expires-ttl) to minutes -
Token Endpoint Change: Token service URI changed from
/tokensto/token -
Action: Review and update JWT configuration if you have custom settings
-
Action: If you prefer RND tokens, explicitly disable JWT and enable RND in your configuration
-
Action: Update any references from
/tokensto/tokenendpoint
-
-
Map-Reduce Pipelines Removed
-
Action: Migrate all map-reduce aggregations to standard aggregation pipelines before upgrading
-
-
Docker Image Changes
-
Action: Update Docker references from
softinstigate/restheart(OpenJDK) to the GraalVM-based image -
Note: The
latesttag now points to the GraalVM image
-
-
GraphQL App Descriptor Structure
-
Action: Remove
descriptor.namefield from GraphQL app definition documents -
Action: Ensure
_idfields are strings if not using custom URIs
-
-
Token Authentication Endpoints
-
Action: Update client applications to use
/tokenor/token/cookieendpoints -
Action: Remove
?set-auth-cookieand similar query parameters from API calls
-
-
CORS Headers Optimization
-
Exposed Headers Changed: Default
Access-Control-Expose-Headersis now empty (previously includedLocation,ETag,Auth-Token, etc.) -
Action: Services that set response headers like
Location,ETag, or custom headers must implement theaccessControlExposeHeaders()method to explicitly expose them -
Action: Review browser-based applications that rely on reading response headers and ensure services expose the necessary headers
-
Note: This is a critical change for services that return
Locationheaders (e.g., POST operations that create resources)
-
Configuration Updates
JWT Configuration Provider:
RESTHeart v9 introduces a centralized JWT configuration provider that eliminates duplication. JWT settings are now defined once and shared across all JWT-related components:
jwtConfigProvider:
key: null # null enables secure random key generation
algorithm: HS256
issuer: restheart
audience: restheart
Default JWT Authentication:
JWT authentication is now enabled by default with secure defaults. If you prefer RND tokens, update your configuration:
# Disable JWT authentication and token manager
jwtAuthenticationMechanism:
enabled: false
jwtTokenManager:
enabled: false
# Enable RND token manager
rndTokenManager:
enabled: true
Cookie Handling:
Cookie handlers are now enabled by default. Note the TTL unit has changed from seconds to minutes:
authCookieHandler:
enabled: true
authCookieSetter:
enabled: true
ttl: 15 # minutes (previously was expires-ttl in seconds)
Token Endpoint:
The token service endpoint has changed from /tokens to /token:
authTokenService:
uri: /token # changed from /tokens
Aggregation Security:
Review and configure aggregation security settings based on your security requirements:
mongo:
aggregationSecurity:
blacklistedStages:
- $out
- $merge
allowJavaScriptExecution: false
MongoRealmAuthenticator:
If using attached parameters, add the attached-props configuration:
mongoRealmAuthenticator:
attached-props:
- tenantId
- customAttribute
Migration Steps
-
Backup your configuration and data
-
Update to Java 25 LTS
-
Review and update configuration file following the examples above
-
Migrate map-reduce pipelines to aggregation pipelines
-
Update GraphQL app definition documents
-
Update client applications to use new
/tokenendpoints -
Test in a staging environment before production deployment
-
Update Docker images if using containerized deployments
-
Monitor logs using the enhanced logging features to verify correct operation
Testing the Upgrade
After upgrading, verify the following:
-
Authentication - Test login via
/tokenendpoint -
Authorization - Verify ACL rules work as expected
-
Aggregation Pipelines - Ensure aggregations execute correctly with security controls
-
GraphQL - Test GraphQL endpoints with updated app descriptors
-
Custom Plugins - Verify custom plugins work with new logging and metrics features
-
Docker Deployment - Confirm correct image is being used and startup is successful
Getting Help
If you encounter issues during the upgrade:
-
Check the RESTHeart v9 Documentation
-
Review the v9.0.0 Milestone Issues
-
Ask questions on the GitHub Discussions
-
Report bugs on GitHub Issues