AP
AdminPanelBuilder Laravel Admin + API Generator
Quickstart

AdminPanelBuilder — Developer Documentation

AdminPanelBuilder is a JSON-driven generator that produces a Laravel admin panel foundation (Admin Auth, User Auth, Role/Permission management, Landing page) and your business modules (CRUD + API + Admin UI). Developers define what to build using a schema; the system generates how to build it.

Admin + User Authentication Roles & Permissions CRUD + Validation Scoped routes (admin/user/public) Compose (multi-table) SEO/Media auto sections

Quickstart

Fastest successful workflow

Start with 1–2 modules, verify output, then expand schema. Keep schema in version control — it becomes both the blueprint and the team’s contract.

1) Write your schema

{
  "config": { "preset": "full" },
  "modules": {
    "brand": {
      "label": "Brand",
      "is_modal": true,
      "fields": {
        "name":   { "type": "text", "db": "string", "required": true },
        "status": { "type": "radio", "options": ["active","inactive"], "db": "string", "required": true }
      },
      "api": { "public": { "endpoints": ["index","show"] } }
    }
  }
}

2) Generate & download zip

  • Submit schema through your generator UI/endpoint.
  • Download zip and unzip locally.

3) Install & run

composer install
cp .env.example .env
php artisan key:generate
php artisan migrate
php artisan serve
What you get immediately

Admin auth area, user auth area, role/permission pages, a basic landing page, and your generated modules.

Generated Foundation (Built-in)

The generated project includes a stable foundation so teams don’t waste time rebuilding common admin features. Documenting these is important because developers must know what already exists.

FeatureWhat it providesWhy it matters
Admin authentication Admin login + admin-protected routes Separates admin access from public/user
User authentication User login/registration for user-scoped features Enables user APIs safely (ownership rules)
Role management Roles + permissions + assign access to modules/actions Prevents “everyone is super admin”
Landing page Simple public entry page (replaceable) Gives a clean default public route
Scopes admin/user/public route exposure per module Same module can be private/admin-only or partially public
Scopes & auth mapping

Admin scope uses admin authentication; user scope uses user authentication; public scope requires no auth.

Schema Overview

The schema has two top-level keys: config (global settings) and modules (entities).

{
  "config": { ... },
  "modules": {
    "module_key": { ... },
    "another_module": { ... }
  }
}
Defaults are intentional

Missing keys usually do not break generation. The system applies safe defaults so new developers can move fast.

Legacy compatibility

Older schema styles may be accepted, such as: api.endpoints or web.endpoints. The adapter typically normalizes them to api.admin.endpoints and web.admin.endpoints.

config Reference

config.preset

ValueMeaningUse-case
fullGenerate DB + Models + API + Admin UI (+ extras)Most projects
api_onlyDB + Models + APIMobile/API-first
admin_onlyDB + Models + Admin UIInternal admin tools
db_onlyMigrations + Models onlyFoundation first
If missing: defaults to full.

config.generate (global feature switches)

Optional override flags. Use when you need fine control beyond preset.

FlagEffect
migrationGenerate migrations
modelGenerate models (+ casts/relations)
apiGenerate API layer
api_routesWrite API routes
api_controllerGenerate API controllers
viewsGenerate admin blade views
web_routesWrite web/admin routes
web_controllerGenerate web/admin controllers
postmanGenerate Postman collection
sqlGenerate SQL output
Module-level override:

Each module can define generate to override global flags for that module only.

config.pack (zip packaging)

KeyMeaningRecommended
packControls export sizelite for dev, full for deployment
include_envInclude .env inside zipUsually false (dev sets env)
include_vendorInclude vendor/ inside zipUsually false (composer install)

config.sidebar

{
  "config": {
    "sidebar": [
      {
        "group": "Catalog",
        "icon": "layers",
        "items": [
          { "module": "category", "label": "Categories" },
          { "module": "sub_category", "label": "Sub Categories" },
          { "module": "child_category", "label": "Child Categories" }
        ]
      },
      {
        "group": "Products",
        "icon": "box",
        "items": [
          { "module": "brand", "label": "Brands", "icon": "tag" },
          { "module": "product", "label": "Products" }
        ]
      }
    ]
  }
}

config.dashboard

{
  "config": {
    "dashboard": {
      "widgets": [
        { "type": "stats", "modules": ["category","brand","product"] },
        { "type": "recent", "module": "product", "limit": 10 },
        { "type": "status_summary", "module": "brand", "field": "status" },
        { "type": "chart_count_by_day", "module": "product", "days": 14 },
        { "type": "line_chart", "module": "order", "days": 30, "label": "Orders trend" },
        { "type": "bar_chart", "module": "product", "field": "category_id", "limit": 8 },
        { "type": "pie_chart", "module": "order", "field": "status" },
        { "type": "kpi_card", "module": "user", "metric": "count", "compare_days": 7 },
        { "type": "table_recent", "module": "order", "columns": ["id","invoice","status","created_at"], "limit": 8 },
        { "type": "trend_compare", "module": "order", "days": 7 }
      ]
    }
  }
}
Supported widget types
  • stats — quick counts per module
  • recent — recent rows table (auto columns)
  • status_summary — grouped counts by status field
  • chart_count_by_day — activity bars by day
  • line_chart — time-series trend line
  • bar_chart — top values by field
  • pie_chart — distribution by field
  • kpi_card — single metric with optional delta
  • table_recent — custom recent table
  • trend_compare — current vs previous period

modules Reference

KeyTypeWhat it controlsIf missing
labelstringUI name for moduleAuto from module key
is_modalbooleanModal create/edit UXfalse
generateobjectOverride generation flagsUses global
fieldsobjectDB columns + forms + validationEmpty
apiobjectAPI scopes + endpointsDefaults if API enabled
webobjectWeb scopes + endpointsDefaults if Web enabled
composeobjectMulti-table store/updateOff

Module-level generate override example

{
  "config": { "preset": "api_only" },
  "modules": {
    "brand": {
      "generate": { "views": true, "web_routes": true, "web_controller": true },
      "fields": { "name": { "required": true } },
      "web": { "admin": { "endpoints": ["index","create","edit"] } }
    }
  }
}

fields Reference (Types, DB, UI, Validation)

Field = DB + Form + Rules

A field definition drives: migration column type (db), admin input rendering (type), and validation (required, unique, array rules for json fields, etc.).

Supported input type values

typeMeaningCommon keysNotes
textSingle-line inputlabel, required, uniqueDefault type if missing
numberNumeric input UXdb, precision, scaleDB decides integer/decimal
textareaMulti-line inputdb, richRich editor enabled by default
selectDropdownoptions, multipleUse db=json for multiple
checkboxMulti choiceoptions, multipleUsually db=json
radioSingle choiceoptionsGood for status, gender, etc.
fileUploadmimes, is_image, sizeStores path/string usually
imageImage upload aliasmimes, sizeBehaves like file + is_image
relationFK relation pickerrelation.kind/target/displaydb=foreignId recommended

Supported db / column types

dbCommon usageExtra params
stringShort textlength
textLong text
integerCounts, quantity
bigIntegerLarge counters
booleanTrue/false
decimalMoney/priceprecision, scale
floatApprox numbers
doubleApprox numbers
jsonArrays/objects
enumConstrained stringoptions
foreignIdRelationsrelation target
dateDate only
datetimeDate + time
timestampUnix style

Field helper & UI keys (IMPORTANT)

These keys improve developer experience and UI without custom blade edits:

KeyMeaningNotes
labelOverride human labelDefault label derived from field key
help / hint / description / noteHelp text below inputMultiple aliases supported
attributesExtra HTML attributesplaceholder, readonly, min, max, step, etc.
full_widthForce UI full row widthUseful for long text or editors
requiredValidation requiredDefault false
uniqueValidation uniqueUse for SKU, slugs, etc.

Textarea rich editor behavior

Default: Rich editor ON

type: "textarea" typically renders as a WYSIWYG editor (e.g., Summernote). To render a plain textarea, set rich: false.

{
  "fields": {
    "description": { "type":"textarea", "db":"text" },
    "short_note":  { "type":"textarea", "db":"text", "rich": false }
  }
}

Multiple select / checkbox (store as JSON array)

Best practice

If multiple: true, use db: "json" so the model can cast to array and validation can treat it as an array.

{
  "fields": {
    "tags": {
      "type": "select",
      "options": ["new","featured","sale"],
      "multiple": true,
      "db": "json"
    }
  }
}

Status field special behavior

If the field key is status, the system may generate a dedicated status action/UI. The display mode depends on data type and option count:

Status definitionBehavior
db: "boolean" or numeric boolean-likeToggle mode
db: "string" or enum with exactly 2 optionsActive/Inactive style toggle
Other option setsNormal select/radio behavior

Auto UI sections (Basic / SEO / Media)

The admin form is automatically grouped into sections:

  • Basic: normal fields
  • SEO: fields starting with meta_ (example: meta_title, meta_description)
  • Media: file/image fields
{
  "fields": {
    "name": { "type":"text", "db":"string" },
    "meta_title": { "type":"text", "db":"string" },
    "image": { "type":"file", "db":"string", "is_image": true }
  }
}

Relations

Relation field format

{
  "fields": {
    "category_id": {
      "type": "relation",
      "db": "foreignId",
      "required": true,
      "relation": {
        "kind": "belongsTo",
        "target": "category",
        "display": "name"
      }
    }
  }
}

Supported relation kinds

Common supported kinds include: belongsTo hasOne hasMany belongsToMany

relation_between (auto inverse relation)

If relation_between: true is set on a relation field, the generator attempts to create the inverse relation on the target model automatically.

{
  "fields": {
    "category_id": {
      "type": "relation",
      "db": "foreignId",
      "relation": { "kind": "belongsTo", "target": "category", "display":"name" },
      "relation_between": true
    }
  }
}

Scopes & Endpoints (api/web)

Both API and web layers support scopes: admin user public

Allowed endpoints

LayerCommon endpointsNotes
API index, show, store, update, destroy, status, track track is commonly used with token ownership (public tracking).
Web index, create, store, edit, update, destroy, status Admin UI endpoints

Scope configuration example

{
  "api": {
    "admin":  { "endpoints": ["index","show","store","update","destroy","status"] },
    "user":   { "endpoints": ["index","show","store"] },
    "public": { "endpoints": ["index","show"] }
  },
  "web": {
    "admin": { "endpoints": ["index","create","edit","status"] }
  }
}

Middleware per scope (optional)

If your system supports it, you can override middleware per scope:

{
  "api": {
    "admin":  { "endpoints": ["index"], "middleware": ["auth:admin"] },
    "user":   { "endpoints": ["index"], "middleware": ["auth:sanctum"] },
    "public": { "endpoints": ["index"], "middleware": [] }
  }
}

Ownership (auth mode & token mode)

Why ownership exists

Ownership prevents user A from reading/updating user B’s records in user-scoped endpoints.

Ownership: auth mode

{
  "api": {
    "user": {
      "endpoints": ["index","show","store","update","destroy"],
      "ownership": {
        "mode": "auth",
        "field": "user_id",
        "create_owner": true
      }
    }
  }
}
KeyMeaning
mode: "auth"Ownership comes from authenticated user
fieldDB field storing owner id (commonly user_id)
create_ownerAuto-set owner field on create
admin_bypassAdmin can bypass ownership filter (if enabled)

Ownership: token mode (public tracking patterns)

Token ownership allows access based on a token (query param or path param) matching a stored field. Commonly used with a public track endpoint.

{
  "api": {
    "public": {
      "endpoints": ["store","track"],
      "ownership": {
        "mode": "token",
        "token_field": "tracking_token",
        "param": "token",
        "source": "query",
        "admin_bypass": true
      }
    }
  }
}
KeyMeaning
mode: "token"Ownership based on token lookup
token_fieldDB field holding token (e.g. tracking_token)
paramRequest param name (e.g. token)
sourceWhere token is read from (query/path)
admin_bypassAdmin can bypass (optional)

Compose (Multi-table store/update)

One request → parent + children

Compose solves real admin workflows: Product + Images + Variations + Stock in one submit.

Compose keys

KeyMeaning
compose.store.withChild modules to create during parent store
compose.update.withChild modules to update during parent update
compose.payload.parent_keyPayload key where parent object lives
compose.payload.children_keysChild module → request payload key mapping
compose.singleChild module treated as object instead of array

Compose template

{
  "compose": {
    "store":  { "with": ["childA","childB"] },
    "update": { "with": ["childA","childB"] },
    "payload": {
      "parent_key": "parent",
      "children_keys": {
        "childA": "itemsA",
        "childB": "itemsB"
      }
    },
    "single": ["childB"]
  }
}

Full example (Product + images + variations + stock)

{
  "modules": {
    "product": {
      "label": "Product",
      "fields": {
        "name":   { "type":"text", "db":"string", "required": true },
        "status": { "type":"select", "options":["active","inactive"], "db":"string", "required": true }
      },
      "compose": {
        "store":  { "with": ["product_image","product_variation","product_stock"] },
        "update": { "with": ["product_image","product_variation","product_stock"] },
        "payload": {
          "parent_key": "product",
          "children_keys": {
            "product_image": "images",
            "product_variation": "variations",
            "product_stock": "stock"
          }
        },
        "single": ["product_stock"]
      }
    },

    "product_image": {
      "label": "Product Images",
      "fields": {
        "product_id": {
          "type":"relation","db":"foreignId","required": true,
          "relation": { "kind":"belongsTo", "target":"product", "display":"name" }
        },
        "image": { "type":"image", "db":"string", "required": true, "mimes":["jpg","jpeg","png"] }
      }
    },

    "product_variation": {
      "label": "Product Variations",
      "fields": {
        "product_id": {
          "type":"relation","db":"foreignId","required": true,
          "relation": { "kind":"belongsTo", "target":"product", "display":"name" }
        },
        "sku":   { "type":"text","db":"string","required": true, "unique": true },
        "color": { "type":"select","options":["Red","Green","Blue"], "db":"string" },
        "size":  { "type":"select","options":["S","M","L","XL"], "db":"string" },
        "price": { "type":"number","db":"decimal","precision":10,"scale":2,"required": true }
      }
    },

    "product_stock": {
      "label": "Product Stock",
      "fields": {
        "product_id": {
          "type":"relation","db":"foreignId","required": true,
          "relation": { "kind":"belongsTo", "target":"product", "display":"name" }
        },
        "quantity": { "type":"number","db":"integer","required": true },
        "warehouse": { "type":"text","db":"string" }
      }
    }
  }
}
Client payload shape

Client must send payload keys matching compose.payload: product, images, variations, stock.

Recipes (Copy/Paste)

1) Minimal module

{
  "modules": {
    "brand": {
      "fields": {
        "name": { "type":"text", "db":"string", "required": true }
      }
    }
  }
}

2) Catalog relations (category → sub_category → child_category)

{
  "modules": {
    "category": {
      "fields": { "name": { "type":"text","db":"string","required": true } }
    },
    "sub_category": {
      "fields": {
        "category_id": {
          "type":"relation","db":"foreignId","required": true,
          "relation": { "kind":"belongsTo","target":"category","display":"name" },
          "relation_between": true
        },
        "name": { "type":"text","db":"string","required": true }
      }
    },
    "child_category": {
      "fields": {
        "subcategory_id": {
          "type":"relation","db":"foreignId","required": true,
          "relation": { "kind":"belongsTo","target":"sub_category","display":"name" }
        },
        "name": { "type":"text","db":"string","required": true }
      }
    }
  }
}

3) Multiple select stored as JSON

{
  "modules": {
    "product": {
      "fields": {
        "name": { "type":"text","db":"string","required": true },
        "tags": { "type":"select","options":["new","featured","sale"], "multiple": true, "db":"json" }
      }
    }
  }
}

4) Textarea rich editor OFF

{
  "modules": {
    "page": {
      "fields": {
        "title": { "type":"text","db":"string","required": true },
        "body":  { "type":"textarea","db":"text", "rich": false, "full_width": true }
      }
    }
  }
}

5) Token tracking endpoint (public)

{
  "modules": {
    "delivery": {
      "fields": {
        "tracking_token": { "type":"text","db":"string","required": true, "unique": true },
        "status": { "type":"select","options":["pending","shipped"], "db":"string","required": true }
      },
      "api": {
        "public": {
          "endpoints": ["track"],
          "ownership": {
            "mode": "token",
            "token_field": "tracking_token",
            "param": "token",
            "source": "query"
          }
        }
      }
    }
  }
}

Gotchas (Common mistakes)

Multiple choice without db=json

If multiple: true and you store as string, saving + reading will become inconsistent. Use db: "json".

Wrong relation target

relation.target must match an existing module key exactly.

Compose payload mismatch

Client payload keys must match compose.payload.children_keys.

Best practices
  • Keep schema in Git, version it (e.g., add config.version).
  • Start admin-only, then enable user/public scopes intentionally.
  • Use label, help text, and attributes.placeholder to make admin UI self-explanatory.

If you want this doc to be even more Laravel-like, next step is splitting into multiple pages (Field Types page, Ownership page, Compose page) and adding “Edit this page” links. But the current single-page version is production-friendly and easy to ship inside your generator.