Page updated Jan 16, 2024

Override Amplify-generated API Gateway resources

amplify override api
1amplify override api

Run the command above to override Amplify-generated Amazon API Gateway resources.

The command creates a new overrides.ts file under amplify/backend/api/<resource-name>/ which provides you the Amplify-generated resources as CDK constructs.

Apply all the overrides in the override(...) function. For example:

// This file is used to override the REST API resources configuration import { AmplifyApiRestResourceStackTemplate } from '@aws-amplify/cli-extensibility-helper'; export function override(resources: AmplifyApiRestResourceStackTemplate) { resources.restApi.description = "Custom description"; resources.restApi.minimumCompressionSize = 1024; }
1// This file is used to override the REST API resources configuration
2import { AmplifyApiRestResourceStackTemplate } from '@aws-amplify/cli-extensibility-helper';
3
4export function override(resources: AmplifyApiRestResourceStackTemplate) {
5 resources.restApi.description = "Custom description";
6 resources.restApi.minimumCompressionSize = 1024;
7}

To change a field on a particular path, use resources.restApi.body.paths[\<route-path\>]:

export function override(resources: AmplifyApiRestResourceStackTemplate) { // Change the default CORS response header Access-Control-Allow-Origin from "'*'" to the API's domain resources.restApi.body.paths['/items'].options['x-amazon-apigateway-integration'].responses.default.responseParameters['method.response.header.Access-Control-Allow-Origin'] = { 'Fn::Sub': "'https://${ApiId}.execute-api.${AWS::Region}.amazonaws.com'" }; }
1export function override(resources: AmplifyApiRestResourceStackTemplate) {
2 // Change the default CORS response header Access-Control-Allow-Origin from "'*'" to the API's domain
3 resources.restApi.body.paths['/items'].options['x-amazon-apigateway-integration'].responses.default.responseParameters['method.response.header.Access-Control-Allow-Origin'] = { 'Fn::Sub': "'https://${ApiId}.execute-api.${AWS::Region}.amazonaws.com'" };
4}

You can override the following REST API resources that Amplify generates:

Amplify-generated resourceDescription
restApiThe Amazon API Gateway REST API created by amplify add api
deploymentResourceThe deployment resource that deploys the REST API above to a stage.
policiesUser pool group-related IAM policy. Example: resources.policies["/items"].groups["Admin"]

Authorize API requests with Cognito User Pools

Amazon Cognito User Pools is a common service to use alongside API Gateway when adding user Sign-Up and Sign-In to your application. If your application needs to interact with other AWS services such as S3 on behalf of the user who invoked an endpoint, you will need to use IAM credentials with Cognito Identity Pools.

Amplify CLI does not support Cognito User Pool authorizers out-of-the-box. To implement this functionality, you must override your REST API and add a Cognito User Pool authorizer yourself by adding the following code into the override(...) function, in order.

First, assuming the Cognito User Pool you would like to use as an authorizer is the Auth resource configured with your Amplify Project, create a parameter that resolves to its User Pool ARN:

// Replace the following with your Auth resource name const authResourceName = "<your-auth-resource-name>"; const userPoolArnParameter = "AuthCognitoUserPoolArn"; // Add a parameter to your Cloud Formation Template for the User Pool's ID resources.addCfnParameter({ type: "String", description: "The ARN of an existing Cognito User Pool to authorize requests", default: "NONE", }, userPoolArnParameter, { "Fn::GetAtt": [`auth${authResourceName}`, "Outputs.UserPoolArn"], } );
1// Replace the following with your Auth resource name
2const authResourceName = "<your-auth-resource-name>";
3const userPoolArnParameter = "AuthCognitoUserPoolArn";
4
5// Add a parameter to your Cloud Formation Template for the User Pool's ID
6resources.addCfnParameter({
7 type: "String",
8 description: "The ARN of an existing Cognito User Pool to authorize requests",
9 default: "NONE",
10 },
11 userPoolArnParameter,
12 { "Fn::GetAtt": [`auth${authResourceName}`, "Outputs.UserPoolArn"], }
13);

Make sure to replace <your-auth-resource-name> with the name of your auth resource. This is the name of the folder in amplify/backend/auth that was created when you added an Auth resource to your Amplify project.

Now, define a REST API authorizer with Cognito User Pools using the OpenAPI extension, x-amazon-apigateway-authorizer. This change will be applied by modifying the security definition of your REST API:

// Create the authorizer using the AuthCognitoUserPoolArn parameter defined above resources.restApi.addPropertyOverride("Body.securityDefinitions", { Cognito: { type: "apiKey", name: "Authorization", in: "header", "x-amazon-apigateway-authtype": "cognito_user_pools", "x-amazon-apigateway-authorizer": { type: "cognito_user_pools", providerARNs: [ { 'Fn::Join': ['', [{ Ref: userPoolArnParameter }]], }, ], }, }, });
1// Create the authorizer using the AuthCognitoUserPoolArn parameter defined above
2resources.restApi.addPropertyOverride("Body.securityDefinitions", {
3 Cognito: {
4 type: "apiKey",
5 name: "Authorization",
6 in: "header",
7 "x-amazon-apigateway-authtype": "cognito_user_pools",
8 "x-amazon-apigateway-authorizer": {
9 type: "cognito_user_pools",
10 providerARNs: [
11 {
12 'Fn::Join': ['', [{ Ref: userPoolArnParameter }]],
13 },
14 ],
15 },
16 },
17});

Finally, update the security methods for all of the paths in your REST API to use this new Cognito User Pool authorizer. You also add the Authorization header as a parameter on incoming requests for these paths as a place for users to provide their Cognito User ID Tokens.

// For every path in your REST API for (const path in resources.restApi.body.paths) { // Add the Authorization header as a parameter to requests resources.restApi.addPropertyOverride( `Body.paths.${path}.x-amazon-apigateway-any-method.parameters`, [ ...resources.restApi.body.paths[path]["x-amazon-apigateway-any-method"] .parameters, { name: "Authorization", in: "header", required: false, type: "string", }, ] ); // Use your new Cognito User Pool authorizer for security resources.restApi.addPropertyOverride( `Body.paths.${path}.x-amazon-apigateway-any-method.security`, [ { Cognito: [], }, ] ); }
1// For every path in your REST API
2for (const path in resources.restApi.body.paths) {
3 // Add the Authorization header as a parameter to requests
4 resources.restApi.addPropertyOverride(
5 `Body.paths.${path}.x-amazon-apigateway-any-method.parameters`,
6 [
7 ...resources.restApi.body.paths[path]["x-amazon-apigateway-any-method"]
8 .parameters,
9 {
10 name: "Authorization",
11 in: "header",
12 required: false,
13 type: "string",
14 },
15 ]
16 );
17 // Use your new Cognito User Pool authorizer for security
18 resources.restApi.addPropertyOverride(
19 `Body.paths.${path}.x-amazon-apigateway-any-method.security`,
20 [ { Cognito: [], }, ]
21 );
22}

Note that you can add more advanced logic to only use the Cognito User Pool authorizer with some paths or methods.

When performing requests to your REST API, make sure to add the Authorization header with an ID Token provided by Cognito.

Requests to endpoints are now populated with information from Cognito about the user who is invoking the endpoint, and you can reuse the verified ID Token in your endpoint resolvers to assume the identity of the user for accessing other services like AWS AppSync or S3.

Authorize API requests with Lambda authorizer

While Amplify CLI does not support Lambda authorizers natively out-of-box, you can implement this functionality by overriding your REST API resources. The following steps will walk you through how to create token-based Lambda authorizer.

First, you need to have a Lambda authorizer function with required authorization logic in your Amplify project to use it as an authorizer. Refer to the steps to set up a function using amplify add function

After running amplify override api, add the following code to override(...) function. Initially, create a parameter that resolves to Lambda Function ARN

// Replace the following with your Function resource name const functionResourcename = "<your-function-resource-name>"; const functionArnParameter = "FunctionArn"; // Adding parameter to your Cloud Formation Template for Authorizer function arn resources.addCfnParameter( { type: "String", description: "The ARN of an existing Lambda Function to authorize requests", default: "NONE", }, functionArnParameter, { "Fn::GetAtt": [`function${functionResourcename}`, "Outputs.Arn"], } );
1// Replace the following with your Function resource name
2const functionResourcename = "<your-function-resource-name>";
3const functionArnParameter = "FunctionArn";
4
5// Adding parameter to your Cloud Formation Template for Authorizer function arn
6resources.addCfnParameter(
7 {
8 type: "String",
9 description: "The ARN of an existing Lambda Function to authorize requests",
10 default: "NONE",
11 },
12 functionArnParameter,
13 { "Fn::GetAtt": [`function${functionResourcename}`, "Outputs.Arn"], }
14);

Make sure to replace <your-function-resource-name> with the name of your function resource. This is the name of the folder in amplify/backend/function that was created when you added an function resource to your Amplify project.

Next, define the Lambda authorizer using the OpenAPI extension, x-amazon-apigateway-authorizer. This change will be applied by modifying the security definition of your REST API:

// Create the authorizer using the functionArnParameter parameter defined above resources.restApi.addPropertyOverride("Body.securityDefinitions", { Lambda: { type: "apiKey", name: "Authorization", in: "header", "x-amazon-apigateway-authtype": "oauth2", "x-amazon-apigateway-authorizer": { type: "token", authorizerUri: { 'Fn::Join': [ '', [ "arn:aws:apigateway:", { Ref: 'AWS::Region' }, ":lambda:path/2015-03-31/functions/", { Ref: functionArnParameter }, "/invocations" ] ], }, authorizerResultTtlInSeconds: 0 }, }, });
1// Create the authorizer using the functionArnParameter parameter defined above
2resources.restApi.addPropertyOverride("Body.securityDefinitions", {
3 Lambda: {
4 type: "apiKey",
5 name: "Authorization",
6 in: "header",
7 "x-amazon-apigateway-authtype": "oauth2",
8 "x-amazon-apigateway-authorizer": {
9 type: "token",
10 authorizerUri:
11 {
12 'Fn::Join': [
13 '',
14 [
15 "arn:aws:apigateway:",
16 { Ref: 'AWS::Region' },
17 ":lambda:path/2015-03-31/functions/",
18 { Ref: functionArnParameter },
19 "/invocations"
20 ]
21 ],
22 },
23 authorizerResultTtlInSeconds: 0
24 },
25 },
26});

As API Gateway needs permission to invoke the Authorizer lambda function, add resource based policy to the function using following code:

// Adding Resource Based policy to Lambda authorizer function resources.addCfnResource( { type: "AWS::Lambda::Permission", properties: { Action: "lambda:InvokeFunction", FunctionName: {Ref: functionArnParameter}, Principal: "apigateway.amazonaws.com", SourceArn:{ "Fn::Join": [ "", [ "arn:aws:execute-api:", { "Ref": "AWS::Region" }, ":", { "Ref": "AWS::AccountId" }, ":", { "Ref": "<your-API-resource-name>" }, "/*/*" ] ] } } }, "LambdaAuthorizerResourceBasedPolicy" );
1// Adding Resource Based policy to Lambda authorizer function
2resources.addCfnResource(
3 {
4 type: "AWS::Lambda::Permission",
5 properties: {
6 Action: "lambda:InvokeFunction",
7 FunctionName: {Ref: functionArnParameter},
8 Principal: "apigateway.amazonaws.com",
9 SourceArn:{
10 "Fn::Join": [
11 "",
12 [
13 "arn:aws:execute-api:",
14 {
15 "Ref": "AWS::Region"
16 },
17 ":",
18 {
19 "Ref": "AWS::AccountId"
20 },
21 ":",
22 {
23 "Ref": "<your-API-resource-name>"
24 },
25 "/*/*"
26 ]
27 ]
28 }
29 }
30 },
31 "LambdaAuthorizerResourceBasedPolicy"
32);

Make sure to replace <your-api-resource-name> with the name of your Rest API resource. This is the name of the folder in amplify/backend/api that was created when you added an Rest API resource to your Amplify project.

Finally, update the security methods for all of the paths in your REST API to use this new Lambda authorizer. You can also add the Authorization header as a parameter on incoming requests for these paths as a place for users to provide their Auth token.

for (const path in resources.restApi.body.paths) { // Add the Authorization header as a parameter to requests resources.restApi.addPropertyOverride( `Body.paths.${path}.x-amazon-apigateway-any-method.parameters`, [ ...resources.restApi.body.paths[path]["x-amazon-apigateway-any-method"] .parameters, { name: "Authorization", in: "header", required: false, type: "string", }, ] ); // Use your new Lambda authorizer for security resources.restApi.addPropertyOverride( `Body.paths.${path}.x-amazon-apigateway-any-method.security`, [ { Lambda: [], }, ] ); }
1for (const path in resources.restApi.body.paths) {
2 // Add the Authorization header as a parameter to requests
3 resources.restApi.addPropertyOverride(
4 `Body.paths.${path}.x-amazon-apigateway-any-method.parameters`,
5 [
6 ...resources.restApi.body.paths[path]["x-amazon-apigateway-any-method"]
7 .parameters,
8 {
9 name: "Authorization",
10 in: "header",
11 required: false,
12 type: "string",
13 },
14 ]
15 );
16 // Use your new Lambda authorizer for security
17 resources.restApi.addPropertyOverride(
18 `Body.paths.${path}.x-amazon-apigateway-any-method.security`,
19 [ { Lambda: [], }, ]
20 );
21}

Note that you can add more advanced logic to only use the Lambda authorizer with some paths or methods.

When performing requests to your REST API, make sure to add the Authorization header with an token required by Lambda authorizer function.