Key concepts overview
What ApiTables is, why it exists, and how the pieces fit together
If you are new to the team, read this page before writing any table code. It explains the idea behind ApiTables so the rest of the docs make sense.
What problem does ApiTables solve?
Most admin panels need data tables: sortable columns, filters, pagination, row buttons (“Edit”, “Delete”), and bulk actions (“Export selected”). Without a shared approach, every feature team rebuilds the same UI and API contract from scratch.
ApiTables splits the work cleanly:
| Who | Responsibility |
|---|---|
| Backend (you) | Define what the table shows, how data is queried, and what actions are allowed |
| Frontend | Render the table from JSON — no hard-coded columns or buttons |
This is called server-driven UI: the server sends structure; the client renders it.
Analogy
Think of a table class as a recipe card. The frontend is the kitchen — it follows the recipe but does not invent ingredients. If you forget to list an action in setRowActions(), the button will not appear, no matter what the frontend team does.
The three phases of every table
Every ApiTables feature goes through the same lifecycle:
Phase 1 — Structure (GET)
The client asks: “What columns, filters, and actions exist for table users?”
You answer via load-table/users. The response is JSON metadata — not the full dataset (unless withData=true).
Phase 2 — Data (POST)
The client asks: “Give me page 2 of users where name contains ‘john’, sorted by created_at.”
You answer via query-table/users. The package applies filters and sorting on your Eloquent builder and returns paginated rows.
Phase 3 — Actions (POST)
The user clicks “Archive” on a row or “Export” in the toolbar.
The client POSTs to the action URL that was embedded in the structure response. Your table class runs the callback and returns success/errors.
What you actually write as a backend developer
You rarely touch the package internals. Day-to-day work is:
- One config entry per table in
api-tables-config.php - One PHP class per table extending
TableAbstract - Optional translation keys, export views, and custom middleware
config/api-tables-config.php ← registry (table name → model + class)
app/ApiTables/UsersTable.php ← your table definition
lang/en/api-table.php ← column labels (optional)The package handles routing, JSON shaping, pagination, and default actions (show details, export Excel).
How this connects to the frontend
The React ApiTables module in our apps:
- Calls
load-table/{name}on page load - Stores structure in Redux
- Calls
query-table/{name}when the user changes page, filter, or sort - POSTs to action URLs when the user clicks buttons
Both sides must agree on field names. The contract is documented in Table structure. When in doubt, log the load-table response and compare with the frontend docs.
Junior vs senior mental model
| Level | Focus on |
|---|---|
| Junior | Config registration, $TBLColumns, basic filters, testing with curl/Postman |
| Mid | Row/bulk actions, bindings, scoped queries via params, middleware per table |
| Senior | Custom exports, email report pipeline, performance (eager loading, indexes), extending GeneralExport |
Common misconceptions
| Misconception | Reality |
|---|---|
| “I need to build REST endpoints for each column” | No — one load-table + one query-table per table name |
| “The frontend team adds filters” | Filters are defined in your filters() method |
| “Table name can be anything in the URL” | URL segment must match a key in config('api-tables-config.tables') |
| “Actions are separate Laravel routes I register” | Action URLs are generated automatically from your TableRowAction / TableBulkAction definitions |
Next steps
| Your goal | Read next |
|---|---|
| Install and ship first table | Getting started |
| Understand request flow | Data flow |
| Look up a term | Glossary |
| Something broke | Troubleshooting |