How To Set Your REST API's Default Rate & Burst Throttling Limits
Setting the burst and rate throttling limits on an AWS API Gateway REST API's Stage without any 3rd-party plugins using Serverless

In this article, we'll look at how one can set the default method burst and rate throttling limits on an AWS API Gateway REST API's Stage without using any 3rd-party plugins or dependencies. We'll assume at least some familiarity with Serverless and AWS CloudFormation.
The Serverless Framework allows us to configure many settings through the provider section of a serverless.yml file. For example, you can configure the usage plan for your API which allows you to set request throttling limits on each API client (API key) you create. However, not all API Gateway settings are exposed by Serverless. Currently, two of these are the API Stage default method burst and rate throttling limits.
AWS API Gateway has two types of throttling-related settings (docs):
Per-client throttling limits which are configured and applied through usage plans which provide API clients with API keys
Global throttling limits which can be applied on a stage and affect all clients—often useful to prevent your API and account from being overwhelmed by too many requests
There are a few Serverless plugins which let you set the stage default throttling limits, but they don't appear to be actively maintained. Moreover, when it comes to infrastructure deployment, I'm generally mistrusting of 3rd-party libraries including Serverless plugins which muck about with the resources that get provisioned during deployment. For example, through a plugin update, a malicious developer could sneak in code which provisions AWS resources on subsequent deployments after the update is installed—all without us even being aware of it. No good. So, I tend to avoid Serverless plugins.
Luckily, Serverless lets us add our own custom CloudFormation configuration in the resources section of any project's Serverless configuration file. This lets us control settings which go beyond what Serverless supports out of the box.
In the rest of this article, we'll look at how we can set the Stage default burst and rate throttling limits via the serverless.yml
's resources section without requiring any 3rd-party dependencies.
Show Me The Code
In order to configure an API Stage's method settings, CloudFormation requires that we create an AWS::ApiGateway::Stage
resource. This presents a minor conflict with the Stage Name in the AWS::ApiGateway::Deployment
resource which is created by Serverless when deploying. To work around this, we need to make use of Serverless' support for JavaScript configuration includes. We need to use JavaScript because the Logical ID (e.g. ApiGatewayDeployment1614957901394
) for the AWS::ApiGateway::Deployment
resource is dynamically generated by Serverless at runtime (using the instanceId
) and we need to know this ID in order to unset the Stage Name from the AWS::ApiGateway::Deployment
resource.
resource-api-gateway-stage.js:
const HttpMethod = '*'
const ResourcePath = '/*'
const ThrottlingBurstLimit = 50 // <-- set your desired throttling value here
const ThrottlingRateLimit = 10 // <-- set your desired throttling value here
module.exports = (serverless) => {
const { instanceId } = serverless
const stage = serverless.getProvider('aws').getStage()
return {
Resources: {
[`ApiGatewayStage${stage}`]: {
Type: 'AWS::ApiGateway::Stage',
Properties: {
RestApiId: {
Ref: 'ApiGatewayRestApi',
},
DeploymentId: {
Ref: `ApiGatewayDeployment${instanceId}`,
},
StageName: stage,
MethodSettings: [
// you can add multiple of the following object if you want to
// customize the settings per HTTP Method or Route
{
HttpMethod,
ResourcePath,
ThrottlingBurstLimit,
ThrottlingRateLimit,
},
],
},
},
[`ApiGatewayDeployment${instanceId}`]: {
Properties: {
// We unset the stage name here so that the Stage Resource
// is created before the Deployment Resource.
// The Deployment Resource then takes the stage name from the Stage Resource.
StageName: { Ref: 'AWS::NoValue' },
},
},
},
}
}
To make use of the resource-api-gateway-stage.js
file, we include it in the resources
section of the serverless.yml
file.
serverless.yml:
resources:
- ${file(./resource-api-gateway-stage.js)}
- Resources:
DynamoDBTable:
Type: AWS::DynamoDB::Table
# ... etc.
- Outputs:
SomeOutput:
Export:
Name: SomeResource
Unfortunately it can't be done only with YAML because the AWS::ApiGateway::Deployment
resource Serverless creates has a dynamically generated Logical ID (e.g. ApiGatewayDeployment1614957901394
) which is unique to each deployment and we need to access/override that resources' StageName
. As far as I'm aware it's not possible to create dynamically generated key names in YAML. I hope someone tells me I'm wrong, because dropping the .js
file would be nicer.
Nonetheless, with the approach I've outlined, we can set a REST API's Stage default burst and rate throttling limits via the serverless.yml
file without requiring any 3rd-party dependencies or Serverless plugins. The additional file may be a bummer on smaller Serverless projects, but most real-world production projects usually make use of multiple configuration files to break down the complexity of a single serverless.yml
file and to support varying configuration targeting different environments. Taking this into account, I find it's and acceptable trade in order to avoid using a plugin.
Caveats
Note 1: Observe the slight change to the serverless.yml
's resources
section when including values from another file. resources
becomes a list of Resources
and Outputs
. Further documentation here.
Note 2: You'll need to include this the first time you deploy the specified stage or you'll get the error ApiGatewayStage - {{your stage's name}} already exists.
Simple fix for that error is to go to the Stage's section in the API Gateway console for your API and deleting that Stage, then redeploying with Serverless. Do be aware that deleting the Stage will make all requests to your endpoint return a 403 Forbidden until re-deployment is complete... watch out in production! 💥
Note 3: Because of the dynamically generated Logical ID for the Deployment Resource, your serverless.yml
file will now always produce a unique hash — which results in Serverless always thinking that your project has changed. This means that Serverless will never be able to terminate the deployment process if all file hashes are actually the same / unchanged from the previous deployment. For me, this is not a problem because.. who cares. 🤷♂️
You'll find further AWS CloudFormation documentation on Stage method settings here.
This article is part of my 30 days / 30 articles challenge where I've attempted to write thirty articles within thirty days.