Skip to content
mobius1qwe edited this page Nov 10, 2026 · 1 revision

Home > Banco de dados (DBWare) > Recursos REST sobre tabelas 🚧

Recursos REST sobre tabelas (data API)

Important

Em desenvolvimento (2.0). Ainda não está numa versão lançada e pode mudar.

Vale para a 2.0 · Delphi XE em diante e Lazarus/FPC 3.2 em diante · Nível: intermediário

Em uma frase: o TRALDBCrudModule serve tabelas e views como recursos REST prontos (listar, ler, inserir, trocar, alterar, apagar), com filtros, ordem e paginação pela URL, sem você escrever uma rota nem um SELECT.

Você vai precisar: o pacote do módulo de banco da 2.0 (RALDBPackage + o link do seu componente de acesso: FireDAC, Zeos ou SQLDB), um TRALDBModule já configurado (ver DBWare) e um motor.

O que é

  • É REST sobre tabelas: GET /clientes, GET /clientes/1, POST, PUT, PATCH, DELETE.
  • Toda SQL é gerada pelo RAL, com parâmetros: valores da URL e do corpo nunca entram no texto da SQL.
  • Todo nome que vem da URL (coluna de filtro, _sort, _fields, chave do corpo) passa pelo catálogo da tabela, lido do próprio banco. Nome que o catálogo não conhece é 400. Por isso não há injeção de SQL pelos nomes.
  • O banco, o driver e o pool são os do TRALDBModule que você ligar.

Como ligar

Solte um TRALDBCrudModule (paleta RAL - Modules), ligue o Server e aponte o DBModule para um TRALDBModule existente. Depois monte os Resources.

Crud.DBModule := DBModule1;
Res := Crud.Resources.AddResource('/produtos', 'produtos');
Res.VersionField := 'versao';
propriedade do módulo
DBModule o TRALDBModule cujo driver, conexão e pool os recursos usam
Resources a lista de recursos
TotalCount True: as listas respondem X-Total-Count (um COUNT a mais por lista). Falso por padrão
FilterSyntaxes as sintaxes de filtro aceitas na URL: [fsSimple, fsRQL] por padrão
CatalogLife minutos que o catálogo de uma tabela fica em cache; 0 lê a cada pedido. Padrão 10
Storage saída em storage (veja abaixo)
TenantMode, TenantClaim, TenantHeader, TenantIdle multi-inquilino (veja abaixo)
OnGetTenant, OnGetTenantDatabase eventos do multi-inquilino

Cada item de Resources:

propriedade
Route o caminho, como /produtos, sob o Domain do módulo
TableName a tabela ou view (esquema.nome vale)
KeyFields as colunas da chave, separadas por vírgula; vazio = a chave primária do catálogo. Chave composta viaja como 1,ABC na URL
Operations o que o recurso responde: roList, roGet, roInsert, roUpdate, roPatch, roDelete (todas por padrão)
Fields as colunas servidas, uma por linha; vazio = todas menos HiddenFields
HiddenFields colunas que nunca são lidas nem escritas
ReadOnlyFields colunas lidas, nunca escritas
Roles papéis que podem usar o recurso (qualquer um deles, separados por vírgula); vazio = todos
ReadRoles, WriteRoles papéis só da leitura ou só da escrita; vazio = Roles
MaxPageSize o máximo de linhas de uma lista (100 por padrão)
DefaultSort a ordem das listas sem _sort, escrita como o _sort (-preco,nome)
BaseFilter uma condição SQL sua (de confiança) somada a todo comando do recurso, como ativo = 1
SelectSQL um SELECT seu no lugar da tabela; só leitura. Filtro, ordem e página valem sobre as colunas dele
VersionField coluna inteira que cresce de um a cada escrita; vira o ETag (veja concorrência)
TenantField a coluna do inquilino, com TenantMode = tmColumn
OnBeforeInsert, OnBeforeUpdate, OnBeforeDelete antes da escrita: altere os valores ou marque AAllow := False (responde 403)
OnAfterWrite depois de inserir, trocar, alterar ou apagar, com o registro como ficou (nil no delete)

Papéis vêm da identidade do pedido; ver autenticação.

Rotas e códigos

pedido faz resposta
GET /rota lista (com filtros) 200, array JSON
GET /rota/chave lê um registro 200 e ETag (com VersionField); 404 se não existe
POST /rota insere; o corpo é um objeto JSON 201, Location: /rota/chave e o registro
PUT /rota/chave troca o registro; o que o corpo não traz vira nulo 200 e o registro
PATCH /rota/chave altera só o que o corpo traz 200 e o registro
DELETE /rota/chave apaga 204; 404 se não existe
código quando
400 filtro, coluna ou valor inválido; coluna oculta ou só de leitura no corpo; chave gerada pelo banco enviada no corpo
403 sem o papel; recurso de SelectSQL escrito; evento OnBefore* recusou; sem inquilino
404 o registro não existe (ou é de outro inquilino)
405 a operação não está em Operations
409 chave duplicada ou violação de unicidade
412 If-Match não bate com a versão atual

Os erros vêm como problema RFC 9457 (application/problem+json).

Filtros da URL

Sintaxe simples

coluna=valor compara por igualdade; um sufixo muda a operação.

na URL significa
preco=10 igual
preco_gt=10 maior que
preco_gte=10 maior ou igual
preco_lt=10 menor que
preco_lte=10 menor ou igual
preco_ne=10 diferente
nome_like=Ana% LIKE (sem distinguir maiúsculas) com o seu padrão
nome_cont=ana contém
nome_sw=ana começa com
nome_ew=ana termina com
id_in=1,2,3 está na lista
id_nin=1,2 não está na lista
email_null=true é nulo (false: não é nulo)

Vários filtros se juntam com E. Controles (começam com _):

na URL significa
_sort=-preco,nome ordem; - é decrescente
_limit=10 e _offset=20 página por deslocamento
_page=3 e _per_page=10 página por número
_fields=id,nome só essas colunas

O tamanho da página nunca passa de MaxPageSize. Com TotalCount, a lista responde o total sem paginar em X-Total-Count. Um _ desconhecido é 400.

RQL

Na sintaxe RQL o filtro todo vai num parâmetro rql: /produtos?rql=and(eq(categoria,a),or(gt(preco,5),isnull(estoque)),not(eq(id,9))).

operador
and(...), or(...), not(...) grupos
eq, ne, lt, le, gt, ge comparações: eq(campo,valor)
like, contains, starts, ends texto
in(campo,a,b), out(campo,a,b) está / não está na lista
isnull(campo), notnull(campo) nulidade
sort(-campo,outro) ordem
limit(tamanho,deslocamento) página

Valor com vírgula ou parêntese vai entre aspas simples: eq(nome,'a,b').

Escolher a sintaxe

FilterSyntaxes diz quais valem no módulo. O pedido escolhe com o parâmetro filter-syntax (simple ou rql) ou o cabeçalho X-RAL-Filter-Syntax; o parâmetro vence o cabeçalho.

Concorrência otimista

Com VersionField (uma coluna inteira, por exemplo versao integer default 1):

  • toda resposta de registro traz ETag: "N";
  • todo PUT/PATCH soma 1 à coluna;
  • mande If-Match: "N" na escrita; se outro já mudou o registro, a resposta é 412 e nada é gravado. Sem If-Match (ou com *) a escrita passa.

Multi-inquilino

TenantMode diz onde vivem os dados de cada inquilino:

modo como isola
tmNone sem inquilinos (padrão)
tmColumn uma coluna em cada tabela (TenantField do recurso): todo comando filtra por ela e todo insert a preenche
tmSchema um esquema por inquilino no mesmo banco
tmDatabase um banco por inquilino; OnGetTenantDatabase aponta um driver novo (configurado como o DBModule) para o banco dele. Os pools ociosos por mais de TenantIdle segundos são liberados

O nome do inquilino vem de TenantClaim (da identidade), depois de TenantHeader, e OnGetTenant pode trocá-lo; nome vazio recusa o pedido com 403.

Warning

A SQL que o cliente manda no DBWare clássico (o SELECT do dataset) não passa por este módulo e só é isolada por inquilino nos modos tmDatabase e tmSchema. Em tmColumn ela enxerga todas as linhas.

Saída em storage

Ligue Storage a um TRALStorageLink (ver Storage XML e DBWare). A lista responde nesse formato quando o Accept do pedido traz o tipo do storage; sem ele, JSON.

Catálogo e bancos

O catálogo (RALDBTableInfo) lê do banco as colunas, tipos, chave primária, se aceitam nulo, valor padrão e o que o próprio banco preenche (identidade, sequência, auto_increment, rowid). Cada banco tem seu dialeto para aspas, paginação e LIKE sem maiúsculas:

banco paginação detalhe
Firebird 3+ ROWS a TO b
PostgreSQL LIMIT/OFFSET ILIKE
MySQL e MariaDB LIMIT/OFFSET identificadores com crase
SQLite LIMIT/OFFSET upper(...) like upper(...)

Testado de verdade com SQLite, em SQLDB (Lazarus) e FireDAC (Delphi). Firebird, PostgreSQL e MySQL/MariaDB têm o dialeto escrito, mas ainda sem teste contra o banco.

Erros comuns

sintoma causa
400 num filtro a coluna não existe no catálogo, é HiddenFields ou fora de Fields, ou o valor não cabe no tipo
400 no POST o corpo traz a chave que o banco gera, ou coluna oculta/só de leitura
403 no POST de um recurso de SelectSQL o recurso é só leitura
405 a operação não está em Operations
412 em toda escrita o If-Match é de uma versão antiga: leia o registro de novo
X-Total-Count não vem TotalCount está desligado
500 "DBModule is not set" falta ligar o DBModule

Veja também

Começar

Servidor

MQTT 🚧

E-mail 🚧

Cliente

Motores

Avançado

Ajuda

🚧 = em desenvolvimento, pode mudar

Clone this wiki locally