Edit Page

Core Concepts RESTHeart Cloud

Core Concepts

This page explains RESTHeart’s architecture and the key concepts you need to understand.

Prerequisites: Read Introduction to RESTHeart first.

The RESTHeart Runtime

At its core, RESTHeart is a runtime process (restheart-core) that:

  1. Loads Configuration - Reads settings from configuration files or environment variables

  2. Registers Plugins - Discovers and initializes plugins from the plugins/ directory

  3. Routes Requests - Maps incoming HTTP requests to the correct service

  4. Enforces Security - Applies authentication and authorization rules

  5. Executes Interceptors - Runs interceptors at various stages of request processing

  6. Manages Lifecycle - Handles startup, shutdown, and plugin initialization

When you start RESTHeart with java -jar restheart.jar, this runtime process begins and loads everything needed to handle HTTP requests.

Standard Plugins

RESTHeart ships with standard plugins in the plugins/ directory:

plugins/
├── lib/                         # Plugin dependencies
├── restheart-graphql.jar       # GraphQL API
├── restheart-metrics.jar       # Metrics and monitoring
├── restheart-mongoclient-provider.jar  # MongoDB connection
├── restheart-mongodb.jar        # REST API for MongoDB
├── restheart-polyglot.jar       # JavaScript/TypeScript support
└── restheart-security.jar       # Authentication & Authorization

These plugins provide the ready-to-use MongoDB APIs you can use without writing code.

Key point: These are just plugins! RESTHeart treats them the same as custom plugins you develop.

The Four Plugin Types

RESTHeart has four types of plugins, each serving a specific purpose:

1. Services

Purpose: Handle HTTP requests at specific URIs.

Use cases: - REST APIs - GraphQL endpoints - Custom business logic endpoints - File uploads/downloads - WebSocket connections

Example: The MongoDB REST API is implemented as a Service that handles requests to /{db}/{collection}.

Learn more: Developing Services

2. Interceptors

Purpose: Monitor and modify requests/responses as they flow through RESTHeart.

Use cases: - Logging requests - Adding custom headers - Transforming responses - Validating request data - Enforcing business rules

Intercept points: - REQUEST_BEFORE_AUTH - Before authentication - REQUEST_AFTER_AUTH - After authentication, before the service - RESPONSE - After the service, before sending to client - RESPONSE_ASYNC - Asynchronously after sending response

Example: A logging interceptor that records every API call.

3. Providers

Purpose: Supply objects to other plugins via dependency injection.

Use cases: - Database connections - Configuration objects - External API clients - Shared utilities

Example: The mongoclient-provider makes the MongoDB client available to other plugins with @Inject("mclient").

4. Initializers

Purpose: Execute initialization logic at startup.

Use cases: - Database schema setup - Loading reference data - Starting background tasks - Warming up caches

Init points: - BEFORE_STARTUP - Before RESTHeart starts accepting requests - AFTER_STARTUP - After RESTHeart is ready

Example: An initializer that creates default database indexes on startup.

Learn more: Initializers

Request Lifecycle

Understanding how RESTHeart processes requests is key to working with the framework.

The Flow

1. HTTP Request arrives
   ↓
2. REQUEST_BEFORE_AUTH Interceptors run
   ↓
3. Authentication (identify the user)
   ↓
4. Authorization (check permissions)
   ↓
5. REQUEST_AFTER_AUTH Interceptors run
   ↓
6. Service handles the request
   ↓
7. RESPONSE Interceptors run
   ↓
8. HTTP Response sent to client
   ↓
9. RESPONSE_ASYNC Interceptors run (optional)

Understanding the Lifecycle

1. Virtual Threads = Simple Code

Each request runs in its own virtual thread. This means: - Your code is thread-safe by default (no shared state between requests) - No need for async/await or callbacks - Write straightforward, synchronous code - Excellent scalability (millions of concurrent requests)

2. Interceptor Execution Order

When multiple interceptors exist at the same point: - They run in order of priority (lower number = runs first) - Priority 1 runs before priority 10 - Use priorities to control execution sequence

3. Service Routing

RESTHeart matches requests to services by: - URI patterns in @RegisterPlugin(defaultURI = "/myservice") - First matching service handles the request - Use specific patterns to avoid conflicts == Plugin Discovery

RESTHeart automatically discovers plugins at startup:

Java/Kotlin Plugins

  1. RESTHeart scans JAR files in plugins/ directory

  2. Looks for classes annotated with @RegisterPlugin

  3. Instantiates and registers the plugins

Troubleshooting: If your plugin doesn’t load, check: - JAR file is in the plugins/ directory - Class has @RegisterPlugin annotation - No compilation errors in the plugin - Check logs for "Registered plugin" messages on startup

JavaScript Plugins

  1. RESTHeart scans directories in plugins/ for package.json

  2. Loads JavaScript files as plugins

  3. Executes via the polyglot plugin

Troubleshooting: JavaScript plugins require: - GraalVM-based RESTHeart image (not standard image) - Valid package.json with plugin definition - restheart-polyglot.jar in plugins/ directory

Configuration

Plugin Configuration

Each plugin can have configuration in restheart.yml:

ping:
  enabled: true
  secure: false
  uri: /ping
  msg: 'Hello from RESTHeart!'

Special options: - enabled - Enable/disable the plugin - uri - Override default URI (Services only) - secure - Require authentication (Services only)

Plugins access their configuration with:

@Inject("conf")
Map<String, Object> conf;

RESTHeart Configuration

Main configuration file: restheart.yml

Key sections: - http-listener - Port, host, TLS settings - mclient - MongoDB connection string - mongo - MongoDB service configuration - Plugin-specific sections

Override methods:

Configuration Override Methods:

1. Configuration File

$ java -jar restheart.jar custom-config.yml

2. Environment Variable (RHO)

Override specific settings without editing files:

$ RHO='/http-listener/port->9090' java -jar restheart.jar

RHO Syntax:

RHO='/path/to/setting->value'
  • Path uses forward slashes: /section/subsection/key

  • Arrow separates path from value

  • Multiple overrides: /setting1→value1;/setting2→value2

Examples:

# Change port
$ RHO='/http-listener/port->9090'

# Multiple overrides
$ RHO='/http-listener/port->9090;/mclient/connection-string->"mongodb://localhost"'

# Boolean value
$ RHO='/logging/log-to-console->false'

3. Override File

$ java -jar restheart.jar -o overrides.yml

Learn more: Configuration Guide

Dependency Injection

What is Dependency Injection?

Instead of creating objects yourself, RESTHeart provides (injects) them into your plugin. This gives you access to RESTHeart’s built-in services and configuration.

How it works: 1. Add @Inject("name") to a field in your plugin 2. RESTHeart automatically provides the object when your plugin loads 3. Use the injected object in your plugin code

Why use it: - Access MongoDB client without managing connections - Read configuration without parsing files - Use RESTHeart services without manual setup

RESTHeart provides dependency injection through the @Inject annotation.

Built-in Providers

@Inject("conf")
Map<String, Object> conf;  // Plugin's configuration

@Inject("rh-config")
Configuration rhConfig;  // RESTHeart's global configuration

@Inject("mclient")
MongoClient mclient;  // MongoDB client

@Inject("registry")
PluginsRegistry registry;  // Access other plugins

@Inject("acl-registry")
ACLRegistry aclRegistry;  // Programmatic ACL management

Custom Providers

You can create your own providers to supply custom objects:

@RegisterPlugin(name = "myProvider", description = "Custom provider")
public class MyProvider implements Provider<MyService> {
    @Override
    public MyService get(PluginRecord<?> caller) {
        return new MyService();
    }
}

Then inject it:

@Inject("myProvider")
MyService myService;

Security Architecture

RESTHeart’s security has three layers:

1. Authentication Mechanisms

Extract credentials from requests (Basic Auth, JWT, tokens, etc.)

2. Authenticators

Verify credentials and build the user account (check username/password, validate JWT, etc.)

3. Authorizers

Check if the authenticated user has permission to perform the request (ACLs, role-based, etc.)

Security plugins let you customize any of these layers.

Virtual Threads

RESTHeart uses Java 21’s virtual threads for superior performance:

Benefits: - Lightweight - Millions of virtual threads vs thousands of platform threads - Simple code - No async/await complexity - Better performance - Efficient resource utilization - Scalability - Handle massive concurrency

For developers: Write synchronous code that looks simple but scales massively.

// This looks synchronous but doesn't block platform threads
var data = mongoClient.find("db", "collection");
response.setContent(data);

Polyglot Support

RESTHeart supports multiple languages for plugin development:

Java

The native language, best performance:

@RegisterPlugin(name = "myService")
public class MyService implements JsonService {
    public void handle(JsonRequest req, JsonResponse res) {
        res.setContent(object().put("message", "Hello"));
    }
}

Kotlin

Concise JVM language with full interop:

@RegisterPlugin(name = "myService")
class MyService : JsonService {
    override fun handle(req: JsonRequest, res: JsonResponse) {
        res.setContent(Json.obj().put("message", "Hello"))
    }
}

JavaScript/TypeScript

Familiar syntax for web developers:

export const options = { name: "myService" }

export function handle(request, response) {
    response.setContent(JSON.stringify({ message: "Hello" }));
    response.setContentTypeAsJson();
}

Important differences from Node.js: - Runs on virtual threads (not Node’s event loop) - No need for async/await for I/O operations - Synchronous-style code that doesn’t block - No Node.js stdlib - use Java libraries instead

When to use JavaScript plugins: - Rapid prototyping - Simple request/response transformations - Leveraging JavaScript string/JSON manipulation - Teams with JavaScript expertise

When to use Java instead: - Performance-critical code - Complex business logic - Access to Java ecosystem libraries Important: JavaScript plugins run on virtual threads too - no async/await needed!

Performance Characteristics

Startup Time: ~100ms (with standard plugins) Memory Footprint: ~50MB heap (configurable) Request Latency: Sub-millisecond for simple operations Throughput: Hundreds of thousands of requests per second GraalVM Native: ~40MB binary, instant startup (<10ms)

Learn more: Performance Guide

What’s Next?

Now that you understand RESTHeart’s architecture:

Continue Foundations

Start Building

Explore Topics