GraphQL-Rate-Limit

A GraphQL Directive For Rate Limiting Your Resolvers

GraphQL Rate Limit

A GraphQL directive to add basic but granular rate limiting to your Queries or Mutations.


Features

  •  Add rate limits to queries or mutations
  • Add filters to rate limits based on the query or mutation args
  • ❌ Custom error messaging
  • ⏰ Configure using a simple max per window arguments
  • Custom stores, use Redis, Postgres, Mongo… it defaults to in-memory
  • Written in TypeScript

Install

yarn add graphql<span class="token operator">-</span>rate<span class="token operator">-</span>limit
JavaScript

Example

directive @<span class="token function">rateLimit</span><span class="token punctuation">(</span>
  max<span class="token punctuation">:</span> Int<span class="token punctuation">,</span> 
  window<span class="token punctuation">:</span> String<span class="token punctuation">,</span>
  message<span class="token punctuation">:</span> String<span class="token punctuation">,</span> 
  identityArgs<span class="token punctuation">:</span> <span class="token punctuation">[</span>String<span class="token punctuation">]</span><span class="token punctuation">,</span> 
<span class="token punctuation">)</span> on FIELD_DEFINITION

type Query <span class="token punctuation">{</span>
  # Rate limit to <span class="token number">5</span> per second
  getItems<span class="token punctuation">:</span> <span class="token punctuation">[</span>Item<span class="token punctuation">]</span> @<span class="token function">rateLimit</span><span class="token punctuation">(</span>window<span class="token punctuation">:</span> <span class="token string">"1s"</span><span class="token punctuation">,</span> max<span class="token punctuation">:</span> <span class="token number">5</span><span class="token punctuation">)</span>

  # Rate limit access per item ID
  <span class="token function">getItem</span><span class="token punctuation">(</span>id<span class="token punctuation">:</span> ID<span class="token operator">!</span><span class="token punctuation">)</span><span class="token punctuation">:</span> Item @<span class="token function">rateLimit</span><span class="token punctuation">(</span>identityArgs<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

type Mutation <span class="token punctuation">{</span>
  # Rate limit <span class="token keyword">with</span> a custom error message
  <span class="token function">createItem</span><span class="token punctuation">(</span>title<span class="token punctuation">:</span> String<span class="token operator">!</span><span class="token punctuation">)</span><span class="token punctuation">:</span> Item @<span class="token function">rateLimit</span><span class="token punctuation">(</span>message<span class="token punctuation">:</span> <span class="token string">"You are doing that too often."</span><span class="token punctuation">)</span>

  # Rate limit access per item<span class="token punctuation">.</span>id
  <span class="token function">updateItem</span><span class="token punctuation">(</span>item<span class="token punctuation">:</span> Item<span class="token operator">!</span><span class="token punctuation">)</span><span class="token punctuation">:</span> Item @<span class="token function">rateLimit</span><span class="token punctuation">(</span>identityArgs<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"item.id"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
JavaScript

Usage

Step 1.

Create a configured GraphQLRateLimit class.

<span class="token keyword">const</span> <span class="token punctuation">{</span> createRateLimitDirective <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'graphql-rate-limit'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// OR</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> createRateLimitDirective <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'graphql-rate-limit'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> GraphQLRateLimit <span class="token operator">=</span> <span class="token function">createRateLimitDirective</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token comment">/**
   * `identifyContext` is required and used to identify the user/client. The most likely cases
   * are either using the context's request.ip, or the user ID on the context.
   * A function that accepts the context and returns a string that is used to identify the user.
   */</span>
  identifyContext<span class="token punctuation">:</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> ctx<span class="token punctuation">.</span>user<span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token comment">// Or could be something like: return ctx.req.ip;</span>
  <span class="token comment">/**
   * `store` is optional as it defaults to an InMemoryStore. See the implementation of InMemoryStore if 
   * you'd like to implement your own with your own database.
   */</span>
  store<span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">MyCustomStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token comment">/**
   * Generate a custom error message. Note that the `message` passed in to the directive will be used 
   * if its set.
   */</span>
  formatError<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> fieldName <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token template-string"><span class="token string">`Woah there, you are doing way too much </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>fieldName<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
JavaScript

Step 2.

Add GraphQLRateLimit to your GraphQL server configuration. Example using Apollo Server:

<span class="token keyword">const</span> server <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ApolloServer</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  typeDefs<span class="token punctuation">,</span>
  resolvers<span class="token punctuation">,</span>
  schemaDirectives<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    rateLimit<span class="token punctuation">:</span> GraphQLRateLimit
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
JavaScript

Note: If you are calling makeExecutableSchema directly and passing in the schema key to ApolloServer or similar, you should do the following:

<span class="token keyword">const</span> schema <span class="token operator">=</span> <span class="token function">makeExecutableSchema</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  typeDefs<span class="token punctuation">,</span>
  resolvers
  schemaDirectives<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    rateLimit<span class="token punctuation">:</span> GraphQLRateLimit
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> graphql <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ApolloServer</span><span class="token punctuation">(</span><span class="token punctuation">{</span> schema <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
JavaScript

Step 3.

Use in your GraphQL Schema.

# This must be added to the top <span class="token keyword">of</span> your schema<span class="token punctuation">.</span>
directive @<span class="token function">rateLimit</span><span class="token punctuation">(</span>
  max<span class="token punctuation">:</span> Int<span class="token punctuation">,</span> 
  window<span class="token punctuation">:</span> String<span class="token punctuation">,</span>
  message<span class="token punctuation">:</span> String<span class="token punctuation">,</span> 
  identityArgs<span class="token punctuation">:</span> <span class="token punctuation">[</span>String<span class="token punctuation">]</span><span class="token punctuation">,</span> 
<span class="token punctuation">)</span> on FIELD_DEFINITION

type Query <span class="token punctuation">{</span>
  # Limit queries to getThings to <span class="token number">10</span> per minute<span class="token punctuation">.</span>
  getThings<span class="token punctuation">:</span> <span class="token punctuation">[</span>Thing<span class="token punctuation">]</span> @<span class="token function">rateLimit</span><span class="token punctuation">(</span>max<span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span> window<span class="token punctuation">:</span> <span class="token string">"6s"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

type Query <span class="token punctuation">{</span>
  # Limit attempts to login <span class="token keyword">with</span> a particular email to <span class="token number">10</span> per <span class="token number">2</span> hours<span class="token punctuation">.</span>
  <span class="token function">login</span><span class="token punctuation">(</span>email<span class="token punctuation">:</span> String<span class="token operator">!</span><span class="token punctuation">,</span> password<span class="token punctuation">:</span> String<span class="token operator">!</span><span class="token punctuation">)</span><span class="token punctuation">:</span> String @<span class="token function">rateLimit</span><span class="token punctuation">(</span>max<span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span> window<span class="token punctuation">:</span> <span class="token string">"2h"</span><span class="token punctuation">,</span> identityArgs<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"email"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
JavaScript

Subscribe to the Newsletter

Get our latest news,tutorials,guides,tips & deals delivered to your inbox.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

shares