Introduction
Shamar is an admin panel for AdonisJS. You describe a back office in TypeScript — which models people can manage, which fields appear on a form, which columns appear in a list — and Shamar serves the screens, the routes, and the database calls.
You do not draw the list page, the create form, or the edit form yourself for every model. You declare a resource. The panel turns that declaration into the pages your staff actually use.
What an admin panel is doing
Section titled “What an admin panel is doing”An admin panel is the private part of an application. Customers use the public site. Staff use the panel to create products, edit orders, upload files, and change settings. Those screens repeat: a table of records, a form to create one, a form to edit one, a page that shows one record, and buttons that do something to a record.
Shamar owns that repetition. Your application still owns the database models, the authentication (who is signed in), and the business rules that are unique to you.
What you write
Section titled “What you write”A resource is a class. It points at a model and answers a few questions:
- What is this thing called, and where does it live in the sidebar?
- Which inputs appear when someone creates or edits a record?
- Which columns appear in the list, and which of them can be searched or sorted?
- Who is allowed to see or change it?
import { Resource, TextInput, TextColumn, type FormBuilder, type TableBuilder } from '@shamar/core'import Product from '#models/product'
export default class ProductResource extends Resource { static model = Product static slug = 'products' static label = 'Products'
static form(form: FormBuilder) { return form.schema([TextInput.make('name').required()]) }
static table(table: TableBuilder) { return table.schema([TextColumn.make('name').searchable()]) }}That class does not contain HTML. TextInput and TextColumn are descriptions. The Adonis package reads them and renders the actual page.
What Shamar draws for you
Section titled “What Shamar draws for you”After the panel is installed and the person is signed in, a resource registered at slug products gets routes under the panel path (often /admin):
| Page | What the person sees |
|---|---|
| List | A table of records, with search, filters, sorting, and pagination |
| Create | An empty form built from form() |
| Edit | The same form, filled with one record |
| Show | A read-only view of one record |
Around those pages the panel draws the chrome that is the same everywhere: a sidebar, a top bar, a login screen, breadcrumbs, and flash messages. Branding (name, color, logo) is configuration, not a separate front-end project.
How a request moves through the stack
Section titled “How a request moves through the stack”Shamar is split into packages so the description of the UI is not tied to one database.
@shamar/coreis the language you write resources in. It does not open a database connection and it does not speak HTTP.- An adapter reads and writes records.
@shamar/lucidtalks to SQL through Adonis Lucid.@shamar/mongoosetalks to MongoDB through Mongoose. You pick one when you configure the panel. A resource class looks the same either way;static modelis a Lucid model or a Mongoose model. @shamar/adonisis the host. It registers routes, renders Edge templates, and calls the adapter when a list or a form is submitted.
A list request looks like this: the browser asks for /admin/products. The host finds ProductResource, asks the adapter for a page of records, uses table() to decide the columns, and sends back HTML. A save request validates the fields declared on form(), then asks the adapter to create or update the model.
Nothing in that flow builds a separate single-page application by hand. Interactive pieces — search, notifications, counters, and the islands the panel adds over time — sit on Wire. Wire keeps the component’s state on the server and morphs HTML in the browser. Alpine.js still handles tiny client-only details, such as opening a menu that never needs a round trip. The panel, the resources, and the forms are the product built on top of that runtime. Read Wire next. The rest of these docs are what Shamar builds with it.
Panels
Section titled “Panels”A panel is one mounted admin area: its own URL prefix, its own resources, and its own branding. Each panel is a class in app/panels/{id}/panel.ts. The Shamar provider discovers those classes on boot. config/shamar.ts stays the place for the database, sign-in, and branding shared by every panel. node ace make:panel billing adds another panel. The playground uses this so /demo and /app are two classes, not two blocks in the config file.
What is optional
Section titled “What is optional”The panel works with core, one database adapter, and the Adonis host. Wire is the runtime underneath, not an add-on you bolt on later. These packages add capabilities you can leave out until you need them:
| Package | What it adds |
|---|---|
@shamar/cherubim |
Roles, abilities, and API keys — who may do what, after you already know who is signed in |
@shamar/rest |
A JSON API for the same resources, plus an OpenAPI page |
| Media | A file manager and file fields, stored with the same database you already chose |
Those are documented under Packages.
What comes next
Section titled “What comes next”Wire is the next chapter: how a component’s state lives on the server. Installation then adds the host and chooses SQL or MongoDB. Your first resource is the shortest path from an empty panel to a working CRUD screen. The live demo is a seeded panel you can click through before you install anything.