Getting started

Install storageitsolutions/api-tables and add your first control table

This guide walks you through your first working table from zero. No prior ApiTables experience required. If you already know Laravel, you can finish in under an hour.

Read first

New to the concept? Spend 10 minutes on Key concepts so terms like structure, query, and table name are clear before you copy-paste config.

What you will build

By the end of this guide you will have:

  • The package installed and configured
  • A UsersTable class with two columns
  • A working load-table and query-table response
  • A curl command you can run to verify everything

The frontend team can then point their ApiTables page at your endpoints.


Prerequisites

RequirementWhy
Laravel app (10+)Package uses service provider, routes, Eloquent
PHP 8.1+Match your app's PHP version
Laravel SanctumDefault auth middleware is auth:sanctum
api guard in config/auth.phpRoutes register under {guard}/api-table/...
Valid API token for testingUnauthenticated requests return 401

Optional later:

  • Queue worker — only needed for email report bulk actions
  • Published lang files — for translated column headers

Install the package

composer require storageitsolutions/api-tables

The service provider registers automatically — you do not add it to config/app.php manually.

Publish files into your app:

php artisan vendor:publish --tag=config   # → config/api-tables-config.php
php artisan vendor:publish --tag=lang     # → lang files for column labels
php artisan vendor:publish --tag=views    # → email templates for reports

Package identifiers

WhatValue
Composerstorageitsolutions/api-tables
NamespaceStorageitsolutions\ApisTables
Config keyapi-tables-config (file: config/api-tables-config.php)

Register your first table

Open config/api-tables-config.php. Think of the tables array as a phone book: the key is the public table name used in URLs; the value tells the package which model and class to use.

'guards' => ['api'],
'default_middlewares' => ['auth:sanctum'],

'tables' => [
  'users' => [
    'model' => App\Models\User::class,
    'tableClass' => App\ApiTables\UsersTable::class,
    // 'middlewares' => ['admin'],  // uncomment when you need extra auth
  ],
],

'user_model' => App\Models\User::class,

Rules to remember:

  • The config key (users) must match TABLENAME in your table class
  • model must be a valid Eloquent model class
  • tableClass must extend TableAbstract

Generate the table class

php artisan make:api-table UsersTable

This creates app/ApiTables/UsersTable.php from the package stub. You will edit three things minimum:

  1. const TABLENAME = 'users'
  2. $TBLColumns — what columns appear
  3. setInitialBuilder() — base Eloquent query

Complete minimal example

<?php

namespace App\ApiTables;

use Storageitsolutions\ApisTables\Abstracts\TableAbstract;
use Storageitsolutions\ApisTables\Interfaces\APITableInterface;

class UsersTable extends TableAbstract implements APITableInterface
{
    const TABLENAME = 'users';

    protected array $TBLColumns = [
        [
            'type' => 'text',
            'label' => 'name',
            'data_src' => 'name',
            'sortable' => true,
            'attributes' => ['showable' => true],
        ],
        [
            'type' => 'text',
            'label' => 'email',
            'data_src' => 'email',
            'sortable' => true,
            'attributes' => ['showable' => true],
        ],
    ];

    public function __construct(...$args)
    {
        parent::__construct(self::TABLENAME);
    }

    protected function setInitialBuilder(): void
    {
        // Start from the model defined in config
        $this->builder = $this->model::query();
    }

    public function filters(bool $api_array = true): array
    {
        return []; // add filters later — see /docs/filters
    }
}

What each part does

PiecePurpose
TABLENAMELinks this class to config tables.users and URL /load-table/users
$TBLColumnsBecomes the columns array in structure JSON
setInitialBuilder()Defines which rows are eligible (add where, with, policies here)
filters()Returns filter definitions — empty array means no filter UI

Verify it works

Route paths

With guards => ['api'] and Laravel's default RouteServiceProvider prefix:

ActionMethod & path
Load structureGET /api/api-table/control-tables/load-table/users
Query dataPOST /api/api-table/control-tables/query-table/users

Test structure (GET)

export TOKEN="your-sanctum-token"

curl -s -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" \
  https://your-app.test/api/api-table/control-tables/load-table/users | jq .

Expected: "success": true and a columns array with name and email.

Test query (POST)

curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"page":1,"perPage":10,"filters":[],"sorts":[]}' \
  https://your-app.test/api/api-table/control-tables/query-table/users | jq .

Expected: "success": true, items array, and pagination object.

Tip for juniors

If GET fails, fix auth and config before touching column code. If GET works but POST returns empty items, check your database has rows and setInitialBuilder() is not over-filtering.


Column label values are translated via:

lang/{locale}/api-table.php  →  "users.name" => "Full name"

After publishing lang files, add:

// lang/en/api-table.php
return [
    'users' => [
        'name' => 'Name',
        'email' => 'Email',
    ],
];

Without this, the raw label key may appear in the UI.


Hand off to frontend

Share with the frontend developer:

ItemValue
Table nameusers
Structure path/api-table/control-tables/load-table/users (or full URL)
Sample load-table JSONPaste your curl output

They wire this into useTableStructure — see the frontend docs.


What to learn next

GoalRead
Understand request flowArchitecture overview, Data flow
Add search/filterFilters
Add row buttonsRow actions
Add exportBulk actions
Restrict who sees the tableGuards and middleware
JSON field referenceTable structure
Something brokeTroubleshooting

Checklist before opening a PR

  • Table registered in api-tables-config.php
  • TABLENAME matches config key
  • load-table returns expected columns
  • query-table returns paginated data
  • Translation keys added for new column labels
  • Tested with authenticated token (not just locally without auth)

On this page