Docs›REST API›Visão geral
REST API

Visão geral

ApiResourceController sobre Eloquent com mestre-detalhe, exposto por mad.api (routes/api.php, Bearer stateless) ou mad.auth (grupo web) — vs o Driver REST assinado.

"REST API" aqui não é um processo separado nem um entry point próprio — é um conjunto de rotas dentro do mesmo app Laravel, declaradas com Route::apiResource e atendidas por controllers que estendem Mad\Rest\ApiResourceController. Mesmo router, mesmo processo — só que a resposta é sempre JSON, nunca HTML/MadWire.

Existem três superfícies sob o nome "REST" neste framework. Esta seção cobre as duas primeiras (mesmo controller, middlewares diferentes); a terceira é citada para você não confundir com elas.

SuperfíciePara quemAutenticaçãoOnde
ApiResourceController + mad.api (esta seção) Integração externa: outro sistema, script, job, app mobile — sem login web. Authorization: Bearer mad_api_… — stateless, sem cookie nem CSRF, com escopo de tenant/unit vindo do próprio token e abilities por rota. routes/api.php, prefixo /api, grupo api.
ApiResourceController + mad.auth (esta seção) Seu próprio frontend (AJAX do admin) consumindo o banco do app via JSON. mad.auth — sessão do admin + CSRF, igual a qualquer tela. routes/modules/*.php, grupo web com prefixo api.
Driver REST (Mad\Rest\RestDriver*) Ferramenta externa (MadBuilder Database Manager) operando o banco do app sem VPN nem porta aberta. HMAC assinado por chave pareada — sem sessão. _mad/rest-driver/*, desabilitado por padrão.

Veja Autenticação para o detalhe completo das três — inclusive por que o Driver REST não serve para expor dados ao seu frontend (ele devolve linhas cruas de SQL, não um recurso modelado).

// routes/api.php — API pública stateless (prefixo /api aplicado pelo bootstrap)
Route::middleware('mad.api:orders.read')->get('/orders', [OrderApiController::class, 'index']);
Route::middleware('mad.api')->apiResource('customers', CustomerApiController::class);
curl 'https://app.exemplo.com/api/orders' -H 'Authorization: Bearer mad_api_3f9c…'

O token carrega o escopo: cada mad_api_token é amarrado a um par user + unit, e a unit determina tenant e — em multi-database — o banco. Emissão pela tela admin Tokens de API (/app/tokens-api) ou por php artisan mad:api-token; detalhes em Tokens de API e abilities.

O que ApiResourceController resolve

É uma classe base abstrata: a subclasse só declara configuração ($model, filtros permitidos, eager-load, quais relações são "detalhe") — as cinco ações REST (index/show/store/update/destroy) já vêm prontas, casando 1:1 com Route::apiResource:

// app/Http/Controllers/Sys/ImportTemplateApiController.php — exemplo real deste repo
namespace App\Http\Controllers\Sys;

use App\Models\Sys\ImportTemplate;
use Mad\Rest\ApiResourceController;

class ImportTemplateApiController extends ApiResourceController
{
    protected string $model = ImportTemplate::class;

    protected array $searchable = ['title', 'code', 'database_name', 'active', 'created_by'];
    protected array $sortable   = ['title', 'code', 'created_at', 'updated_at'];

    protected array $with      = ['creator'];      // eager-load default (belongsTo)
    protected array $withCount = ['groups', 'users', 'logs'];

    protected array $details = ['groups', 'users']; // hasMany tratadas como mestre-detalhe

    protected ?string $defaultOrder = 'title asc';
}
// routes/modules/admin.php
Route::middleware('mad.auth')
    ->prefix('api')
    ->group(function () {
        Route::apiResource('import-templates', \App\Http\Controllers\Sys\ImportTemplateApiController::class);
    });

Isso já registra as cinco rotas (confira com php artisan route:list --path=api neste app):

GET|HEAD   api/import-templates                       import-templates.index
POST       api/import-templates                       import-templates.store
GET|HEAD   api/import-templates/{import_template}      import-templates.show
PUT|PATCH  api/import-templates/{import_template}      import-templates.update
DELETE     api/import-templates/{import_template}      import-templates.destroy

Ganhos sobre escrever cada endpoint à mão: mestre-detalhe com múltiplas relações (várias hasMany no mesmo payload, cada uma sincronizada — cria/atualiza/apaga por diff de PK), eager-load sem N+1 ($with/$withCount aplicados automaticamente em toda query), validação por convenção (Model::rules($id), a mesma usada pelo MadForm do admin), filtros via DSL JSON e paginação com meta pronta.

Quando NÃO é a ferramenta certa

Para um endpoint AJAX raso de um único Active Record (sem detalhe, sem paginação-meta), a alternativa mais simples é Mad\Service\MadRecordService — um serviço CRUD fino despachado por verbo HTTP. Regra prática: se o recurso tem detalhe/relação no payload ou precisa parecer uma API REST de verdade (status codes, paginação, projeção), use ApiResourceController; se é só ler/gravar um registro plano, MadRecordService resolve com menos código.

Erros não tratados também viram JSON

bootstrap/app.php configura $exceptions->shouldRenderJsonWhen(fn ($request) => $request->is('api/*')). Qualquer exceção que escape de um controller sob /api/* — incluindo 404 de rota inexistente — é renderizada como JSON pelo handler nativo do Laravel, nunca como página HTML de erro.

Próximos passos