Repository: serverless/serverless (JavaScript)
Date: 2017-12-06 18:45:38+00:00
Task type: Feature

Issue:
Amazon API Gateway Endpoint type
# This is a Feature Proposal

## Description
AWS just added endpoint types in API Gateway. Currently, there are two endpoint types you can choose from. Edge-optimized and regional. With Regional you can attach your own CloudFront distribution. I'm not sure if this is supported in CloudFormation yet.

The AWS CLI command to select an endpoint type looks like:

```terminal
aws apigateway create-rest-api \
        --name 'Simple PetStore (AWS CLI, Regional)' \
        --description 'Simple regional PetStore API' \
        --region us-west-2 \
        --endpointConfiguration '{ types: ["REGIONAL"] }'
```

It would be nice to have this as a provider level configuration.


Issue discussion:
@whyvez Can you please use the issue template, shown when creating a new issue, and fill out the resp. sections instead of just using a one-sentence description for an issue? This makes it a lot easier for us to organize incoming requests - may it be bugs or features. Thank you.
@HyperBrain Sorry about that. How does that look?
@whyvez Much better 😄 Thank you for updating.
@whyvez Agree that this would be a great addition! Unfortunately, this is not supported in CloudFormation yet. I propose we keep this issue open until it is supported, but there is a workaround below.

**Manual workaround:**

Another user reported that if you update the configuration of your REST API to a Regional endpoint, the configuration won't be overwritten on subsequent deploys. Deploy your service once, update the config, and it'll stay like that forever 💥 .

Check out the documentation for [updating your REST API endpoint type](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-api-migration.html#migrate-api-using-console) to see the different ways you can change your endpoint type.

+1 for this when it's supported by CloudFormation. Regional Endpoints allow you to use your own CloudFront distribution, so you have more control over things like allowed encryption protocols (we'll need to remove TLS <1.2 from our APIs in the coming months).
This is now supported in CloudFormation, via the `EndpointConfiguration RestApi` parameter. The docs are [here](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-restapi-endpointconfiguration.html).

I'm crucially missing this feature to automate deployment of multi-region API failover. Manual modification is indeed working, but causes deploy to new regions to fail if a custom CF distribution is enabled for the domain.

**Note:** For future issues such as this one, I'd tend to suggest adding a provider option enabling the user to define custom CF parameters not directly supported by Serverless. This would be enough for simple use cases without the hassle of making a plugin for that.
@pierreis Good find 👍 . 
I will have a look. Adding the support should be quite trivial.

The supported values are: A list of endpoint types of an API or its custom domain name. So it also must be possible to set the type to a domain name, but I'd have to test if we have to add a basemapping resource in this case too - otherwise the initial deploy might fail.
`EndpointConfiguration` is a parameter of both `RestApi` and `DomainName`, hence the precision. I'd just suppose that a domain name must be set to regional to be associated with a regional API.

Solution patch:
diff --git a/docs/providers/aws/events/apigateway.md b/docs/providers/aws/events/apigateway.md
index 1852db3d28e..2a229b0e07f 100644
--- a/docs/providers/aws/events/apigateway.md
+++ b/docs/providers/aws/events/apigateway.md
@@ -421,6 +421,27 @@ Please note that those are the API keys names, not the actual values. Once you d
 
 Clients connecting to this Rest API will then need to set any of these API keys values in the `x-api-key` header of their request. This is only necessary for functions where the `private` property is set to true.
 
+### Configuring endpoint types
+
+API Gateway [supports regional endpoints](https://aws.amazon.com/about-aws/whats-new/2017/11/amazon-api-gateway-supports-regional-api-endpoints/) for associating your API Gateway REST APIs with a particular region. This can reduce latency if your requests originate from the same region as your REST API and can be helpful in building multi-region applications.
+
+By default, the Serverless Framework deploys your REST API using the EDGE endpoint configuration. If you would like to use the REGIONAL configuration, set the `endpointType` parameter in your `provider` block.
+
+Here's an example configuration for setting the endpoint configuration for your service Rest API:
+
+```yml
+service: my-service
+provider:
+  name: aws
+  endpointType: REGIONAL
+functions:
+  hello:
+    events:
+      - http:
+          path: user/create
+          method: get
+```
+
 ### Request Parameters
 
 To pass optional and required parameters to your functions, so you can use them in API Gateway tests and SDK generation, marking them as `true` will make them required, `false` will make them optional.
diff --git a/docs/providers/aws/guide/serverless.yml.md b/docs/providers/aws/guide/serverless.yml.md
index e52f887c618..2bb376af946 100644
--- a/docs/providers/aws/guide/serverless.yml.md
+++ b/docs/providers/aws/guide/serverless.yml.md
@@ -39,6 +39,7 @@ provider:
   versionFunctions: false # Optional function versioning
   environment: # Service wide environment variables
     serviceEnvVar: 123456789
+  endpointType: regional # Optional endpoint configuration for API Gateway REST API. Default is Edge.
   apiKeys: # List of API keys to be used by your service API Gateway REST API
     - myFirstKey
     - ${opt:stage}-myFirstKey
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/restApi.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/restApi.js
index 9d180d05047..50cbeb1f8a7 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/restApi.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/restApi.js
@@ -7,11 +7,33 @@ module.exports = {
   compileRestApi() {
     this.apiGatewayRestApiLogicalId = this.provider.naming.getRestApiLogicalId();
 
+    let endpointType = 'EDGE';
+
+    if (this.serverless.service.provider.endpointType) {
+      const validEndpointTypes = ['REGIONAL', 'EDGE'];
+      endpointType = this.serverless.service.provider.endpointType;
+
+      if (typeof endpointType !== 'string') {
+        throw new this.serverless.classes.Error('endpointType must be a string');
+      }
+
+
+      if (!validEndpointTypes.includes(endpointType.toUpperCase())) {
+        const message = 'endpointType must be one of "REGIONAL" or "EDGE". ' +
+                        `You provided ${endpointType}.`;
+        throw new this.serverless.classes.Error(message);
+      }
+      endpointType = endpointType.toUpperCase();
+    }
+
     _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, {
       [this.apiGatewayRestApiLogicalId]: {
         Type: 'AWS::ApiGateway::RestApi',
         Properties: {
           Name: this.provider.naming.getApiGatewayName(),
+          EndpointConfiguration: {
+            Types: [endpointType],
+          },
         },
       },
     });


Test patch:
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/restApi.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/restApi.test.js
index 2ebf517fc1d..d1a066fd330 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/restApi.test.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/restApi.test.js
@@ -15,6 +15,11 @@ describe('#compileRestApi()', () => {
         Type: 'AWS::ApiGateway::RestApi',
         Properties: {
           Name: 'dev-new-service',
+          EndpointConfiguration: {
+            Types: [
+              'EDGE',
+            ],
+          },
         },
       },
     },
@@ -54,4 +59,14 @@ describe('#compileRestApi()', () => {
       );
     })
   );
+
+  it('throw error if endpointType property is not a string', () => {
+    awsCompileApigEvents.serverless.service.provider.endpointType = ['EDGE'];
+    expect(() => awsCompileApigEvents.compileRestApi()).to.throw(Error);
+  });
+
+  it('throw error if endpointType property is not EDGE or REGIONAL', () => {
+    awsCompileApigEvents.serverless.service.provider.endpointType = 'Testing';
+    expect(() => awsCompileApigEvents.compileRestApi()).to.throw(Error);
+  });
 });