Estratégias Práticas para Limitação de Taxa em APIs GraphQL
January 4, 2024
A implementação de limitação de taxa em APIs REST é relativamente simples, pois comumente usamos caminhos de URI para representar recursos específicos da API e métodos HTTP para indicar operações sobre esses recursos. A camada de proxy pode facilmente aplicar regras predefinidas de limitação de taxa com base nessas informações.
No entanto, o cenário se torna significativamente mais complexo quando se trata de APIs GraphQL. Vamos explorar como superar esses desafios.
Cenários Simples
Em contraste com as APIs REST, o GraphQL utiliza uma linguagem de consulta proprietária. Ele não depende mais de caminhos e métodos HTTP para recuperação e manipulação de recursos, mas unifica a consulta e operações de dados sob Query e Mutation, onde Query busca dados e Mutation realiza manipulações como criação, atualização e exclusão.
GET /users
GET /users/1
POST /users
PUT /users/1
DELETE /users/1
query { users { fullName } }
query { user(id: 1) { fullName } }
mutation { createUser(user: { lastName: "Jack" }) { id, fullName } }
mutation { updateUser (id: 1, update: { lastName: "Marry" }) { fullName } }
mutation { deleteUser (id: 1) }
Os exemplos acima destacam a mudança nos métodos de consulta de API. Diferente do REST, o GraphQL se assemelha a chamar funções em recursos, passando parâmetros de entrada necessários, com a resposta contendo os dados consultados. Além das diferenças nos métodos de consulta, o GraphQL expõe APIs normalmente através de um único endpoint (por exemplo, /graphql), com consultas e parâmetros de entrada enviados via corpo POST.
Considere o seguinte exemplo:
query {
users {
fullName
}
photos {
url
uploadAt
}
}
Neste cenário, simulando a página inicial de um álbum, uma chamada de API para o endpoint /graphql
consulta simultaneamente as listas de usuários e fotos. Agora, considere se as estratégias tradicionais de proxy reverso, executadas no nível da solicitação, ainda são aplicáveis a APIs GraphQL.
A resposta é não. Servidores de proxy reverso tradicionais não conseguem lidar efetivamente com chamadas de API GraphQL que contêm as próprias consultas, tornando impossível aplicar políticas como limitação de taxa. Para APIs GraphQL, a granularidade de "solicitações HTTP" parece muito ampla.
No entanto, o gateway de API, Apache APISIX, incorpora suporte nativo para capacidades de GraphQL para HTTP. Administradores podem pré-configurar uma declaração de consulta, permitindo que clientes a chamem diretamente através de um POST HTTP sem entender detalhes do GraphQL, apenas fornecendo os parâmetros de entrada necessários. Isso não apenas aumenta a segurança, mas também permite a aplicação de políticas de API HTTP neste contexto.
Efetivamente, isso transforma consultas dinâmicas do GraphQL em consultas estáticas fornecidas pelos provedores de API, apresentando vantagens e desvantagens. Às vezes, podemos não querer sacrificar o recurso de consulta dinâmica do GraphQL. Vamos continuar a discussão de outros cenários.
Cenários Complexos
O GraphQL utiliza sua linguagem especializada para modelagem de dados e descrição de APIs, permitindo estruturas de dados aninhadas. Expandindo o exemplo anterior:
query {
photos(first: 10) {
url
uploadAt
publisher {
fullName
avatar
}
comments(first: 10) {
content
sender {
fullName
avatar
}
}
}
// users...
}
Neste caso, simulando a recuperação das primeiras 10 fotos, incluindo o editor de cada foto e os primeiros 10 comentários com seus remetentes, o serviço de backend deve lidar com consultas envolvendo várias tabelas de banco de dados ou chamadas a microsserviços. Em cenários de consulta aninhada, à medida que a quantidade de dados e os níveis de aninhamento aumentam, o estresse computacional sobre os serviços de backend e bancos de dados aumenta exponencialmente.
Para evitar que consultas complexas sobrecarreguem o serviço, podemos querer inspecionar e bloquear tais consultas na camada de proxy. Para aplicar essa estratégia, o componente de proxy deve analisar as declarações de consulta em dados estruturados, percorrê-las para obter campos aninhados em cada camada e seguir a prática comum do GraphQL de atribuir valores de complexidade aos campos como custos de consulta. Limites globais no custo total da consulta podem então ser aplicados. Para a consulta acima, assumindo um custo de 1 para cada campo individual:
10 * photo (url + uploadAt + publisher.fullName + publisher.avatar + 10 * comment (content + sender.fullName + sender.avatar))
10 * (1 + 1 + 1 + 1 + 10 * (1 + 1 + 1)) = 340
Com um custo total de consulta de 340, parece aceitável, e podemos configurar limites de custo de consulta de API com base nessas regras. No entanto, se um cliente malicioso tentar buscar dados para 100 fotos em uma única consulta, o custo da consulta subirá para 3400, ultrapassando o limite predefinido e resultando em uma solicitação negada.
Além de restringir o custo máximo por consulta do cliente, limites adicionais em intervalos de tempo, como permitir um total de 2000 consultas por minuto e rejeitar consultas excedentes, podem frustrar rastreadores maliciosos.
Para implementar tais capacidades, o componente de proxy deve analisar e calcular os custos das consultas. A API7 Enterprise suporta esses recursos, permitindo a análise dinâmica de consultas GraphQL e a implementação de limites de taxa com base em configurações.
APIs GraphQL enfrentam desafios na camada de proxy, onde proxies reversos tradicionais lutam para perceber e lidar efetivamente com a complexidade e relações de aninhamento nas declarações de consulta do GraphQL. Em contraste, tecnologias de gateway de API provam ser inestimáveis para superar esses desafios, onde a API7 Enterprise pode ser uma ótima escolha.