diff --git a/cdk/lib/application-layer.ts b/cdk/lib/application-layer.ts index 1023a1f..8ae23d1 100644 --- a/cdk/lib/application-layer.ts +++ b/cdk/lib/application-layer.ts @@ -14,6 +14,7 @@ import { RecordTarget, } from 'aws-cdk-lib/aws-route53'; import { ApiGatewayDomain } from 'aws-cdk-lib/aws-route53-targets'; +import { MealPlannerHttpApi } from './constructs/api'; export interface ApplicationLayerStackProps extends StackProps { readonly delegationRole: Role; @@ -73,31 +74,10 @@ export default class ApplicationLayerStack extends Stack { delegationRole: props.delegationRole, }); - const domainName = ['api', props.domain].join('.'); - const certificate = new Certificate(this, 'ApiDomainCertificate', { - domainName, - validation: CertificateValidation.fromDns(hostedZone), - }); - const api = new LambdaRestApi(this, 'MealPlannerApi', { - handler, - proxy: true, - domainName: { - domainName, - certificate, - basePath: 'mealplanner', - }, - disableExecuteApiEndpoint: true, - }); - - const apiDomainName = api.domainName || api.addDomainName('ApiDomainName', { - domainName, - certificate, - }); - - new ARecord(this, 'ApiAliasRecord', { - zone: hostedZone, - recordName: 'api', - target: RecordTarget.fromAlias(new ApiGatewayDomain(apiDomainName)), + new MealPlannerHttpApi(this, 'ApiConstruct', { + domain: props.domain, + hostedZone, + lambda: handler, }); } } diff --git a/cdk/lib/constructs/api.ts b/cdk/lib/constructs/api.ts new file mode 100644 index 0000000..546556f --- /dev/null +++ b/cdk/lib/constructs/api.ts @@ -0,0 +1,54 @@ +import { Construct } from 'constructs'; +import { DomainName, HttpApi } from 'aws-cdk-lib/aws-apigatewayv2'; +import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations'; +import { IFunction } from 'aws-cdk-lib/aws-lambda'; +import { ARecord, IHostedZone, RecordTarget } from 'aws-cdk-lib/aws-route53'; +import { Certificate, CertificateValidation } from 'aws-cdk-lib/aws-certificatemanager'; +import { ApiGatewayv2DomainProperties } from 'aws-cdk-lib/aws-route53-targets'; + +export interface MealPlannerHttpApiProps { + readonly domain: string; + readonly hostedZone: IHostedZone; + readonly lambda: IFunction; +} + +/** + * Construct for building HttpApi. + */ +export class MealPlannerHttpApi extends Construct { + /** + * Builds an HttpApi. + * @param{Construct} scope the parent scope + * @param{string} id the logical id + * @param{MealPlannerHttpApiProps} props properties + */ + constructor(scope: Construct, id: string, props: MealPlannerHttpApiProps) { + super(scope, id); + const lambda = new HttpLambdaIntegration('LambdaIntegration', props.lambda); + const domainName = ['api', props.domain].join('.'); + const certificate = new Certificate(this, 'ApiDomainCertificate', { + domainName, + validation: CertificateValidation.fromDns(props.hostedZone), + }); + const domain = new DomainName(scope, 'Domain', { + domainName, + certificate, + }); + new ARecord(this, 'ApiAliasRecord', { + zone: props.hostedZone, + recordName: 'api', + target: RecordTarget.fromAlias(new ApiGatewayv2DomainProperties( + domain.regionalDomainName, + domain.regionalHostedZoneId, + )), + }); + new HttpApi(scope, 'Api', { + defaultIntegration: lambda, + defaultDomainMapping: { + domainName: domain, + mappingKey: 'mealplanner', + }, + disableExecuteApiEndpoint: true, + }); + } +} diff --git a/lambda/src/main.rs b/lambda/src/main.rs index 69a9988..c101b2e 100644 --- a/lambda/src/main.rs +++ b/lambda/src/main.rs @@ -43,22 +43,22 @@ async fn main() -> Result<(), Error> { let app = Router::new() .route("/", get(root)) .route("/ping", get(ping)) - .nest("/recipes", recipe_service); - let mealplanner = Router::new().nest("/mealplanner", app).layer( - TraceLayer::new_for_http() - .make_span_with(DefaultMakeSpan::new().include_headers(true)) - .on_request(DefaultOnRequest::new().level(Level::INFO)) - .on_response( - DefaultOnResponse::new() - .level(Level::INFO) - .latency_unit(LatencyUnit::Micros), - ) - .on_failure( - DefaultOnFailure::new() - .level(Level::INFO) - .latency_unit(LatencyUnit::Micros), - ), - ); + .nest("/recipes", recipe_service) + .layer( + TraceLayer::new_for_http() + .make_span_with(DefaultMakeSpan::new().include_headers(true)) + .on_request(DefaultOnRequest::new().level(Level::INFO)) + .on_response( + DefaultOnResponse::new() + .level(Level::INFO) + .latency_unit(LatencyUnit::Micros), + ) + .on_failure( + DefaultOnFailure::new() + .level(Level::INFO) + .latency_unit(LatencyUnit::Micros), + ), + ); - run(mealplanner).await + run(app).await }