Artemiy Davydov
03/18/2022, 8:04 AMPreflightWildcardOriginNotAllowed
I need to set the exact origin for preflight too.
I can get the WebStatic
URL if I create it before the REST API, but then how do I pass the API URL to the frontend?Frank
Artemiy Davydov
03/18/2022, 8:14 AMimport * as sst from "@serverless-stack/resources";
import { TableFieldType } from "@serverless-stack/resources";
import path from "path";
import { CorsHttpMethod } from "@aws-cdk/aws-apigatewayv2-alpha";
export default class CoreStack extends sst.Stack {
constructor(scope: <http://sst.App|sst.App>, id: string, props?: sst.StackProps) {
super(scope, id, props);
const usersTable = new sst.Table(this, "UsersTable", {
fields: {
email: TableFieldType.STRING,
},
primaryIndex: { partitionKey: "email" },
});
const linksTable = new sst.Table(this, "LinksTable", {
fields: {
id: TableFieldType.STRING,
},
primaryIndex: { partitionKey: "id" },
});
const api = new sst.Api(this, "Api", {
cors: {
allowHeaders: ["Origin", "X-Requested-With", "Content-Type", "Accept"],
allowMethods: [
CorsHttpMethod.OPTIONS,
<http://CorsHttpMethod.POST|CorsHttpMethod.POST>,
CorsHttpMethod.GET,
CorsHttpMethod.PUT,
],
allowOrigins: [
"<http://localhost:3000>",
"I NEED TO PUT FRONTEND URL HERE",
],
allowCredentials: true,
},
defaultFunctionProps: {
// See /esbuild.js. This is necessary to support decorators
bundle: {
esbuildConfig: {
plugins: "esbuild-decorators-plugin.js",
},
},
permissions: [usersTable, linksTable],
},
routes: {
"GET /{id}": "src/functions/_id/get/handler.main",
"DELETE /links/{id}": "src/functions/links/_id/delete/handler.main",
"GET /links/{id}": "src/functions/links/_id/get/handler.main",
"PUT /links/{id}": "src/functions/links/_id/put/handler.main",
"POST /links/create": "src/functions/links/create/handler.main",
"GET /links/list": "src/functions/links/list/handler.main",
"GET /auth/redirect": "src/functions/auth/redirect/handler.main",
"GET /users/me": "src/functions/users/me/handler.main",
},
});
const webStatic = new sst.ReactStaticSite(this, "WebStatic", {
path: path.resolve(__dirname, "../../frontend"),
environment: {
REACT_APP_API_URL: api.url,
},
cfDistribution: {
comment: "Distribution for React website",
},
});
const fns = this.getAllFunctions();
const env = {
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID || "",
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET || "",
USERS_TABLE_NAME: usersTable.tableName,
LINKS_TABLE_NAME: linksTable.tableName,
API_URL: api.url,
WEB_URL: webStatic.url,
};
for (const fn of fns) {
for (const key in env) {
fn.addEnvironment(key, env[key as keyof typeof env]);
}
}
this.addOutputs({
ApiEndpoint: api.url,
WebEndpoint: webStatic.url,
});
}
}
Frank
let webStatic;
const api = new sst.Api(this, "Api", {
cors: {
allowHeaders: [ ... ],
allowMethods: [ ... ],
allowOrigins: [
"<http://localhost:3000>",
Lazy.stringValue({
produce(context) {
return webStatic.url;
}
}),
],
allowCredentials: true,
},
...
};
Artemiy Davydov
03/18/2022, 8:18 AMREACT_APP_API_URL
to webStatic now?Frank
Frank
Artemiy Davydov
03/18/2022, 8:37 AMlet webStatic: sst.ReactStaticSite | null = null;
const api = new sst.Api(this, "Api", {
cors: {
allowHeaders: ["Origin", "X-Requested-With", "Content-Type", "Accept"],
allowMethods: [
CorsHttpMethod.OPTIONS,
<http://CorsHttpMethod.POST|CorsHttpMethod.POST>,
CorsHttpMethod.GET,
CorsHttpMethod.PUT,
],
allowOrigins: [
"<http://localhost:3000>",
Lazy.string({
produce() {
return webStatic?.url;
},
}),
],
....
webStatic = new sst.ReactStaticSite(this, "WebStatic", {
path: path.resolve(__dirname, "../../frontend"),
environment: {
REACT_APP_API_URL: api.url,
},
cfDistribution: {
comment: "Distribution for React website",
},
});
produces
Error [ValidationError]: Circular dependency between resources:
Frank
Frank
import { Lazy } from "aws-cdk-lib";
import * as sst from "@serverless-stack/resources";
export class MainStack extends sst.Stack {
constructor(scope: <http://sst.App|sst.App>, id: string) {
super(scope, id);
let site;
// Create Api
const api = new sst.Api(this, "Api", {
cors: {
allowOrigins: [
"<http://localhost:3000>",
Lazy.stringValue({
produce() {
return site.url;
}
})
],
},
routes: {
"GET /": "src/lambda.main",
},
});
// Create StaticSite
site = new sst.ReactStaticSite(this, "Frontend", {
path: "src/sites/react-app",
environment: {
REACT_APP_API_URL: api.url,
},
});
}
}
Frank
sst build
, I’m able to see the CORS setting in the generated Cloudformation template
"ApiCD79AAA0": {
"Type": "AWS::ApiGatewayV2::Api",
"Properties": {
"CorsConfiguration": {
"AllowOrigins": [
"<http://localhost:3000>",
{
"Fn::Join": [
"",
[
"https://",
{
"Fn::GetAtt": [
"FrontendDistributionC0C89627",
"DomainName"
]
}
]
]
}
]
},
Artemiy Davydov
03/18/2022, 9:02 AMhttps://i.imgur.com/P5G7XCz.png▾
Frank
Artemiy Davydov
03/18/2022, 9:18 AMstringValue
instead of string
But I don't have such a method
Property 'stringValue' does not exist on type 'typeof Lazy'
Frank
Artemiy Davydov
03/18/2022, 9:21 AM(Lazy as any).stringValue
Frank
Frank
site.url
as a Lambda environment variable, because CloudFormation can:
1. first create the Api
2. then create the CloudFront site
(with Api’s endpoint)
3. and finally create the Lambda functions
(with site’s url).
https://gist.github.com/fwang/db1e5697913c5533f8b95a4f04464870Frank
Api
setting (not the Lambda function settings). So there is a circular dependency between 1 and 2.Artemiy Davydov
03/18/2022, 9:26 AMArtemiy Davydov
03/18/2022, 9:28 AMArtemiy Davydov
03/18/2022, 9:29 AMFrank
Frank
Artemiy Davydov
03/18/2022, 9:31 AMArtemiy Davydov
03/18/2022, 9:32 AMFrank
Frank