Getting Started with Facet
Start building data-driven web interfaces in minutes
Quick Start
1. Run Facet with MongoDB
# Download docker-compose.yml
curl -O https://getfacet.org/docker-compose.yml
# Start Facet and MongoDB
docker-compose up -d
This starts Facet on port 8080 and MongoDB on port 27017. Facet connects to MongoDB automatically.
2. Create Database and Collection
# Create database
curl -X PUT http://localhost:8080/mydb
# Create collection
curl -X PUT http://localhost:8080/mydb/products
3. Add Sample Data
# Insert sample products
curl -X POST http://localhost:8080/mydb/products \
-H "Content-Type: application/json" \
-d '[
{"name": "Laptop", "price": 999, "description": "High-performance laptop"},
{"name": "Mouse", "price": 29, "description": "Wireless mouse"},
{"name": "Keyboard", "price": 79, "description": "Mechanical keyboard"}
]'
4. Create Template Directory
mkdir -p templates/mydb/products
Templates follow your API structure. The path templates/mydb/products/ maps to /mydb/products endpoint.
5. Add Your First Template
{% extends "layout" %}
{% block content %}
<div class="container">
<h1>Products</h1>
{% if documents %}
<div class="products-grid">
{% for product in documents %}
<div class="product-card">
<h2>{{ product.name }}</h2>
<p>{{ product.description }}</p>
<span class="price">${{ product.price }}</span>
</div>
{% endfor %}
</div>
{% else %}
<p>No products found.</p>
{% endif %}
{# Pagination automatically available #}
{% if totalPages > 1 %}
<nav class="pagination">
{% for p in range(1, totalPages + 1) %}
<a href="?page={{ p }}">{{ p }}</a>
{% endfor %}
</nav>
{% endif %}
</div>
{% endblock %}
6. Visit in Browser
# Open in browser or use curl
curl http://localhost:8080/mydb/products
✨ HTML is rendered! The same endpoint still serves JSON when you use Accept: application/json.
How Template Resolution Works
Facet uses path-based template resolution with hierarchical fallback:
GET /mydb/products
Accept: text/html
Search order:
1. templates/mydb/products/index.html ✓ (exact match)
2. templates/mydb/index.html (parent fallback)
3. templates/index.html (global fallback)
4. No template → return JSON
SSR is opt-in per resource: If no template exists, RESTHeart returns JSON normally. Add templates only where you need HTML.
Native HTMX Support
Facet automatically detects HTMX requests and returns fragments instead of full pages. No backend code needed—the framework reads HTMX headers and renders accordingly.
Automatic Fragment Detection
When HTMX sends the HX-Request header, Facet sets isHtmxRequest to true in your template context.
Target-Aware Rendering
The HX-Target header is available as hxTarget in templates. Facet automatically tries to render fragment templates for targeted updates.
Convention-Based Fragments
Place fragment templates alongside your main templates. For example: index.html and index-productList.html for the #productList target.
Example: Dynamic Product List
Full Page Template
{# templates/mydb/products/index.html #}
{% extends "layout" %}
{% block content %}
<div id="product-list">
{% include "_fragments/product-list" %}
</div>
<button hx-get="/mydb/products?category=electronics"
hx-target="#product-list">
Load Electronics
</button>
{% endblock %}
Fragment Template
{# templates/_fragments/product-list.html #}
{# No extends - just the fragment #}
{% for product in documents %}
<div class="product">
<h3>{{ product.name }}</h3>
<p>{{ product.description }}</p>
<span>${{ product.price }}</span>
</div>
{% endfor %}
The pattern: Full page template includes the fragment. HTMX requests target the fragment directly.
Facet routes HTMX requests with HX-Target: #product-list to _fragments/product-list.html.
Template Context Variables
Templates have access to rich context about the request and data:
MongoDB Data
documents- Array of documents from the collectionpage,pagesize,totalPages- Pagination infodatabase,collection- Resource names
Authentication
username- Authenticated user (null if not logged in)roles- User's roles array
HTMX Integration
isHtmxRequest- True when request came from HTMXhxTarget- Target element selector (when HX-Target header is set)
Fragment Template Convention
When hxTarget is set, Facet looks for fragment templates in the _fragments/ directory:
# Directory structure
templates/
├── mydb/
│ └── products/
│ ├── index.html # Full page
│ └── _fragments/
│ └── product-list.html # Fragment for #product-list target
# HTMX request with HX-Target: #product-list
# → Renders templates/mydb/products/_fragments/product-list.html
Fallback: If resource-specific fragment doesn't exist, Facet looks for
templates/_fragments/product-list.html (global fragments).