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

Facet uses the Pebble template engine for server-side rendering. Pebble is a Java templating engine inspired by Twig and the Python Jinja syntax. It supports template inheritance, an easy-to-read syntax, built-in autoescaping for security, and integrated internationalization support.

{% 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 collection
  • page, pagesize, totalPages - Pagination info
  • database, 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 HTMX
  • hxTarget - 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).

Next Steps

📚 Read the Docs

Explore comprehensive guides, API references, and best practices.

View Documentation →

💻 Explore Examples

See real-world applications and templates in action.

Examples coming soon

🏗️ Learn RESTHeart

Understand the powerful API server that powers Facet.

RESTHeart Docs →