200.Land

api-cdk.ts

The TypeScript code uses AWS CDK to create an AWS API Gateway with a set of predefined REST API resources (like "signup", "login") each supporting specific HTTP methods. It sets up CORS, associates the API with a custom domain using a DNS certificate, and creates a DNS record in Route 53. The API integrates with a Lambda function handler and optionally protects resources with a Cognito User Pools authorizer. It configures a usage plan to throttle the API requests to a specified rate limit.

import {
  AuthorizationType,
  CognitoUserPoolsAuthorizer,
  Cors,
  CorsOptions,
  EndpointType,
  LambdaRestApi,
} from "aws-cdk-lib/aws-apigateway";
import { IFunction } from "aws-cdk-lib/aws-lambda";
import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager";
import { Construct } from "constructs";
import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53";
import { ApiGateway } from "aws-cdk-lib/aws-route53-targets";

export class ApiGateWay extends Construct {
  constructor(
    scope: Construct,
    id: string,
    private handler: IFunction,
    private authorizer: CognitoUserPoolsAuthorizer,
  ) {
    super(scope, id);
    this.configureResources();
  }

  private corsOptions: CorsOptions = {
    allowOrigins: Cors.ALL_ORIGINS,
    allowHeaders: [...Cors.DEFAULT_HEADERS, "access-token", "accept-encoding", "accept"],
    allowMethods: Cors.ALL_METHODS,
  };

  private rootDomainName = "200.land";
  private apiDomainName = `api.${this.rootDomainName}`;

  public api = new LambdaRestApi(this, "RestApi", {
    handler: this.handler,
    deployOptions: { stageName: `TwoHunredLandApi-api-prod` },
    defaultCorsPreflightOptions: this.corsOptions,
    proxy: false,
    domainName: {
      domainName: this.apiDomainName,
      certificate: new Certificate(this, "api-domain-cenrtificate", {
        domainName: this.apiDomainName,
        validation: CertificateValidation.fromDns(),
      }),
      endpointType: EndpointType.REGIONAL,
    },
  });

  public apiDNS = new ARecord(this, "apiDNS", {
    zone: new HostedZone(this, "api-hosted-zone", { zoneName: this.rootDomainName }),
    recordName: "api",
    target: RecordTarget.fromAlias(new ApiGateway(this.api)),
  });

  private resources = [
    { name: "signup", methods: ["POST"], isProtected: false },
    { name: "login", methods: ["POST"], isProtected: false },
    { name: "verify", methods: ["POST"], isProtected: false },
    { name: "chat", methods: ["POST"], isProtected: false },
    { name: "forgot-password", methods: ["POST"], isProtected: false },
    { name: "reset-password", methods: ["POST"], isProtected: false },
  ];

  private configureResources(): void {
    this.resources.forEach(({ name, methods, isProtected }) => {
      const reasource = this.api.root.addResource(name, {
        defaultCorsPreflightOptions: this.corsOptions,
      });
      methods.forEach(method => {
        reasource.addMethod(method, undefined, {
          ...(isProtected && {
            authorizationType: AuthorizationType.COGNITO,
            authorizer: this.authorizer,
          }),
        });
      });
    });
    this.api.addUsagePlan("api-usage-plan", { throttle: { rateLimit: 10 } });
  }
}