Pular para o conteúdo
Todos os artigos
  • C#
  • .NET
  • React
  • TypeScript
  • APIs

O contrato vem antes da tela: integrando ASP.NET Core e React com menos ambiguidade

Uma aplicação full-stack fica mais previsível quando backend e frontend compartilham um contrato claro sobre dados, erros e estados — antes mesmo de a interface ser construída.

Willians Torres 5 min de leitura

O contrato é maior do que o formato do JSON

Quando pensamos no contrato entre uma API e uma interface, é comum olhar apenas para o corpo da resposta. Se o JSON possui os campos esperados, parece que a integração está resolvida. Na prática, o contrato também inclui o significado desses campos, os códigos HTTP, os possíveis erros e o comportamento esperado quando algo não existe.

Um campo de status, por exemplo, pode ser tecnicamente uma string e ainda assim gerar dúvidas importantes. Quais valores ele pode receber? Um novo valor pode surgir sem aviso? O frontend deve apenas exibi-lo ou tomar decisões a partir dele? Uma propriedade pode ser nula, ser omitida ou sempre estar presente?

Essas perguntas parecem pequenas, mas se espalham rapidamente pelo produto. Quando não são respondidas no contrato, acabam sendo respondidas de formas diferentes em cada tela.

  • a estrutura e o significado dos dados
  • quais valores são opcionais
  • como datas e valores numéricos são representados
  • quais erros podem ocorrer
  • o que caracteriza sucesso, ausência e falha

DTOs devem representar a conversa, não o banco

Expor diretamente uma entidade interna da aplicação pela API cria um acoplamento desnecessário. Uma mudança feita para atender ao banco ou à regra de negócio pode alterar o JSON sem que essa fosse a intenção.

Um DTO explícito comunica melhor o que aquele endpoint realmente oferece:

OrderSummaryResponse.cscsharp
public sealed record OrderSummaryResponse(
    Guid Id,
    string Number,
    decimal Total,
    string Status,
    DateTimeOffset CreatedAt
);

No frontend, o tipo correspondente documenta a expectativa da interface:

order-summary.tstypescript
type OrderSummary = {
  id: string
  number: string
  total: number
  status: string
  createdAt: string
}

Mesmo nesse exemplo simples há decisões a registrar. DateTimeOffset será serializado como texto e precisará ser interpretado no navegador. Um decimal normalmente chega ao JavaScript como number, o que pode não ser adequado para cálculos financeiros que exijam precisão exata. Dependendo do domínio, representar valores monetários em unidades mínimas ou como texto pode ser uma escolha mais segura.

O ponto não é encontrar uma representação universal. É fazer a escolha conscientemente e tratá-la como parte do contrato.

Erros também fazem parte da API

Uma integração não está completa quando apenas o caminho feliz funciona. Se cada endpoint devolve erros em um formato diferente, o frontend precisa acumular exceções, condicionais e mensagens genéricas.

O ASP.NET Core oferece suporte ao formato ProblemDetails, que permite representar erros com uma estrutura consistente. Campos como título, status, detalhe e identificador da ocorrência podem ser complementados com informações específicas, como erros de validação.

  • erro de validação associado aos campos do formulário
  • recurso não encontrado
  • ação não autorizada
  • conflito com o estado atual
  • falha inesperada apresentada sem expor detalhes internos

A mensagem exibida ao usuário não precisa ser uma reprodução literal da resposta técnica. O backend comunica a categoria e o contexto; o frontend traduz isso para a experiência adequada. Também é importante distinguir erro de transporte de erro de negócio: uma conexão interrompida não tem o mesmo significado de uma regra que impediu a operação.

TypeScript reduz dúvidas, mas não valida a rede

Tipos compartilhados ou gerados a partir de uma especificação OpenAPI podem reduzir trabalho manual e detectar incompatibilidades durante o desenvolvimento. Eles ajudam bastante, mas não garantem que o valor recebido em execução possui aquela forma.

O TypeScript desaparece depois da compilação. Uma resposta externa, um dado antigo ou uma implantação incompatível ainda pode violar o tipo declarado. Em pontos críticos, a validação em tempo de execução continua sendo necessária.

  1. DTOs intencionais no backend
  2. uma especificação de API atualizada
  3. tipos consumidos pelo frontend
  4. validação em execução onde o risco justificar
  5. testes de integração para os fluxos principais

A interface precisa conhecer todos os estados

Uma tela não possui apenas os estados com dados e sem dados. Durante uma requisição, ela pode estar carregando, vazia, parcialmente preenchida, indisponível ou exibindo dados antigos enquanto uma atualização acontece.

  • carregamento inicial
  • sucesso com dados
  • sucesso sem resultados
  • erro recuperável
  • acesso negado
  • atualização em andamento
  • operação concluída com feedback

Nomear esses estados antes de implementar evita que uma lista vazia seja confundida com uma falha ou que um botão permaneça ativo durante duas submissões consecutivas. Se ausência, erro e coleção vazia são tratados da mesma forma pela API, a interface perde a capacidade de explicar ao usuário o que aconteceu.

Evoluir sem transformar cada alteração em ruptura

Contratos mudam porque produtos mudam. A meta não é congelar a API, mas tornar sua evolução compreensível. Adicionar um campo opcional costuma ser menos arriscado do que alterar o significado de um campo existente. Remover propriedades ou mudar formatos exige conhecer os consumidores afetados.

Antes de concluir uma integração, verifico se nomes e significados estão claros, se nulidade e ausência foram diferenciadas, se datas e valores possuem uma representação definida, se os erros são consistentes e se a interface contempla carregamento, vazio e falha.

Quando essas respostas aparecem cedo, backend e frontend deixam de negociar comportamento por tentativa e erro.

Continue lendo

Artigos relacionados

4 min

Arquitetura clara antes de arquitetura sofisticada

Uma boa arquitetura não é a que acumula mais camadas e padrões. É a que torna evidente onde uma mudança deve acontecer, quais partes ela afeta e por que o sistema foi organizado daquela maneira.

Arquitetura