Construindo APIs RESTful nível 3 com Delphi e Datasnap

APIs RESTful - DevSpace
APIs RESTful - DevSpace

Última atualização em 10 de outubro de 2024 por Willian Tuttoilmondo

APIs RESTful e Delphi – Um casamento perfeito

Quem acompanha meus artigos há algum tempo já deve ter lido sobre vários assuntos. Já falei sobre multi-tenacidade e chaves primárias, padrões de projetos, class helpers, APIs RESTful, verticalização de dados, registro de logs em modo thread safe, interfaces e HATEOAS, de maneira bem didática, com um único propósito: chegarmos até aqui, onde veremos como aplicar todos estes conhecimentos para construir APIs RESTful dinâmicas e funcionais, onde a codificação será a menor possível e o resultado será uma API RESTful CRUD plenamente funcional.

Como já disse na série de artigos sobre APIs RESTful com Delphi e Datasnap – as partes I, II, III e IV podem ser lidas individualmente -, e que servirão de base para este projeto, uma versão Enterprise ou Architect do Embarcadero Delphi é necessária. Até o momento do fechamento deste artigo, a versão disponibilizada é a Athens 12.2. Se já temos tudo o que precisamos, podemos começar. Mas, antes, é sempre bom deixar as coisas bem claras.

Delphi é uma linguagem, ao contrário do imaginário popular, que implementa fortemente o paradigma de desenvolvimento orientado a objetos. O que comumente vemos são projetos RAD, muitos desenvolvidos há 20 ou 30 anos, que nunca passaram por um processo de modernização e foram sendo “evoluídos” utilizando as piores práticas possíveis. O mais comum é que eles sejam desenvolvidos em uma camada muito superficial da orientação a objetos, onde os desenvolvedores dropavam componentes visuais e não visuais em um formulário ou datamodule e usavam os eventos destes componentes para desenvolver as regras de negócio e persistência da aplicação.

Esse cenário gerou aplicações onde as regras de negócio se fragmentam e onde o acoplamento entre componentes acaba sendo o fator mais custoso durante sua manutenção. Com isso, empresas com produtos desenvolvidos desta forma no mercado têm pouca margem de manobra para mudanças mais profundas, o que acaba sucateando o próprio produto. A opção, muitas vezes, é reconstruir tudo, e geralmente com outra tecnologia, desperdiçando todo o potencial da equipe e todo o capital intelectual acumulado.

Mesmo que o Delphi tenha nascido da orientação a objetos, sua finada criadora insistia em divulgar amplamente o conceito RAD, o que trouxe popularidade entre as décadas de 1990 e 2000,mas que hoje tem sido motivo de piada no meio do desenvolvimento. Tanto é que há muita gente que acredita, ainda, que não existe orientação a objetos em Delphi e que, por isso, é uma tecnologia morta e ultrapassada. Neste ponto, estou aqui para desmentir esta afirmação, afinal, com quase 30 anos de experiência, acompanhei a evolução do Delphi e evoluí com ele, ao contrário de muitos outros desenvolvedores, ainda presos ao modelo RAD, aos eventos e a todas as práticas que, há anos, o mercado luta para extirpar.

“Delphi é uma ferramenta poderosa, versátil e completa, capaz de construir projetos seguros e confiáveis. Mas você precisa entender uma coisa: ele deve ser usado para aquilo que se propõe. Eu não usaria Delphi para construir um sistema web, assim como não usaria uma colher para bater num prego.”

Construir APIs RESTful com Delphi e Datasnap é a prova de que, sim, a ferramenta continua relevante e, mais do que isso, robusta, entregando um produto final com qualidade e que pode ser perfeitamente escalável. Basta escolher a arquitetura e abordagem mais adequadas. Mas, como tempo é dinheiro e eu não tenho nenhum dos dois, vamos ao que interessa.

APIS RESTful – Começando pelo começo

Após criarmos nosso módulo Apache onde nossas APIs RESTful ficarão abrigadas – se você não sabe como fazer isso, volte aqui e veja como -, chegou a hora de trabalhar. Como vimos no artigo sobre o modelo de maturidade de Richardson para APIs RESTful, é importante definir o comportamento inicial da nossa API. Isso significa definir como os endpoints serão apresentados, e isso definirá, inclusive, como modelaremos as respostas às consultas feitas à própria API.

Então, vale lembrar, como uma API CRUD, nossa função é realizar as “quatro operações básicas”, sem muita complexidade, portanto, não teremos ações de recursos sendo executadas aqui. Isso simplificará muito as coisas e, num futuro não tão distante, evoluiremos nosso projeto para abrigar ações mais complexas. Mas, agora,antes de voar, vamos aprender a caminhar.

Sendo assim, como definição minha, nosso endpoint raiz será http[s]://{servidor}[:porta]/api/devspace/cadastro/v1, onde adicionaremos o nome do recurso ao fim do endpoint para interagir diretamente com os recursos que deixaremos disponíveis para nosso projeto.

Outro ponto importante é: o nome dos recursos deve, sempre, ser criado em letras minúsculas e sem caracteres especiais, acentuados ou iniciando com números. É mais natural, em uma URI, vermos algo como /api/devspace/cadastro/v1/endereco ao invés de /api/devspace/cadastro/v1/Endereço. Além de uma boa prática, manter os nomes dos recursos em minúsculo ajuda na codificação, evitando a necessidade e decodificá-la para entender qual é o recurso acessado.

Criando os recursos para uso nas APIs RESTful

Como vimos no artigo sobre verticalização de dados, vamos criar, essencialmente, duas tabelas em uma base PostgreSQL e partiremos destas duas tabelas para criar nossas APIs RESTful. Assim, vamos seguir esse roteirinho maneiro e bem rápido para criar tudo que vamos precisar.

Aqui, vamos criar o usuário para o banco de dados e o próprio banco de dados.

Em seguida, vamos preparar o banco de dados para trabalhar com UUIDs como chaves primárias e criar o schema que abrigará os cadastros das nossas APIs RESTful.

E, por fim, as tabelas.

Com tudo criado, vamos criar alguns recursos que poderemos usar para nosso CRUD.

E, checando o que acabamos de fazer, chegamos nisso:

Recursos cadastrados para uso em APIs RESTful - DevSpace
Recursos cadastrados para uso em APIs RESTful – DevSpace

Só não esqueçam do seguinte: quando o cadastro é efetuado sem especificar um UUID, a função uuid_generate_v4() se encarregará de criar um novo, portanto, os valores do campo id serão diferentes dos que aparecem na imagem.

Criando a estrutura para as APIs RESTful

Com o banco de dados criado e alguns recursos cadastrados, podemos começar a colocar a mão na massa. Para criar APIs RESTful dinâmicas, vamos partir do seguinte princípio: uma aplicação do tipo Datasnap REST Application, como módulo para o webserver Apache. Tudo muito simples e rápido. Após todo o processo que já vimos aqui, chegaremos nesse resultado:

APIs Restful - Datamodule - DevSpace
APIs Restful – Datamodule – DevSpace

Além desses dois carinhas aí, hoje, vamos inserir mais alguns componentes que vamos utilizar em nossas APIs RESTful: uma conexão com o banco de dados, seu devido driver, seu devido complemento exigido por ela e uma consulta onde executaremos as ações pertinentes, chegando nisso:

APIs RESTful - Datamodule com conexão com o banco de dados - DevSpace
APIs RESTful – Datamodule com conexão com o banco de dados – DevSpace

A conexão com o banco de dados, logicamente, deve ser configurada para apontar para o banco de dados PostgreSQL criado para este projeto. Como geralmente isso é feito na mesma máquina em que se desenvolve, a configuração apontará para localhost, assim como nossas APIs RESTful também serão direcionadas para este banco de dados. Portanto, todo o conjunto de banco de dados e o servidor web Apache devem estar no mesmo local para que nenhuma mudança seja necessária.

Criando APIs RESTful dinâmicas

Agora, vamos nos concentrar naquilo que realmente interessa: a codificação. É importante entender que, como em todo projeto que desenvolvo, duas características são muito marcantes. A primeira é a utilização massiva dos princípios de Clean Code, dando nomes aos métodos, variáveis e constantes que realmente façam sentido para a compreensão do código. A segunda é o cuidado com o nível de toxicidade do código gerado, fazendo com que ele seja um código mais simples de efetuar a manutenção futuramente.

Quando verificarmos o resultado final da codificação, teremos uma visão muito clara disso.

Codificando APIs RESTful dinâmicas

Basicamente, vamos organizar as coisas da seguinte forma: uma unit com constantes para as consultas, outra com as constantes para os nós de objetos JSON e uma terceira para as constantes das mensagens que serão adicionadas às respostas 400, 404 e 500. Sendo assim, nossas units serão:

Já a unit do nosso datamodule ficará desta forma:

E, por fim, nosso arquivo DPR fica desta maneira:

 

Entendendo a codificação das APIs RESTful dinâmicas

Num primeiro momento, a codificação destas APIs RESTful podem parecer um tanto confusas, eu sei. O datamodule, em si, tem apenas um método – ContainerDefaultAction – e este tem uma infinidade de sub-rotinas. Para quem não está familiarizado com o conceito de sub-rotinas, realmente é estranho, já que todas elas poderiam ter sido criadas como métodos de visibilidade privada dentro do datamodule. Isso até faria sentido se esses métodos fossem invocados por mais de um método, o que não acontece aqui.

Mesmo que tivéssemos mais algum método do datamodule, dificilmente ele teria a necessidade de invocar as mesmas sub-rotinas que o método atual invoca e, por uma questão simples de escopo, isso já é o suficiente para que eu não as coloque com visibilidade privada no escopo do datamodule.

Outra razão para isso é o tamanho final dos métodos implementados. Se toda a codificação fosse feita no evento do datamodule, ele ultrapassaria facilmente as 200 linhas de código. Em termos de toxicidade de código, isso é terrível. Se acessarmos os parâmetros de toxicidade padrão do Delphi, teremos algo como isso:

APIs RESTful - Parâmetros de toxicidade - DevSpace
APIs RESTful – Parâmetros de toxicidade – DevSpace

Isso nos diz que, se um método tem mais que 20 linhas, 6 parâmetros, 5 níveis de profundidade em estruturas de decisão ou uma complexidade ciclomática acima de 6, nosso código está mais próximo de Chernobil do que imaginamos. Então, se verificarmos a toxicidade do código gerado para nossas APIs RESTful, teremos o seguinte:

APIs RESTful - Análise de toxicidade - DevSpace
APIs RESTful – Análise de toxicidade – DevSpace

O nível de toxicidade é medido partindo do 0 e tem o valor máximo, antes de se tornar tóxico, 999. Então, qualquer coisa acima de 1.000 indica que nosso código não está bom o suficiente e precisa ser melhorado. Como, no caso deste código, não ultrapassamos 400, nosso código é tão limpo quanto aquele rio que nasce no alto de uma serra.

No mais, a codificação está bem documentada, com comentários que indicam o comportamento de cada rotina, por mais que isso pareça até óbvio. Uma leitura mais atenta do próprio código nos trará o panorama geral de seu funcionamento e, por conseguinte, sua compreensão.

Compilando e testando nossas APIs RESTful

Como já vimos como fazer a compilação e o deploy de nossas APIs RESTful, não vamos descrever todo o processo novamente. Partindo diretamente para os testes, vamos testar a URL http://localhost/api/devspace/cadastro/v1 sem definir o recurso e ver o que a API responde:

APIs RESTful - Testando as requisições - DevSpace
APIs RESTful – Testando as requisições – DevSpace

Se dermos uma olhada no código do datamodule, mais precisamente na linha 350 – sub-rotina VerificaRecursoDefinido -, veremos que, quando não definimos o recurso, a resposta será, exatamente, a apresentada na imagem acima. Já se fizermos o teste com um recurso diferente dos que cadastramos aqui, teremos o seguinte:

APIs RESTful - Testando requisições com recursos não cadastrados - DevSpace
APIs RESTful – Testando requisições com recursos não cadastrados – DevSpace

Mais uma vez, se olharmos o código desenvolvido para nossas APIs RESTful, veremos na linha 337, sub-rotina VerificaExistenciaRecurso, que a resposta também é adequada. Já se fizermos a requisição usando um dos recursos cadastrados,como ainda não cadastramos nenhum registro, teremos o seguinte:

APIs RESTful - Testando requisições com recursos existentes - DevSpace
APIs RESTful – Testando requisições com recursos existentes – DevSpace

Inserindo registros em nossas APIs RESTful

Já publicamos e fizemos testes básicos com nossa API, então é hora de inserir dados e entender o comportamento por trás dessa estrutura. Se usarmos este objeto JSON no corpo de uma requisição POST, obteremos o seguinte:

APIs RESTful - Cadastrando um registro de um recurso existente - DevSpace
APIs RESTful – Cadastrando um registro de um recurso existente – DevSpace

Se consultarmos este registro que acabamos de inserir, usando seu Id, teremos o seguinte:

APIs RESTful - Consulta um recurso por seu Id - DevSpace
APIs RESTful – Consulta um recurso por seu Id – DevSpace

Já se fizermos uma consulta sem especificar o Id consultado, teremos:

APIs RESTful - Consultando um recurso sem Id definido - DevSpace
APIs RESTful – Consultando um recurso sem Id definido – DevSpace

Ampliando nossas APIs RESTful sem a necessidade de codificação

Uma das grandes vantagens de construirmos APIs RESTful dinâmicas é eliminar qualquer necessidade de codificação para ampliar os recursos disponíveis nela, o que as torna APIs RESTful de evolução no code, algo que, sinceramente, muita gente nem imagina que seja possível com Delphi. Se fizermos uma requisição GET à URL http://localhost/api/devspace/cadastro/v1/tributo, receberemos uma resposta 400 (Bad request), dizendo que o recurso não foi encontrado em nossa API. Contudo, se executarmos a instrução insert into cadastro.recurso values (uuid_generate_v4(), ‘tributo’); em nosso banco de dados e refizermos nossa requisição GET, a resposta muda para 404 (Not found), já que o recurso foi encontrado na API, mas ele ainda não tem registros.

O que fizemos aqui, afinal, foi criar uma estrutura que nos possibilita construir APIs RESTful com baixíssima necessidade de manutenção, altamente escaláveis e com evolução sem qualquer necessidade de codificação. Isso torna nossas APIs RESTful extremamente versáteis, capazes de atender às necessidades de qualquer projeto que necessite de cadastros simples.

Por óbvio, dentro dessa estrutura básica, não foram previstas formas de validação de objetos JSON antes de sua inserção. Mas, caso você queira entender como isso funciona e tiver um pouquinho de paciência, um dos próximos passos será o artigo sobre esse tema, trazendo diversas informações para que você possa implementar este recurso utilizando todo o poder do PostgreSQL e eliminar a necessidade de fazê-lo com Delphi.

Testando os demais verbos em nossas APIs RESTful

Ao chegarmos neste ponto, já está bem claro como funcionarão nossas APIs RESTful construídas sobre esta estrutura. Com qualquer cliente REST é possível fazer todas as requisições, em todos os verbos disponíveis, sendo eles GET, POST, PUT, PATCH, DELETE e HEAD. É claro que, para isso, é necessária uma boa compreensão de como funcionam APIs RESTful e como elas se comportam conforme mudamos os verbos das requisições.

Aqui, você é livre para efetuar seus próprios testes e verificar as respostas mas, isso eu posso garantir, tudo será simples e rápido, como as boas APIs RESTful devem ser.

Posso aplicar tudo isso em minhas APIs REStful?

Algo que sempre falo por aqui é: o nível de maturidade de quem trabalha com o desenvolvimento é o que determina a aplicação ou não de uma técnica, tecnologia ou conhecimento. Mas, sim, com esta estrutura você pode construir APIs RESTful extremamente confiáveis e que te permitirão expandir qualquer negócio de forma rápida e simples e, o melhor de tudo, com pouquíssimo ou até nenhum código adicional.

“Quanto mais buscamos o conhecimento sobre aquilo que fazemos, mais percebemos que ainda não sabemos tudo. Se eu, hoje, desenvolvesse soluções como há 15 anos, teria desperdiçado 15 anos da minha carreira. Se daqui a 2 anos eu ainda desenvolver soluções como desenvolvo hoje, serão mais dois anos da minha carreira jogados fora.”

E o que vem depois?

Quero terminar este artigo como aquela série que sempre deixa um gancho gigante no último episódio de cada temporada. Para o futuro, existem outros artigos planejados, como estruturas de validação de objetos JSON através de JSON schemas usando o PostgreSQL, para que possamos garantir que os objetos que estamos inserindo ou atualizando na base de dados correspondem ao objeto esperado, ou utilização de padrões de projetos, interfaces e RTTI para manipularmos objetos e efetuarmos operações complexas em nossas APIs RESTful. Mas, até lá, vale a pena explorar a estrutura que desenvolvemos aqui.

Ah, e como sempre, o código fonte desse projeto pode ser baixado aqui.

Abraços e até a próxima.

Sobre Willian Tuttoilmondo 12 Artigos
Arquiteto de software com mais de 25 anos de experiência em desenvolvimento de software, especialista em desenvolvimento multicamadas utilizando Embarcadero Delphi e NodeJS para o back-end e o próprio Delphi para o front-end. Usuário Linux desde 1998, é evangelista PostgreSQL, banco de dados no qual é especialista. Ocupa hoje a posição de arquiteto de software na TOTVS, a maior empresa de tecnologia do Brasil, além de ser sócio fundador da LT Digital Labs, empresa especializada em desenvolvimento e treinamentos.

Seja o primeiro a comentar

Faça um comentário

Seu e-mail não será publicado.


*