I have following AWS CDK backed solution:
Static S3 based webpage which communicates with
API Gateway which then sends data to
AWS lambda.
The problem is that S3 page needs to be aware of API gateway endpoint URL.
Obviously this is not achievable within the same CDK stack. So I have defined two stacks:
Backend (API gateway + lambda)
Frontend (S3 based static webpage)
They are linked as dependant in CDK code:
const app = new cdk.App();
const backStack = new BackendStack(app, 'Stack-back', {...});
new FrontendStack(app, 'Stack-front', {...}).addDependency(backStack, "API URL from backend is needed");
I try to share URL as follows.
Code from backend stack definition:
const api = new apiGW.RestApi(this, 'MyAPI', {
restApiName: 'My API',
description: 'This service provides interface towards web app',
defaultCorsPreflightOptions: {
allowOrigins: apiGW.Cors.ALL_ORIGINS,
}
});
api.root.addMethod("POST", lambdaIntegration);
new CfnOutput(this, 'ApiUrlRef', {
value: api.url,
description: 'API Gateway URL',
exportName: 'ApiUrl',
});
Code from frontend stack definition:
const apiUrl = Fn.importValue('ApiUrl');
Unfortunately, instead of URL I get token (${Token[TOKEN.256]}). At the same time, I see URL is resolved in CDK generated files:
./cdk.out/Stack-back.template.json:
"ApiUrlRef": {
"Description": "API Gateway URL",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "MyAPI7DAA778AA"
},
".execute-api.us-west-1.",
{
"Ref": "AWS::URLSuffix"
},
"/",
{
"Ref": "MyAPIDeploymentStageprodA7777A7A"
},
"/"
]
]
},
"Export": {
"Name": "ApiUrl"
}
}
},
What I'm doing wrong?
UPD:
After advice of fedonev to pass data as props, situation did not changed much. Now url looks like that:
"https://${Token[TOKEN.225]}.execute-api.us-west-1.${Token[AWS.URLSuffix.3]}/${Token[TOKEN.244]}/"
I think important part I missed (which was also pointed by
Milan Gatyas) is how I create HTML with URL of gateway.
In my frontend-stack.ts, I use template file. After template is filled, I store it in S3:
const filledTemplatePath: string = path.join(processedWebFileDir,'index.html');
const webTemplate: string = fs.readFileSync(filledTemplatePath, 'utf8')
const Handlebars = require("handlebars")
let template = Handlebars.compile(webTemplate)
const adjustedHtml: string = template({ apiGwEndpoint: apiUrl.toString() })
fs.writeFileSync(filledTemplatePath, adjustedHtml)
// bucket
const bucket: S3.Bucket = new S3.Bucket(this, "WebsiteBucket",
{
bucketName: 'frontend',
websiteIndexDocument: 'index.html',
websiteErrorDocument: 'error.html',
publicReadAccess: true,
})
new S3Deploy.BucketDeployment(this, 'DeployWebsite', {
sources: [S3Deploy.Source.asset(processedWebFileDir)],
destinationBucket: bucket,
});
(I'm new to TS and web, please don't judge much :) )
Am I correct that S3 is populated on synth, deploy does not change anything and this is why I get tokens in html?
Will be grateful for a link or explanation so that I could understand the process better, there are so much new information to me that some parts are still quite foggy.
As #fedonev mentioned, the tokens are just placeholder values in the TypeScript application. CDK app replaces tokens with intrinsic functions when the CloudFormation template is produced.
However, your use case is different. You try to know the information inside the CDK app which is available only at synthesis time, and you can't use the intrinsic function to resolve the URL while being in CDK app to write to file.
If possible you can utilize the custom domain for the API Gateway. Then you can work with beforehand known custom domain in your static file and assign the custom domain to the API Gateway in your CDK App.
[Edit: rewrote the answer to reflect updates to the OP]
Am I correct that S3 is populated on synth, deploy does not change anything and this is why I get tokens in html?
Yes. The API URL will resolve only at deploy-time. You are trying to consume it at synth-time when you write to the template file. At synth-time, CDK represents not-yet-available values as Tokens like ${Token[TOKEN.256]}, the CDK's clever way of handling such deferred values.
What I'm doing wrong?
You need to defer the consumption of API URL until its value is resolved (= until the API is deployed). In most cases, passing constructs as props between stacks is the right approach. But not in your case: you want to inject the URL into the template file. As usual with AWS, you have many options:
Split the stacks into separate apps, deployed separately. Deploy BackendStack. Hardcode the url into FrontendStack. Quick and dirty.
Instead of S3, use Amplify front-end hosting, which can expose the URL to your template as an environment variable. Beginner friendly, has CDK support.
Add a CustomResource construct, which would be backed by a Lambda that writes the URL to the template file as part of the deploy lifecycle. This solution is elegant but not newbie-friendly.
Use a Pipeline to inject the URL variable as a build step during deploy. Another advanced approach.
Related
Introduction
I have some TypeScript code that uses CDK to create an API Gateway and a Lambda. It works and deploys to a standard AWS URL. So far so good.
I now need to transfer the API Gateway so that it operates on a custom domain, so that it can set a cookie in a web app. This is proving far harder, and I suspect I am having difficulty because I am new to TypeScript, AWS, and CDK all at the same time. There are a number of documentation resources on the web, but most would require me to rewrite the precious little working code I have, which I am reluctant to do.
I have created a certificate manually, because that requires validation and thus it does not make sense to create it in code. Other than that I want all other resources to be created by CDK code in a Stack. In my view, it defeats the purpose of CDK if I have to configure things manually.
Problem
The below code deploys everything I need to gatekeeper.d.aws.example.com - a HostedZone, an ARecord, a LambdaRestApi and a Function (lambda). However it does not work because the NS records newly assigned to gatekeeper.d.aws.example.com do not match the ones in the parent d.aws.example.com.
I think this means that although d.aws.example.com is "known", the gateway subdomain cannot delegate to it.
Here is my working code:
// Create the lambda resource
const referrerLambda = new lambda.Function(this, 'EisReferrerLambda', {
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '../../src/lambda')),
environment: env
});
// Set up the domain name on which the API should appear
const domainName = 'gatekeeper.d.aws.example.com';
// TODO need to fetch it with an env var? Or read from environment?
const certificateArn = 'arn:aws:acm:us-east-1:xxx:certificate/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy';
const certificate = acm.Certificate.fromCertificateArn(this, 'SslCertificate', certificateArn);
const hostedZone = new route53.HostedZone(this, 'EisReferrerHostedZone', {
zoneName: domainName
});
// Add an A record
new route53.ARecord(this, 'DnsRecord', {
zone: hostedZone,
target: route53.RecordTarget.fromAlias(new targets.ApiGateway(apiGateway)),
});
// I think I need a DomainNameOptions object
const dno : DomainNameOptions = { certificate, domainName };
// Create the APIG resource
// See https://intro-to-cdk.workshop.aws/the-workshop/4-create-apigateway.html
const apiGateway = new apigw.LambdaRestApi(this, "EisReferrerApi", {
handler: referrerLambda,
// proxy = on means that the lambda handles all requests to the APIG,
// instead of just explicit resource endpoints
proxy: false,
// deploy = on means that we get a default stage of "prod", I don't want
// that - I'm creating a custom Deployment anyway
deploy: false,
// Point to a domain name options object
domainName: dno
});
// Create an endpoint in the APIG
// https://docs.aws.amazon.com/cdk/api/latest/docs/aws-apigateway-readme.html#defining-apis
const items = apiGateway.root.addResource('gatekeeper');
items.addMethod('GET'); // GET /default/gatekeeper
// The deployment resource is just needed by the Stage system
const deployment = new apigw.Deployment(
this,
'EisReferrerDeployment',
{ api: apiGateway }
);
// Create a Stage (this affects the first component in the path
const stageName = 'default';
apiGateway.deploymentStage = new apigw.Stage(
this,
stageName,
{ deployment, stageName }
);
Question
As you can see from the code, I've found how to create an A record, but creating/modifying NS records seems harder. For a start, there does not seem to be an NSRecord class, at least based on exploring the class structure from my IDE autocomplete.
A rudimentary solution would allow me to create NS records with the fixed values that are set up elsewhere (in the AWS account that "owns" the domain). A better solution would be to read what those records are, and then use them.
Update
To see if my thinking is on the right track, I have run this deployment code, and manually modified the automatically assigned NS records in the HostedZone to match the records in the parent (in the other account). I think I have to wait for this change to seep into the DNS system, and I will update with the result.
Update 2
My manual adjustment did not work. I have therefore found a new thing to try (see "To add a NS record to a HostedZone in different account"):
// Commented out from earlier code
// const hostedZone = new route53.HostedZone(this, 'EisReferrerHostedZone', {
// zoneName: domainName
// });
// In the account containing the HostedZone
const parentZone = new route53.PublicHostedZone(this, 'HostedZone', {
zoneName: 'd.aws.example.com',
crossAccountZoneDelegationPrincipal: new iam.AccountPrincipal('12345678012')
});
// In this account
const subZone = new route53.PublicHostedZone(this, 'SubZone', {
zoneName: domainName
});
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneId: parentZone.hostedZoneId,
delegationRole: parentZone.crossAccountDelegationRole
});
This sounds exactly what I need, but I fear the AWS documentation is out of date here - crossAccountDelegationRole is rendered in red in my IDE, and it crashes due to being undefined when cdk diff is run.
Update 3
I am assuming the property mentioned above is a typo or a reference to an outdated version of the library. I am now doing this:
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneId: parentZone.hostedZoneId,
delegationRole: parentZone.crossAccountZoneDelegationRole
});
This feel tantalisingly close, but it crashes:
Failed to create resource. AccessDenied: User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/CustomCrossAccountZoneDelegationC-xxx is not authorized to
perform: sts:AssumeRole on resource: arn:aws:iam::yyyyyyyyyyyy:role/HostedZoneCrossAccountZoneDelegat-yyy
I wonder if I need to declare the IAM creds for the other account? I do have them.
I am not sure why permissions are needed, anyway - could it not just read the NS records in the other account and copy them to the local account? The DNS in the other account is public anyway.
I am willing to research fixing the IAM error, but this doesn't half feel like shooting in the dark. I might spend another two hours inching towards solving that sub-problem, only to find that the whole thing will fail for another reason.
Update 4
I have created a "Role" in the remote account to give "AmazonRoute53FullAccess" perms to the account that I am targetting for CDK deployment. However I still get the AccessDenied error. I wonder if I need to explicitly invoke that remote role in some fashion; how can I do that?
Was trying to do the same thing today & your post got me 90% of the way there, thanks! I ended up getting it to work with a different IAM principal (Organization) which was ok for my use case.
The crossAccountZoneDelegationPrincipal gives access to accounts hosting subzones, to access your root zone and write delegation (NS) records for the subzones.
For my use case, all the accounts resided within the same organization, so I created my root zone like this ->
const rootZone = new route53.PublicHostedZone(this, 'rootZone', {
zoneName: `root.zone`,
crossAccountZoneDelegationPrincipal: new iam.OrganizationPrincipal('o-####')
});
This sets up an IAM role with the following policy;
"Version": "2012-10-17",
"Statement": [
{
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/#####",
"Effect": "Allow"
}
]
}
And the following trust policy;
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-####"
}
}
}
]
}
Which effectively allows anyone with that OrgID to write records in the root zone.
In my subzones, I run with this;
const subZone = new route53.PublicHostedZone(this, 'SubZone', {
zoneName: 'sub.root.zone'
});
const delegationRole = iam.Role.fromRoleArn(this, 'delegationRole', 'arn:aws:iam::###:role/###')
new route53.CrossAccountZoneDelegationRecord(this, 'delegate', {
delegatedZone: subZone,
parentHostedZoneId: '###',
delegationRole: delegationRole
});
This ended up creating the delegation records in the root zone, for my subzone. If the organization principal doesn't fit your use case and you still need to grant multiple accounts that authority, try the composite principal https://docs.aws.amazon.com/cdk/api/latest/docs/#aws-cdk_aws-iam.CompositePrincipal.html
Also wanted to address the concerns raised in the other answer around it being an anti-pattern & cross-account CDK being hard. This isn't really cross account CDK. This is utilizing a pattern provided by AWS (specifically spinning up a lambda to execute the provisioning of subzone records in a root zone).
Hopefully it works for you!
I have consulted two AWS experts, and they do not favour cross-account operations. One said:
This is an anti-pattern, since it requires permissions to remain even after the stack is deployed. What happens if a cross-account operation has to roll back and that permission is revoked? It would result in the app being stuck in a middle/undefined state (the local part is rolled back, but the remote part cannot be rolled back due to an access violation).
The other advised:
Cross-account CDK is hard.
It is much better to split your stacks into two or more operations, so you can run them independently. This applies nicely to "one off" operations like DNS delegation - realistically you are not going to change the zone delegation for your Stack unless you destroy it, which you are not going to do until you actually don't need it. Thus, there is no reason for the zone information to change for the lifetime of the system.
This also works well where you have an app and a database, and you want the ability to take down your app without destroying the data.
So, this is an answer in the sense that some folks will say "don't do it". However, it looks like AWS has the ability to do it, so answers in that direction are still welcome.
I used the accepted answer to create this GitHub project which implements a proof-of-concept:
https://github.com/adamcbuckley/CdkCloudfrontUsingDnsFromAnotherAccount
This sample project demonstrates how to use the AWS CDK serve static web content using a Cloudfront distribution. The Cloudfront distribution is given a domain name and an HTTPS certificate, even though the Route 53 hosted zone is owned by a different AWS Account (the parent account).
I wan to verify an email address from my CDK itself so that when my stack is deployed tto some other regions this verification is automatically triggered, rather than going to AWS console and doing it manually.
You can either do that using AwsCustomResource from #aws-cdk/custom-resources and it looks similar to the example that you can find here for validating a domain: Custom Resource Examples.
Verify email using TypeScript
I'm adjusting the example here for your use case:
const verifyDomainIdentity = new AwsCustomResource(this, 'VerifyDomainIdentity', {
onCreate: {
service: 'SES',
action: 'verifyEmailIdentity',
parameters: {
EmailAddress: 'your#example.com'
},
physicalResourceId: PhysicalResourceId.of('verify-email-address')
},
policy: AwsCustomResourcePolicy.fromSdkCalls({resources: AwsCustomResourcePolicy.ANY_RESOURCE}) // This does not work somehow with SES or maybe I did something wrong :-(
});
Unfortunately this does not work out of the box because somehow the generated policy includes an email: prefix instead of ses: and you need to provide your own policy. But there's an alternative below.
Using an existing CDK Construct with TypeScript
The other alternative is to use a CDK Construct which is already doing that for you. I recently ran into the same problem like you and I've published a CDK Construct for that: ses-verify-identities. You can then do it like this:
new VerifySesEmailAddress(this, 'SesEmailVerification', {
emailAddress: 'hello#example.org'
});
You can find the source code of the CDK construct here in case you are interested. The same is possible for verifying domains.
I want to be able to call a API gateway endpoint from within cloudformation and parse the response from the output and pass in relevant information to one of the other service in the cloudformation.
I have an api endpoint
https://123x123x.execute-api.eu-west-2.amazonaws.com/myendpoint/tenants
with
x-api-key: b8Yk6m63rq8XRnMDKa2PeWE3KvBcU7ZyFIn0Vvrty
Content-Type: application/json
which returns
{
"tenants": [
{
"tenantId": "tenant-1234",
"AZ": "us-west-2c",
"tenantUsers": 24,
"instanceType": "m1.small"
},
{
"tenantId": "tenant-2345",
"AZ": "us-west-2b",
"tenantUsers": 32,
"instanceType": "t2.micro"
},
{
"tenantId": "tenant-3456",
"AZ": "us-west-2a",
"tenantUsers": 12
"instanceType": "m1.large"
}
]}
I want to be able to set the InstanceTypeParameter which needs to be a list ["t2.micro", "m1.small", "m1.large"] retrieved from the above response and passed in as parameter in cloudformation as below.
"Ec2Instance" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"InstanceType" : { "Ref" : "InstanceTypeParameter" },
"ImageId" : "ami-0ff8a91507f77f867"
}
}
I am assuming the only way to do this would be using a custom resource. Can someone help me develop that (atleast a pseudocode)?
You are correct, it must be a custom resource. Below I will provide general steps which can be fallowed to achieve your aim.
Develop a standalone lambda function. Just plain, regular function for now, which is going to call the API, gets its response, parse it prepare result you require based on input parameters you will provide. The aim is to test how such lambda function will work. Its like a blue-print for a custom resource to be developed.
Once you know how the lambda function will work, its time to prepare a custom resource. I recommend creating a new function for that using custom-resource-helper. The helper simplifies a lot development of custom resources. To use it, you will have to prepare a zip deployment package to bundle it with your function handler. Since you know from step 1 exactly how your function should work from step 1, you need to amend it to work in context of the helper. Adding modified the code into def create(event, context) of the helper should be enough. delete(event, context) can be empty as you are not creating any new physical resource in AWS. update(event, context) its up to you want to do with that.
Once you deploy your custom resource lambda, its time to actually create a custom resource in your CFN tempalte. General form is as follows:
MyGetExternalApiResponseResource:
Type: Custom::CallExternalAPI
Version: "1.0"
Properties:
ServiceToken: <ARN of function from step 2>
InputParameterToFunction1: <for example, api key>
InputParameterToFunction2: <for example, url of api to call>
Lots of debugging and troubleshooting. It will almost center not work first time.
Once it works, you can get return values from the custom resource, using either !Ref MyGetExternalApiResponseResource or !GetAtt MyGetExternalApiResponseResource.InstanceTypeParameter. Depends which way you prefare. Second way would be better probably, as the custom resource doesn't create physical resource. Usually !Ref would be used for id of physical resource created, e.g. id of an AMI, id of an instance.
To fully automate it, you would also deploy the code for custom lambda as a CFN template, instead of doing this manually. In this scenario your template would both create a custom resource lambda function, and a custom resource itself using the function.
I am trying to create an app that uses AWS Services, I already use Cognito plugin for flutter but can't get it to work with DynamoDB, should I use a lambda function and point to it or is it possible to get data form a table directly from flutter, if that's the case which URL should I use?
I am new in AWS Services don’t know if is it possible to access a dynamo table with a URL or I should just use a lambda function
Since this is kind of an open-ended question and you mentioned Lambdas, I would suggest checking out the Serverless framework. They have a couple of template applications in various languages/frameworks. Serverless makes it really easy to spin up Lambdas configured to an API Gateway, and you can start with the default proxy+ resource. You can also define DynamoDB tables to be auto-created/destroyed when you deploy/destroy your serverless application. When you successfully deploy using the command 'serverless deploy' it will output the URL to access your API Gateway which will trigger your Lambda seamlessly.
Then once you have a basic "hello-word" type API hosted on AWS, you can just follow the docs along for how to set up the DynamoDB library/sdk for your given framework/language.
Let me know if you have any questions!
-PS: I would also, later on, recommend using the API Gateway Authorizer against your Cognito User Pool, since you already have auth on the Flutter app, then all you have to do is pass through the token. The Authorizer can also be easily set up via the Serverless Framework! Then your API will be authenticated at the Gateway level, leaving AWS to do all the hard work :)
If you want to read directly from Dynamo It is actually pretty easy.
First add this package to your project.
Then create your models you want to read and write. Along with conversion methods.
class Parent {
String name;
late List<Child> children;
factory Parrent.fromDBValue(Map<String, AttributeValue> dbValue) {
name = dbValue["name"]!.s!;
children = dbValue["children"]!.l!.map((e) =>Child.fromDB(e)).toList();
}
Map<String, AttributeValue> toDBValue() {
Map<String, AttributeValue> dbMap = Map();
dbMap["name"] = AttributeValue(s: name);
dbMap["children"] = AttributeValue(
l: children.map((e) => AttributeValue(m: e.toDBValue())).toList());
return dbMap;
}
}
(AttributeValue comes from the package)
Then you can consume dynamo db api as per normal.
Create Dynamo service
class DynamoService {
final service = DynamoDB(
region: 'af-south-1',
credentials: AwsClientCredentials(
accessKey: "someAccessKey",
secretKey: "somesecretkey"));
Future<List<Map<String, AttributeValue>>?> getAll(
{required String tableName}) async {
var reslut = await service.scan(tableName: tableName);
return reslut.items;
}
Future insertNewItem(Map<String, AttributeValue> dbData, String tableName) async {
service.putItem(item: dbData, tableName: tableName);
}
}
Then you can convert when getting all data from dynamo.
List<Parent> getAllParents() {
List<Map<String, AttributeValue>>? parents =
await dynamoService.getAll(tableName: "parents");
return parents!.map((e) =>Parent.fromDbValue(e)).toList()
}
You can check all Dynamo operations from here
I'm trying to set path parameters when making calls to API Gateway endpoints via the JavaScript SDK and not having any luck. It looks like I either have something misconfigured or there is a bug in the SDK generation.
I am able to successfully call endpoints that do not take path parameters but when I try to pass in a parameter to be used as a path parameter the SDK just replaces the path parameter with a blank and my call fails.
Example, assume client is a properly initialized API Gateway client. I have an endpoint called /measurement with a child of /measurement/{id}. I am able to call both directly.
client.measurementGet({},{}); - successfully calls my /measurement endpoint
client.measurementIdGet({"id": "1234"}, {}); - Browser makes a call to /measurement/ instead of /measurement/1234
Looking at the source of my apigClient.js, it appears that the SDK generator is not putting path parameters into the list of parameters that it's looking for. For example, the code of my generated measurementIdGet method looks like this:
apigClient.measurementIdGet = function (params, body, additionalParams) {
if(additionalParams === undefined) { additionalParams = {}; }
apiGateway.core.utils.assertParametersDefined(params, [], ['body']);
var measurementIdGetRequest = {
verb: 'get'.toUpperCase(),
path: pathComponent + uritemplate('/measurement/{id}').expand(apiGateway.core.utils.parseParametersToObject(params, [])),
headers: apiGateway.core.utils.parseParametersToObject(params, []),
queryParams: apiGateway.core.utils.parseParametersToObject(params, []),
body: body
};
return apiGatewayClient.makeRequest(measurementIdGetRequest, authType, additionalParams, config.apiKey);
};
I dug into the assertParametersDefined and parseParametersToObject and it looks like those methods are expecting a list of parameters to look for. In both cases the SDK has generated empty lists instead of putting my path parameter in there.
If I manually update the generated file to change the two lines to
apiGateway.core.utils.assertParametersDefined(params, ['id'], ['body']);
and
apiGateway.core.utils.parseParametersToObject(params, ['id'])
The SDK makes the proper call.
Am I missing something in my configuration or is there a bug in the code generator?
If you are using cloud formation like me. You will need to add it in the RequestParameters.
for a resource like this /api/pets/{id}/attributes/{attrid} following code works
PetsByIdAttributesByAttridGetMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
RestApiId: !Ref MyApi
ResourceId: !Ref PetsByIdAttributesByAttridResource
HttpMethod: GET
AuthorizationType: AWS_IAM
RequestParameters:
method.request.path.id : true
method.request.path.attrid : true
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambda.Arn}/invocations
I know it's an old question, but for those new to AWS that may still run into this issue like I did, I hope this answer helps:
In the AWS API Gateway Console, make sure that you specify the "URL Query String Parameters" in the "Method Request" for your "GET". Then re-deploy your API and generate the SDK again. This time the apigClient.js will be generated with the defined Query Parameter keys correctly filled in to the calls to assertParametersDefined and parseParametersToObject.
Assuming that you're importing a swagger definition to create the API, defining your parameters at the method level as opposed to the path level will result in a generated SDK with the key filled out and should work correctly.
{
...
"/path/{to}/resource": {
"get": {
"parameters": [ // define here
"name": "to",
"in": "path",
...
],
...
},
"parameters": [] // not here
}
Although defining parameters at the path level is correct according the the Swagger spec and API Gateway does use them in the created API, it appears that API Gateway disregards them in some contexts.
This look's like an issue doesn't parse the params.
https://github.com/aws/chalice/issues/498enter link description here