Criar APIs REST e GraphQL
Execute uma API como uma function que lê o SQL Database, grava pela Azion API, guarda respostas de lista em cache e reporta latência e erros.
Um time de desenvolvimento constrói o backend REST ou GraphQL que os clientes web e mobile dele chamam, com usuários em várias regiões e sem nenhum servidor que ele queira operar. A API guarda os registros em um banco de dados relacional, a maioria das chamadas lê e algumas chamadas gravam. Esta página implanta uma function que implementa todos os endpoints de uma API REST, lê os registros do SQL Database, grava-os pela Azion API e mantém a resposta de lista em cache por pouco tempo. O resultado é medido pelo tempo de resposta da API por região, pela taxa de erros sob carga e pelo tempo entre uma mudança de código e a produção.
Este caso de uso não cobre APIs que reagem a eventos em vez de chamadores, que Criar APIs orientadas a eventos cobre, nem controles de segurança na frente de uma API que roda em outro lugar, que Proteger APIs públicas contra abuso cobre.
Pré-requisitos
- A Azion CLI instalada, com o seu personal token salvo. Para configurá-la, consulte Primeiros passos com a Azion CLI.
- SQL Database habilitado na sua conta e a permissão Edit SQL Database. O produto está em Preview, então solicite acesso pelo Technical Support.
- Um personal token para as chamadas ao banco de dados, separado do que a CLI usa, porque a function o armazena. Para criar um, consulte Personal tokens.
- Node.js e um gerenciador de pacotes, que a CLI usa para construir o projeto.
- Os nomes que esta página usa:
tasks-apipara o banco de dados e para o cache de respostas,/api/taskspara o caminho da API,TASKS_DB_IDeTASKS_SQL_TOKENpara as variáveis de ambiente da function eapi.example.compara o domínio. O deploy exibe um domínioxxxxxxxxxx.map.azionedge.net; para servir a API no seu próprio domínio, consulte Adicione um domínio a um workload. Substitua cada valor pelo seu em todos os passos.
Produtos necessários
| A API precisa de | O que significa | Produto | Documentado em |
|---|---|---|---|
| Endpoints que rodam sem nenhum servidor para operar | Uma function, implantada com a Azion CLI, que a aplicação executa em toda requisição | Functions | Faça o deploy de uma function com a Azion CLI |
| Registros que todos os endpoints leem e gravam | Leituras de uma read replica do banco de dados dentro da function, e gravações pela Azion API | SQL Database | Grave linhas no SQL Database a partir de uma function |
| Uma resposta de lista que não é reconstruída a cada chamada | Uma resposta que a function guarda com a Cache API do runtime, sob um max-age | Cache | Armazene em cache a resposta de uma function com a Cache API |
| Latência e erros no domínio da API | Os dashboards Requests e Status Codes filtrados pelo domínio, e as invocações de Functions | Real-Time Metrics | Dashboards de Build |
Arquitetura de referência
Esta página constrói a API de serviço único sobre o SQL Database: uma function implementa todos os endpoints e lê e grava um banco de dados no SQL Database.
Leia o diagrama da function para fora. Toda requisição que a aplicação recebe executa a mesma function, então a function é o único lugar que conhece todos os endpoints. A partir dela, os caminhos se dividem pelo que o endpoint faz: uma resposta que todo cliente pode compartilhar vai para o Cache, uma leitura vai para uma réplica do banco de dados e uma gravação vai para a instância principal. O KV Store fica ao lado do banco de dados para valores lidos por chave, como uma sessão, e o design funciona sem ele. O Real-Time Metrics lê o que a aplicação registra e não muda nada no caminho da requisição.
Fluxo de dados
- A requisição de um cliente chega ao workload no domínio da API, que a entrega à aplicação, e uma regra da aplicação executa a function da API.
- A function compara o método e o caminho. Um caminho fora de
/api/tasksresponde404. - Um
GETda lista de tarefas é respondido pela cópia que a function guardou no Cache. Quando não existe cópia, a function lê as linhas e guarda a nova resposta por 60 segundos. - Um
GETde uma tarefa abre uma conexão com uma read replica do banco de dados comDatabase.open, que recebe o nome do banco de dados e nenhum token, então os dados de que uma leitura precisa ficam dentro da Azion. - Um
POSTou umDELETEenvia o statement ao endpoint de query do banco de dados na Azion API, com o personal token que a function lê de uma variável de ambiente, porque a instância principal é a única que aplica gravações. As réplicas passam a ter a mudança em seguida. - Depois de uma gravação, a function apaga a resposta de lista guardada, para que a requisição de lista seguinte leia as linhas de novo.
Componentes
- aplicação: o Platform Resource que recebe as requisições da API no domínio dela e as encaminha para a function com uma regra do Rules Engine.
- Functions: uma function implementa todos os endpoints. Um deploy substitui o código de todos os endpoints juntos, e é isso que faz da API uma unidade, então um deploy, um rollback ou uma mudança de schema chega a todos os endpoints no mesmo momento.
- SQL Database: guarda os dados relacionais da API. Uma function lê por uma read replica sem token, e as gravações vão para a instância principal pela Azion API com um personal token.
- KV Store: guarda sessões e configurações lidas por chave, uma opção de design. Ele tem consistência eventual e não tem compare-and-set, então carrega valores que toleram um atraso curto, não os registros da API.
- Cache: guarda as respostas que todo cliente pode compartilhar, como uma lista ou uma consulta, para que uma chamada repetida pule a leitura no banco de dados.
- Real-Time Metrics: reporta o tempo de requisição e os status codes da API por domínio e o número de vezes que a function rodou.
Outros designs para este caso de uso
- API de serviço único sobre um banco de dados externo: para times cujos dados já estão em um banco de dados gerenciado como Neon, MongoDB Atlas, TiDB ou Turso, que uma function consulta pelo driver serverless ou pela API HTTP dele. Toda chamada fora do cache chega ao banco de dados externo, então a latência e a disponibilidade dele entram nos fluxos de requisição e de falha.
- API de microsserviços sobre Functions: para times que dividem uma API em serviços de times diferentes, cada um uma function com o seu próprio armazenamento, implantada e versionada de forma independente. Uma regra do Rules Engine encaminha cada prefixo de caminho para o seu serviço e os serviços chamam uns aos outros por HTTP pelas mesmas regras, então um deploy muda um serviço e deixa os outros na versão que tinham.
Configure o banco de dados de tarefas
A API de tarefas guarda os registros em uma tabela de um banco de dados chamado tasks-api. A function lê o banco de dados pelo nome e grava nele pelo identificador, então esta seção cria o banco de dados pela API, cuja resposta de criação retorna esse identificador. O Azion Console também pode criar o banco de dados e executar o statement na aba Editor, como Crie e gerencie bancos de dados mostra.
Para criar o banco de dados:
A API responde 202. Guarde data.id: é o identificador do banco de dados em que a function grava.
O provisionamento leva cerca de 15 segundos. Envie GET /v4/workspace/sql/databases/<database-id> até que status mostre created e, depois, crie a tabela e duas linhas para ler de volta. completed é uma coluna inteira, 0 ou 1, porque a function a lê como número:
A API responde 200 com "state": "executed" e uma entrada por statement em data. Um statement que falha também responde 200, com error no lugar de results na entrada dele, então verifique cada entrada:
O banco de dados tasks-api tem a tabela tasks e duas linhas.
Configure as credenciais de gravação da function
Uma function lê um banco de dados por uma réplica somente leitura, e um statement que grava falha nela com attempt to write a readonly database. Por isso, a function da API envia as gravações para a Azion API e precisa de dois valores para isso: o identificador do banco de dados e um personal token. Armazene os dois como Grave linhas no SQL Database a partir de uma function descreve, com os nomes de chave da API:
Uma variável só chega à function depois de um deploy, então crie as duas antes do deploy feito na configuração da function da API.
Configure a function da API
A function da API é um handler ES Modules que implementa todos os endpoints sob /api/tasks. Ela lê com a classe Database do global Azion.Sql. As gravações dela seguem Grave linhas no SQL Database a partir de uma function, e a lista dela segue Armazene em cache a resposta de uma function com a Cache API, com estes valores:
- Gravações:
writeTasksenvia um statement por chamada, comTASKS_DB_IDeTASKS_SQL_TOKEN, e lança um erro quando a requisição ou o statement falha. - Cache da lista: o cache
tasks-api, a chave<origin>/api/tasksemax-age=60. TodoPOSTe todoDELETEapaga essa chave depois da gravação, então os 60 segundos só limitam por quanto tempo uma lista fica desatualizada quando essa exclusão não roda.
Execute azion init, insira tasks-api como nome e selecione o preset Javascript e o template Hello World, como Faça o deploy de uma function com a Azion CLI mostra. Depois, vá para o diretório do projeto.
Substitua o conteúdo de index.js, o entrypoint que o build lê, pelo código abaixo.
Execute azion deploy no diretório do projeto. O comando constrói o projeto, cria a aplicação e a function, instancia a function e exibe o domínio que a serve.
O código toma mais três decisões:
- As leituras ficam dentro da Azion.
Database.opennão precisa de token, e o parâmetro?carrega o ID da tarefa como inteiro, que a réplica vincula. Passar uma string JavaScript como parâmetro falha comunknown variant `String`. - A exclusão reporta uma tarefa inexistente.
rows_writtenconta as linhas que o statement gravou, então0significa que nenhuma tarefa tinha aquele ID. - A cópia da lista carrega o próprio timestamp.
x-tasks-cached-até definido uma única vez, quando a function guarda a resposta, então duas respostas com o mesmo valor vieram da mesma cópia guardada.
A API responde no domínio que o deploy exibiu, todos os endpoints rodam na function, e a resposta de lista fica em cache por 60 segundos. O primeiro deploy pode levar vários minutos para responder em todas as localidades.
Verifique a configuração
Cada verificação chama a API no domínio dela. Um primeiro deploy que ainda não responde ainda está se propagando; espere alguns minutos e tente de novo.
-
O endpoint de lista lê o banco de dados. Requisite a lista:
A resposta traz
200e as duas linhas que a tabela guarda: -
A lista vem da cópia em cache. Repita a requisição em até 60 segundos. O header
x-tasks-cached-attraz o mesmo valor da primeira resposta. -
Uma gravação chega ao banco de dados. Crie uma tarefa:
ShellA resposta traz
201e o ID que o banco de dados atribuiu:Um
500aqui significa que a gravação falhou. Leia a mensagem que a function registrou no log, como Consultar logs de console de uma function mostra:Azion API answered 401aponta para o valor deTASKS_SQL_TOKEN, e uma mensagem de erro do banco de dados aponta para o statement. -
Uma gravação atualiza a lista. Requisite a lista de novo. A resposta traz um novo valor de
x-tasks-cached-ate três tarefas. -
Uma tarefa inexistente responde 404. Apague a mesma tarefa duas vezes:
A primeira chamada responde
{"message":"Task deleted"}, e a segunda responde404com{"error":"Task not found"}.
Medindo resultados
| Métrica | Onde ler | Como fica quando funciona |
|---|---|---|
| Tempo de resposta da API por região | Average Request Time no dashboard Requests do Real-Time Metrics, filtrado por Host pelo domínio da API e por Country. Consulte Filtre um dashboard de Real-Time Metrics | Comparável entre os países de onde os seus clientes chamam e estável conforme o tráfego cresce |
| Taxa de erros sob carga | HTTP Status Codes 5XX no dashboard Status Codes, filtrado pelo domínio da API. Consulte Dashboards de Build | Nenhuma série de 500 subindo com o volume de requisições; uma subida aponta para gravações que falharam, que a function registra no log |
| Com que frequência o código da API roda | Total Invocations na aba Functions, série Edge Application Invocations. Consulte Dashboards de Build | Acompanha o número de requisições da API, já que toda requisição executa a function |
| Tempo entre uma mudança de código e a produção | O log de deploy que azion deploy indica no Azion Console. Consulte Como a Azion CLI funciona | Cada deploy termina, e o novo código responde assim que se propaga, em cerca de dois minutos para um deploy depois do primeiro |
Boas práticas
-
Leia pela réplica e grave pela API.
Database.openchega a uma read replica sem token e recusa gravações. Enviar as leituras para a API gasta o personal token em toda chamada e adiciona uma requisição que a réplica responde diretamente. Para como a instância principal e as réplicas dividem o trabalho, consulte Como o SQL Database funciona. -
Verifique cada statement, não o status HTTP. Um statement que falha retorna
200comerrorna entrada dele. Um cliente que lê só o status reporta um insert que falhou como sucesso, e o registro nunca é gravado:JavaScript -
Dê à function um token só dela, com uma expiração planejada. Um personal token expira na data escolhida quando ele é criado, e todas as gravações falham a partir desse momento. Um token usado só pela function pode ser substituído e implantado de novo sem mexer no token da CLI. Para as opções de expiração, consulte Personal tokens.
-
Nunca monte um statement a partir do texto bruto da requisição. O endpoint de query recebe strings SQL, então um título que carrega uma aspa muda o statement que o banco de dados executa. Coloque os valores de texto entre aspas e duplique as aspas deles, como
sqlTextfaz, e valide cada campo antes que ele chegue a um statement. -
Guarde em cache só o que todo cliente pode ler. Uma resposta guardada com a Cache API é devolvida a qualquer requisição seguinte com a mesma chave. Guarde em cache respostas de lista e de consulta que são as mesmas para todo cliente, e nunca uma resposta construída a partir das credenciais de um cliente.