Carga em lote
Uma carga em lote agrupa várias escritas numa requisição só. Em vez de um POST por
recurso, você manda um Bundle com uma entrada por escrita, e recebe de volta o resultado de
cada uma.
Serve para dois casos:
- volume — cadastrar dez pacientes sem fazer dez chamadas;
- dependência — criar um paciente e, na mesma requisição, uma condição que aponta para ele, sem precisar do identificador que ainda não existe.
Não há atomicidade. Cada entrada é processada de forma independente: uma falha não interrompe as demais, e o que já foi gravado não é desfeito. Um lote de cinco entradas independentes com uma falha grava quatro.
O FHIR chama isso de batch. O tipo transaction, que no padrão significa “tudo ou nada”, é
aceito por compatibilidade e processado exatamente como um batch — não confie na palavra.
A independência tem um limite: uma entrada que referencia outra falha junto com ela. Se o
paciente não gravar, a condição que aponta para ele é recusada com
Dependency <fullUrl> not processed successfully. É a contrapartida do caso de uso de
dependência.
Como montar
Só POST funciona. PUT, GET, DELETE e PATCH não estão implementados — a
entrada é recusada com Request method … not implemented. E os cabeçalhos condicionais
ifMatch, ifNoneMatch e ifModifiedSince também recusam a entrada.
O único condicional suportado é o ifNoneExist.
Recomendamos no máximo 10 entradas por lote, e de preferência cinco. Não é um limite validado — a API aceita mais —, mas lotes grandes degradam o processamento, e, como não há rollback, um lote grande que falha no meio é mais difícil de reconciliar do que cinco chamadas avulsas.
Referências entre entradas do mesmo lote
É o que o lote resolve melhor. Uma entrada aponta para outra pelo fullUrl dela, e a Nilo
grava na ordem certa, substituindo a referência pelo identificador do recurso que acabou de
criar.
O fullUrl é um identificador local do lote, não um endereço. Use uma urn:uuid: nova a
cada envio — ela não precisa existir em lugar nenhum, e não é gravada.
Para referenciar um recurso que já existe fora do lote, use o identifier dele, como em
qualquer escrita avulsa.
As entradas são processadas na ordem do array. O que a Nilo antecipa são só as dependências de uma entrada: se a entrada 2 referencia a 5, a 5 é gravada antes da 2.
Quando a ordem importa por outro motivo — desligar e religar um webhook, por exemplo —, é a ordem do array que vale.
Não monte um ciclo. A referenciando B e B referenciando A não tem solução, e a entrada
falha com erro inesperado (500), não com uma recusa limpa.
Três erros comuns de referência interna, todos recusando a entrada:
Reference <fullUrl> not found in bundle entries— ofullUrlreferenciado não existe no lote;Reference <fullUrl> has not identifier— a entrada referenciada não temidentifierpróprio, e sem ele não há o que substituir na referência;Reference <system>|<value> not found— a referência poridentifieraponta para um recurso que não existe fora do lote.
Evitar duplicata com ifNoneExist
ifNoneExist é uma busca. Se ela encontrar um recurso, a entrada não grava nada e devolve
o que já existe:
Só identifier= é aceito no ifNoneExist. Qualquer outro parâmetro de busca recusa a
entrada com ifNoneExist only allow the query param 'identifier'.
E encontrando mais de um recurso, a entrada também é recusada, com
Query … returned more than one resource. Use um identificador que resolva para um recurso só.
ifNoneExist é diferente do identifier do recurso: o identifier decide entre criar e
atualizar — veja Identificadores —; o ifNoneExist
decide entre criar e não fazer nada. Numa integração que
reenvia o mesmo lote, o identifier normalmente já resolve — o ifNoneExist é para quando você
quer garantir que nada seja tocado.
A resposta
O 200 da requisição não diz que as escritas deram certo. Ele diz que o lote foi
processado. O resultado de cada entrada está em entry[].response.status, como texto:
200 OK, 400 Bad Request, 409 Conflict, 429 Too Many Requests ou 500.
Uma integração que só olha o status HTTP da requisição perde todas as falhas.
E repare que o 500 vem sem frase, ao contrário dos outros quatro: quem parseia
“código + texto” quebra nele. Leia só os três primeiros caracteres.
As entradas da resposta não trazem fullUrl. A correspondência com o que você enviou é pela
posição no array — e, nas falhas, pelo location, que repete o seu fullUrl.
Quando o lote inteiro é recusado
Quatro coisas derrubam o lote antes de qualquer entrada:
typediferente debatchoutransaction;- payload que não é um
Bundleválido pelo FHIR; - identificador Nilo de outro ambiente em qualquer ponto do payload;
- uma entrada sem
resource— que, em vez de falhar sozinha, derruba a requisição inteira com erro inesperado (500).
Nos três primeiros a resposta é 400 com um OperationOutcome, e nada foi gravado.
Quais recursos podem ir num lote
Qualquer recurso que tenha POST avulso nesta API. Um recurso somente leitura numa entrada é
recusado — com uma mensagem limpa, ao contrário do POST direto:
É a única situação em que a tentativa de escrever um recurso somente leitura dá um erro
tratável. No POST avulso, ela falha com erro inesperado do servidor.
Nem toda recusa dentro de uma entrada é limpa. Uma validação que acontece antes de a
escrita começar — como uma classe de atendimento não suportada — sai com status 500 e um texto
de erro de linguagem, em vez do OperationOutcome do recurso. Confira o payload de cada entrada
como se fosse enviá-la sozinha.
O que a integração não cobre
- atomicidade — não há rollback, nem no
transaction; - métodos além de
POST; - os condicionais
ifMatch,ifNoneMatcheifModifiedSince; fullUrlnas entradas da resposta;- qualquer critério de
ifNoneExistque não sejaidentifier=.

