Stratégies pratiques pour la limitation de débit des API GraphQL

January 4, 2024

Technology

La mise en œuvre de la limitation de débit sur les API REST est relativement simple, car nous utilisons couramment les chemins d'URI pour représenter des ressources API spécifiques et les méthodes HTTP pour indiquer les opérations sur ces ressources. La couche proxy peut facilement appliquer des règles de limitation de débit prédéfinies en fonction de ces informations.

Cependant, le scénario devient nettement plus complexe lorsqu'il s'agit d'API GraphQL. Explorons comment surmonter ces défis.

Scénarios simples

Contrairement aux API REST, GraphQL utilise un langage de requête propriétaire. Il ne repose plus sur les chemins et les méthodes HTTP pour la récupération et la manipulation des ressources, mais unifie l'interrogation et les opérations sur les données sous les termes Query et Mutation, où Query récupère les données, et Mutation effectue des manipulations de données telles que la création, la mise à jour et la suppression.

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) }

Les exemples ci-dessus mettent en évidence le changement dans les méthodes d'interrogation des API. Contrairement à REST, GraphQL ressemble à l'appel de fonctions sur des ressources, en passant les paramètres d'entrée nécessaires, avec la réponse contenant les données interrogées. Outre les différences dans les méthodes d'interrogation, GraphQL expose généralement les API via un seul point de terminaison (par exemple, /graphql), avec les requêtes et les paramètres d'entrée envoyés via le corps de la requête POST.

Considérons l'exemple suivant :

query {
    users {
        fullName
    }
    photos {
        url
        uploadAt
    }
}

Dans ce scénario, simulant la page d'accueil d'un album, un appel API au point de terminaison /graphql interroge à la fois les listes d'utilisateurs et de photos. Maintenant, réfléchissez à savoir si les stratégies traditionnelles de proxy inverse, exécutées au niveau de la requête, restent applicables aux API GraphQL.

La réponse est non. Les serveurs proxy inverse traditionnels ne peuvent pas gérer efficacement les appels d'API GraphQL contenant les requêtes elles-mêmes, rendant impossible l'application de politiques comme la limitation de débit. Pour les API GraphQL, la granularité des "requêtes HTTP" semble trop grossière.

Cependant, la passerelle API, Apache APISIX, intègre une prise en charge native des capacités de GraphQL vers HTTP. Les administrateurs peuvent préconfigurer une instruction de requête, permettant aux clients de l'appeler directement via une requête HTTP POST sans comprendre les détails de GraphQL, en fournissant uniquement les paramètres d'entrée nécessaires. Cela améliore non seulement la sécurité, mais permet également l'application de politiques d'API HTTP dans ce contexte.

rate-limiting

En effet, cela transforme les requêtes GraphQL dynamiques en requêtes statiques pilotées par la connaissance fournies par les fournisseurs d'API, présentant à la fois des avantages et des inconvénients. Parfois, nous pourrions ne pas vouloir sacrifier la fonctionnalité d'interrogation dynamique de GraphQL. Continuons la discussion sur d'autres scénarios.

Scénarios complexes

GraphQL utilise son langage spécialisé pour la modélisation des données et la description des API, permettant des structures de données imbriquées. En développant l'exemple précédent :

query {
    photos(first: 10) {
        url
        uploadAt
        publisher {
            fullName
            avatar
        }
        comments(first: 10) {
            content
            sender {
                fullName
                avatar
            }
        }
    }
    // users...
}

Dans ce cas, simulant la récupération des 10 premières photos, y compris l'éditeur pour chaque photo et les 10 premiers commentaires avec leurs expéditeurs, le service backend doit gérer des requêtes impliquant plusieurs tables de base de données ou des appels à des microservices. Dans de tels scénarios de requêtes imbriquées, à mesure que la quantité de données et les niveaux d'imbrication augmentent, la pression de calcul sur les services backend et les bases de données augmente de manière exponentielle.

Pour empêcher que des requêtes complexes ne submergent le service, nous pourrions vouloir inspecter et bloquer de telles requêtes au niveau du proxy. Pour appliquer cette stratégie, le composant proxy doit analyser les instructions de requête en données structurées, les parcourir pour obtenir les champs imbriqués à chaque niveau, et suivre la pratique courante de GraphQL consistant à attribuer des valeurs de complexité aux champs comme coût de requête. Des limites globales sur la complexité globale de la requête peuvent alors être appliquées. Pour la requête ci-dessus, en supposant un coût de 1 pour chaque champ individuel :

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

Avec un coût total de requête de 340, cela semble acceptable, et nous pouvons configurer des limites de coût de requête d'API en fonction de telles règles. Cependant, si un client malveillant tente de récupérer des données pour 100 photos dans une seule requête, le coût de la requête grimpera à 3400, dépassant la limite prédéfinie, et entraînant un refus de la requête.

Au-delà de la restriction du coût maximum par requête client, des limites supplémentaires sur les intervalles de temps, comme permettre aux clients un total de 2000 requêtes par minute et rejeter les requêtes excédentaires, peuvent contrecarrer les robots malveillants.

Pour implémenter de telles capacités, le composant proxy doit analyser et calculer les coûts de requête. API7 Enterprise prend en charge ces fonctionnalités, permettant l'analyse dynamique des requêtes GraphQL et la mise en œuvre de limites de débit utilisateur basées sur les configurations.

Les API GraphQL posent des défis au niveau de la couche proxy, où les proxies inverses traditionnels peinent à percevoir et gérer efficacement la complexité et les relations d'imbrication dans les instructions de requête GraphQL. En revanche, les technologies de passerelle API s'avèrent inestimables pour surmonter ces défis où API7 Enterprise peut être un excellent choix.

Tags: