DEV Community

Cover image for Facet: The data-driven Web framework
Maurizio Turatti
Maurizio Turatti

Posted on

Facet: The data-driven Web framework

Building Data-Driven Web Apps Without the Backend Complexity

Facet brings Java's production-grade reliability with PHP's rapid development experience. Map MongoDB collections to HTML templates through convention—no controllers, no ORMs, just templates. Hot reload during development, deploy as a single JAR or native binary with sub-100ms startup.

The Problem

Modern web development forces you to choose:

  • Spring Boot? Powerful but complex—controllers, services, repositories, configuration files, dependency injection
  • Laravel/Django/Rails? Fast development but requires careful operational tuning for production scale
  • Modern SPAs? Build separate frontend and backend, manage state, loading states, API orchestration
  • Headless CMS? Pay for features you don't need, locked into proprietary systems

What if you could get Java's performance with PHP's simplicity?

Meet Facet

Facet is a data-driven web framework that transforms MongoDB REST APIs into server-rendered HTML. Convention over configuration means your collections become web pages automatically—no backend code needed.

Hybrid API/UI from the same endpoint:

# JSON for APIs
curl -H "Accept: application/json" http://localhost:8080/shop/products

# HTML for browsers  
curl -H "Accept: text/html" http://localhost:8080/shop/products
Enter fullscreen mode Exit fullscreen mode

The same code, same data, different representation. Templates are opt-in—add HTML rendering only where you need it. Your REST API remains unchanged.

How It Works

1. Convention-Based Template Resolution

Your template structure mirrors your API paths:

Request: GET /shop/products
         Accept: text/html

Facet looks for:
1. templates/shop/products/index.html   ✓ (found!)
2. templates/shop/index.html            (parent fallback)
3. templates/index.html                 (global fallback)
4. No template → return JSON            (API unchanged)
Enter fullscreen mode Exit fullscreen mode

SSR is opt-in per resource. Add templates only where you need HTML.

2. Rich Template Context

Templates automatically receive:

<h1>{{ database }}/{{ collection }}</h1>

{% for item in items %}
  <div class="product">
    <h3>{{ item.data.name }}</h3>
    <p>{{ item.data.description }}</p>
    <span>${{ item.data.price }}</span>
  </div>
{% endfor %}

{# Pagination built-in #}
{% if totalPages > 1 %}
  <nav>
    {% for p in range(1, totalPages + 1) %}
      <a href="?page={{ p }}">{{ p }}</a>
    {% endfor %}
  </nav>
{% endif %}
Enter fullscreen mode Exit fullscreen mode

Context includes: pagination, authentication, permissions, MongoDB query parameters, and more.

3. First-Class HTMX Support

Facet automatically detects HTMX requests and routes to fragment templates:

Full page template:

{% extends "layout" %}

{% block main %}
  <div id="product-list">
    {% include "_fragments/product-list" %}
  </div>

  <form hx-get="/shop/products"
        hx-target="#product-list"
        hx-indicator="#loading">
    <input name="filter" placeholder="Search...">
    <button type="submit">Search</button>
  </form>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

Fragment template (templates/_fragments/product-list.html):

{% for item in items %}
  <article>
    <h3>{{ item.data.name }}</h3>
    <p>{{ item.data.description }}</p>
    <span>${{ item.data.price }}</span>
  </article>
{% endfor %}
Enter fullscreen mode Exit fullscreen mode

When HTMX sends HX-Target: #product-list, Facet automatically renders just the fragment—no backend code needed.

Quick Start

1. Start Facet and MongoDB

curl -O https://getfacet.org/docker-compose.yml
docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

2. Create Database and Add Data

# Create collection
curl -X PUT http://localhost:8080/shop/products

# Insert products
curl -X POST http://localhost:8080/shop/products \
  -H "Content-Type: application/json" \
  -d '[
    {"name": "Laptop", "price": 999},
    {"name": "Mouse", "price": 29}
  ]'
Enter fullscreen mode Exit fullscreen mode

3. Create Template

mkdir -p templates/shop/products
Enter fullscreen mode Exit fullscreen mode

templates/shop/products/index.html:

<!DOCTYPE html>
<html>
<head>
  <title>Products</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
  <script src="https://unpkg.com/htmx.org@2.0.8"></script>
</head>
<body>
  <main class="container">
    <h1>Products</h1>

    <div id="product-list">
      {% for item in items %}
        <article>
          <h3>{{ item.data.name }}</h3>
          <span>${{ item.data.price }}</span>
        </article>
      {% endfor %}
    </div>
  </main>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

4. Visit in Browser

open http://localhost:8080/shop/products
Enter fullscreen mode Exit fullscreen mode

✨ That's it! You have a working web application with zero backend code.

Real-World Example

The product catalog example demonstrates a complete application with:

  • ✅ MongoDB data binding and display
  • ✅ Search and filtering with MongoDB queries
  • ✅ Pagination with preserved query parameters
  • ✅ HTMX partial updates for dynamic search
  • ✅ Authentication & role-based access control
  • ✅ Full CRUD operations (admin only)

Try it yourself:

git clone https://github.com/SoftInstigate/facet.git
cd facet/examples/product-catalog
docker-compose up
Enter fullscreen mode Exit fullscreen mode

Visit http://localhost:8080/shop/products

Login credentials: admin / secret

The example includes hot-reload—edit templates and see changes instantly without restart.

Why This Approach?

For Simple Projects

  • No backend coding - Templates directly render API data
  • Convention over configuration - No route wiring, templates map to URLs automatically
  • Hot reload - Edit templates, refresh browser, see changes instantly (no build step, no webpack, no npm scripts)
  • Progressive enhancement - Start with JSON API, add HTML where needed

For Complex Projects

  • Flexible layouts - Different layouts per application area
  • Framework agnostic - Use any CSS/JS framework (React, Alpine.js, Tailwind, etc.)
  • HTMX native - Build SPA-like experiences with server-side rendering
  • SEO friendly - Server-rendered HTML from the start
  • Extensible - Add custom logic via RESTHeart plugins (Java or JavaScript via GraalVM)

For Teams

  • Separation of concerns - Web devs work on templates, backend devs on API
  • No deployment coupling - Templates update without backend changes
  • Gradual adoption - Add HTML to existing APIs incrementally

Architecture Benefits

Facet leverages RESTHeart's production-grade features:

  • MongoDB REST API with full query support
  • Authentication (JWT, Basic Auth, OAuth, LDAP)
  • Authorization with fine-grained ACL
  • WebSocket and Change Streams for real-time data
  • GraphQL support (coming soon to Facet templates)
  • Plugins for custom business logic

You get enterprise-grade infrastructure without building it yourself.

When to Use Facet

Great fit:

  • ✅ Content-driven applications (blogs, catalogs, dashboards)
  • ✅ Internal tools and admin panels
  • ✅ MongoDB-backed applications
  • ✅ Projects prioritizing SEO and fast initial load
  • ✅ Teams wanting to avoid frontend/backend coordination

Not ideal for:

  • ❌ Highly interactive applications with complex client-side state
  • ❌ Non-MongoDB databases (Facet is MongoDB-specific)
  • ❌ Applications requiring real-time collaboration (though RESTHeart supports WebSockets)

How Does Facet Compare?

vs. Spring Boot and Java MVC Frameworks

Spring Boot is powerful but complex: controllers, services, repositories, configuration files, build tools, and dependency injection.

Facet eliminates this boilerplate:

  • ✅ MongoDB collections automatically become web pages through convention-based templates
  • ✅ No controllers, no ORMs, no XML configuration
  • ✅ Hot reload templates without restart
  • ✅ Get Java's performance without Java's ceremony

Performance: Same JVM benefits (sub-100ms startup with native images, ~50MB memory footprint, handle thousands of concurrent connections)

When to use Spring Boot instead: Complex business logic requiring extensive service layers, non-MongoDB databases, or team already invested in Spring ecosystem

vs. Laravel, Django, Rails

PHP and Python frameworks are great for rapid development, but they require careful operational tuning for production scale.

Facet gives you the same rapid development experience:

  • ✅ Convention over configuration
  • ✅ Hot reload during development
  • ✅ Minimal boilerplate

But runs on the battle-tested JVM:

  • ✅ Deploy with confidence—no PHP-FPM tuning, no WSGI complexity
  • ✅ Sub-100ms startup with native images
  • ✅ ~50MB memory footprint
  • ✅ Handle thousands of concurrent connections out of the box

When to use Laravel/Django instead: Team expertise in PHP/Python, need for framework-specific packages, or using PostgreSQL/MySQL

vs. Modern SPAs (React, Vue, Angular)

SPAs excel at highly interactive applications but add complexity:

Facet simplifies:

  • ✅ Server-rendered HTML by default (SEO-friendly from day one)
  • ✅ No state management, loading states, or API orchestration needed
  • ✅ HTMX for SPA-like interactivity without build tools
  • ✅ Progressive enhancement—add interactivity where needed

When to use SPAs instead: Highly interactive applications with complex client-side state (think Gmail, Figma, or real-time collaboration tools)

vs. Headless CMS (Contentful, Strapi)

Headless CMSs provide content modeling and APIs:

Facet offers:

  • ✅ More flexibility—not limited to content modeling paradigms
  • ✅ Direct MongoDB access with full query power
  • ✅ Open source—run anywhere, no vendor lock-in
  • ✅ No licensing costs

When to use Headless CMS instead: Non-technical content editors need a UI, or you need advanced content workflows and localization

Performance & Deployment

Choose Your Runtime

GraalVM JDK (with JavaScript extensibility):

  • ~1s startup time
  • ~150MB memory footprint
  • Support for JavaScript plugins via GraalVM

Native Image (maximum performance):

  • <100ms startup time
  • ~50MB memory footprint
  • Single binary, no Java installation required

Both configurations handle thousands of concurrent requests efficiently. MongoDB queries stream directly to templates—no ORM overhead.

Production Features

  • Template caching - Compiled templates cached for optimal performance
  • ETag support - Browser caching with 304 Not Modified responses
  • MongoDB indexes - RESTHeart leverages your database indexes
  • Stateless design - Horizontal scaling is trivial
  • Single artifact - Deploy as JAR or native binary
  • 12-factor compliant - Environment-based configuration, health checks built in

Deployment Options

Deploy anywhere:

  • Docker containers
  • Kubernetes clusters
  • AWS, Azure, Google Cloud
  • Bare metal servers

Getting Started

Documentation: https://getfacet.org/docs

GitHub: https://github.com/SoftInstigate/facet

Examples: Product Catalog Tutorial

RESTHeart Docs: https://restheart.org/docs

License

Facet is Apache 2.0 licensed—free for commercial use. Built on RESTHeart (AGPL v3 core, Apache 2.0 plugin interfaces).


Conclusion

Facet brings together the best of two worlds: Java's production-grade reliability and performance with the rapid development experience of PHP or Python frameworks. Convention over configuration, hot reload during development, deploy as a single JAR or native binary.

If you're building data-driven applications and want to skip the backend boilerplate while maintaining production-grade performance, give Facet a try.

Quick start:

curl -O https://getfacet.org/docker-compose.yml
docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

Or explore the product catalog example:

git clone https://github.com/SoftInstigate/facet.git
cd facet/examples/product-catalog
docker-compose up
Enter fullscreen mode Exit fullscreen mode

Top comments (0)