Lavra
A consulta tipada do DataForge: o cliente diz exatamente quais campos quer, e recebe exatamente aqueles.
Lavra é a consulta tipada do DataForge. O cliente diz exatamente o que precisa, numa consulta, e recebe exatamente aquilo — nem um campo a mais, nem uma segunda chamada para buscar o que faltou.
busca:
usuario(id: 7):
nome
pedidos(limite: 3):
numero
totalUma rota REST devolve o que o servidor decidiu devolver. Quem precisa de menos carrega o resto; quem precisa de mais faz outra chamada. Numa tela de celular com rede ruim, as duas coisas custam.
O nome#
Lavra é a extração de um veio de minério — e lavrar também é redigir um documento. As duas coisas que este módulo faz: um esquema é lavrado, e a consulta lavra dele exatamente o minério que quer. Segue a metáfora da forja, como Kiln, Crucible e Vitrine.
O esquema nasce dos seus records#
Esta é a decisão que define o módulo. Não há uma segunda linguagem de esquema ao lado da linguagem:
record Usuario:
id: Integer
nome: String
email: String
esq := Lavra.esquema("loja")
Lavra.tipo(esq, Usuario)O GraphQL precisa de um SDL próprio porque o servidor pode estar escrito em qualquer coisa, e o esquema tem de existir fora dela. Aqui o servidor é DataForge, e o record já diz nome, campo e tipo. Um arquivo de esquema ao lado seria uma segunda fonte de verdade para divergir da primeira.
A consulta é indentada#
Porque a linguagem é. Uma consulta escrita ao lado do código que a usa tem de parecer com ele — e o : no fim marca "isto tem seleção dentro", exatamente como em given, cycle e action.
| GraphQL | Lavra |
|---|---|
{ … } | indentação |
query | busca |
mutation | mudanca |
subscription | assinatura |
type | tipo |
input | entrada |
interface | contrato |
union | uniao |
scalar | escalar |
fragment X on T | trecho X em T |
...X | ...X |
... on T { } | ... em T: |
@include(if:) | @incluir(se:) |
@skip(if:) | @pular(se:) |
$v: T = padrão | $v: T := padrão |
! e [ ] | iguais |
! e [ ] são a única coisa emprestada da escrita do GraphQL, e de propósito: quem já conhece lê sem aprender nada, e quem não conhece aprende dois símbolos.
Um exemplo inteiro#
adopt Arcane.Lavra as Lavra
record Usuario:
id: Integer
nome: String
email: String
usuarios := [Usuario(1, "Ana", "ana@forja.co"), Usuario(2, "Bia", "bia@forja.co")]
esq := Lavra.esquema("loja")
Lavra.tipo(esq, Usuario)
Lavra.campo(esq, "Usuario", "nome", "String!")
action achar(raiz, args, ctx):
achados := [u cycle u in usuarios given u.id is args["id"]]
yield achados[0] given len(achados) bigger 0 otherwise void
Lavra.busca(esq, "usuario", "Usuario", args := {"id": "Integer!"}, resolve := achar)
Lavra.conferir(esq)
r := Lavra.executar(esq, """
busca:
usuario(id: 1):
nome
""")
out r["dados"]{usuario: {nome: Ana}}O que ele traz#
| Assunto | Página |
|---|---|
| Tipos, campos, argumentos, nulidade, listas | O esquema |
| Enums, entradas, escalares, contratos, uniões | O esquema |
| Busca, mudança, assinatura, apelidos | A consulta |
| Trechos, variáveis, diretivas, introspecção | A consulta |
| Resolvedores, contexto, erros, autorização | Resolvedores |
| Lote contra o N+1, paginação, cache | Desempenho |
| Validação, limites, rate limit, observabilidade | Segurança |
| HTTP, WebSocket, tempo real, cliente | O servidor |
| Vários serviços, portão, composição | Federação |
| CRUD, banco, DDD, testes, deploy, CI | Na prática |
O que ele NÃO é#
Lavra não é GraphQL. Não fala o protocolo do GraphQL, não lê arquivos de SDL, e uma ferramenta de GraphQL não conversa com ele. As ideias são as mesmas — e são boas —, a escrita é a do DataForge.
Ele também não é um planejador de consulta distribuído: a federação compõe esquemas e resolve a fronteira com o lote, e não com um plano de execução entre serviços. Um subconjunto pela metade seria pior que a honestidade de não ter.