Skip to main content

Quickstart

A working admin for one resource — backend and frontend — in about ten minutes.

The example is an e-shop product catalogue, the domain used throughout this documentation. Pick your backend in the tabs; the frontend part differs only in the URL and resource names the chosen backend serves.

1. Install the backend library​

pip install habarico
# settings.py
INSTALLED_APPS = [
...,
"habarico",
"rest_framework", # must come after habarico
...
]
# urls.py
from django.urls import include, path
from habarico import site

urlpatterns = [
path("api/v1/eshop/admin/", include(site.urls)),
]

See Configuration for permissions and authentication.

2. Declare one resource​

# models.py
from django.db import models
from habarico.models import MoneyField


class Category(models.Model):
name = models.CharField(max_length=100)

def __str__(self):
return self.name


class Product(models.Model):
class Status(models.TextChoices):
DRAFT = "draft", "Draft"
PUBLISHED = "published", "Published"

name = models.CharField(max_length=200)
description = models.TextField(blank=True)
status = models.CharField(max_length=16, choices=Status.choices, default=Status.DRAFT)
category = models.ForeignKey(Category, on_delete=models.PROTECT, related_name="products")
price = MoneyField(currency="CZK")
stock = models.PositiveIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return self.name
# admin.py
from habarico import ModelAdmin

from .models import Category, Product


class CategoryAdmin(ModelAdmin):
model = Category
can_create = True
can_update = True
fields = ["name"]
search_fields = ["name"]


class ProductAdmin(ModelAdmin):
model = Product
can_create = True
can_update = True
fields = ["name", "description", "status", "category", "price", "stock", "created_at"]
read_only_fields = ["created_at"]
search_fields = ["name", "description"]
filter_fields = ["status", "category", "price"]
ordering_fields = ["name", "price", "created_at"]

can_read is on by default; can_create and can_update are not — without them the schema contains no create or edit view.

That is the whole backend. The schema endpoint now describes a list, a detail, a create form and an edit form — check it with curl before moving on:

curl -H "Authorization: Bearer $TOKEN" http://localhost:8000/api/v1/eshop/admin/schema/

3. Mount the frontend​

npm install habarico
// App.tsx
import {SchemaAdmin, SchemaResource} from 'habarico';

const SERVICES = {
eshop: {
url: 'http://localhost:8000/api/v1/eshop/admin',
label: 'E-shop',
},
} as const;

const App = () => (
<SchemaAdmin serviceConfigMap={SERVICES} authProvider={authProvider} columnSelector>
<SchemaResource name="eshop/product" />
<SchemaResource name="eshop/category" />
</SchemaAdmin>
);

The two variants differ in two places. The service url points at wherever the backend from step 1 is mounted — :8000 is Django's development default, :3000 is Rails'. And the resource name after the eshop/ service prefix is the name the backend emits in its schema: Python names a resource after its model, singular and lowercase (product), while Ruby names it after the route drawn from the config, so it stays plural (products).

The data and schema providers are not passed in: SchemaAdmin builds them itself from authProvider.getAuthHeaders, which is where the token comes from. Replace one only for a backend that does not speak the Habarico REST contract, and then pass the factory rather than its result — dataProviderFactory={getDataProviderFactory}, schemaProviderFactory={getSchemaProvider} — because SchemaAdmin calls it once the schemas and the identity are known. See DataProvider and SchemaProvider.

authProvider is the one piece that depends on how you authenticate. buildServiceAuthProvider builds one from the individual auth operations (login, logout, password reset, MFA, …) — against @eluvia/auth that's its hooks passed straight through; any other library, or hand-rolled auth state, works the same way. Implementing the ServiceAuthProvider interface directly is always an option too; see Auth provider.

What you get​

Opening the app now shows a menu with Products and Categories, and for each:

ScreenBuilt from
List with filters, sorting and searchfilter_fields, ordering_fields, search_fields — in the Ruby DSL filters, sorting_fields, search_fields
Detailthe show view schema
Create and edit formsfields minus the read-only ones
A status chip, a category link, a currency-formatted priceeach field's type + format

Nothing above configured a single component: the frontend chose them from the schema, which is what How it works describes in detail.

Where to go next​

You want toGuide
Lay the form out in tabs or a gridForms and layout
Add an operation that is not CRUDActions and dialogs
Accept file uploadsFile uploads
Render one field your own wayCustom field type
Restrict what a user may seePermissions and visibility