Decentralized server-side UI composition β no gateway, no central controller.
View on GitHub
Ableron is a library for decentralized server-side UI composition. Unlike traditional solutions like nginx SSI
, thereβs no central component. Each service is capable of composing its own HTML using <ableron-include>
tags, resolved internally. This is achieved by installing Ableron as a dependency in your service.
TL;DR: Ableron is a decentralized, efficient solution for dynamic, server-side UI composition with local caching, easy configuration, and great developer experience.
No need to route all traffic through a centralized infrastructure component for UI composition. Services stay independent and scalable.
Since includes are resolved directly within your service, local development works out of the box β without needing to run complex infrastructure setups.
Configure per-include settings like timeouts, fallback fragments, or static fallback content with minimal effort β no central coordination required.
All fragments can be cached in memory directly inside your service, based on their individual cache policy. This reduces HTTP overhead and latency β saving both time and money.
Feature | Ableron | nginx SSI |
---|---|---|
Centralized vs Distributed | Decentralized (each service composes its own UI) | Centralized (nginx manages UI composition) |
Developer Experience | Easy local development with no extra infrastructure | Requires complex infrastructure setup for local development |
Scalability | Scalable by design, no bottlenecks | Potential bottleneck at the centralized server |
Caching | In-memory fragment caching per service | Caching typically done at the centralized infrastructure level |
Complexity | Low complexity (library-based, no additional infrastructure) | Higher complexity with nginx or similar infrastructure |
Ableron provides framework integrations for Spring Boot 2, Spring Boot 3, express and fastify as well as low level implementations for Java and JavaScript.
Minimal usage example with express:
import express from 'express';
import ableron from '@ableron/express';
const app = express();
app.use(ableron());
app.get('/', function(req, res) {
res.send('<ableron-include src="http://user-service/profile"/>');
});
app.listen(port);
More examples and documentation available in the GitHub repository.
The <ableron-include>
tag is used to include fragments from external URLs into your page. It comes with various attributes and fallback options.
The <ableron-include>
tag must be properly closed. You can either use the self-closing syntax <ableron-include ... />
or explicitly close the tag with <ableron-include ...></ableron-include>
.
Content placed between <ableron-include>
and </ableron-include>
is used as fallback content in case the fragment cannot be loaded due to request timeout or erroneous response.
2000
or 2000ms
) or seconds (e.g., 2s
). Defaults to the global request timeout.
src
fails to load.
fallback-src
URL. The value can be specified similarly to src-timeout
.
When resolving the fragment content, the precedence is as follows: src β fallback-src β fallback content. This ensures a robust fallback mechanism in case of failures.
Here's an example of how you can use the <ableron-include>
tag to include external fragments into your service response. This example demonstrates the use of several important attributes, such as src
, fallback-src
, headers
, cookies
, and fallback content.
<ableron-include
id="header"
src="https://header-service/api/fragments/header"
src-timeout="2s"
fallback-src="https://cdn-proxy/static/fallback-header"
headers="x-ab-test-groups,x-country"
cookies="device,consent">
<header>Static fallback header</header>
</ableron-include>
In this example:
src
and fallback-src
both fail, the content between the opening and closing <ableron-include>
tags will be used as a static fallback. In this case, itβs a simple static <header>
element.
Ableron supports marking an <ableron-include>
as primary. Primary includes are considered to be the main part of the finally composed page and thus define the HTTP response status and influence response headers.
src
returns success status, this status code is set as response code for the entire page.
src
returns error status, fallback-src
is defined and returns success status, this status code is set as response code for the entire page.
src
and fallback-src
both return error status, the status code returned by src
is set as response code for the entire page and the fragment content is set to the body returned by src
. Fallback content is ignored.
Ableron supports automatic cache refreshing for included fragments β a feature designed to keep response times low and cache hit rates high.
To provide insights into what is happening during page composition, Ableron provides the opportunity to add composition statistics to the finally composed page via HTML comment. This feature is disabled by default, as you may not want to expose those statistics in your production environment.
<!-- Processed 4 includes in 93ms
Time | Include | Resolved With | Fragment Cacheability | Fragment URL
------------------------------------------------------
0ms | header | fallback content | - | -
56ms | main | remote src | not cacheable | https://content-service/main
93ms | footer | remote src | expires in 600s | https://content-service/footer
0ms | nav | cached fallback-src | expires in 10s | https://content-service/nav
Cache: 3 items, 1 hits, 3 misses, 0 successful refreshs, 0 failed refreshs
-->
Fragments will be cached, if their cache policy allows caching and if the response code is one of 200, 203, 204, 206, 300, 404, 405, 410, 414, 501
.
In order to not introduce unwanted latency, redirects returned by src
or fallback-src
URLs will not be followed. Instead, the request is considered failed.
As each fragment brings its own cache policy, Ableron will adjust the cache policy of the finally composed page accordingly. The fragment with the lowest TTL defines the max-age
of the page, in case max age of the page is not below it.
All contributions are greatly appreciated. To contribute, either open an issue or fork the repository and create a pull request:
git checkout -b feature/amazing-feature
git commit -m 'Added some amazing feature'
git push origin feature/amazing-feature