Search code examples
node.jstypescriptcorsserverlessserverless-framework

CORS error Serverless Framework with Typescript?


I am stuck on this basic CORS issue but nothing seems to help.

Tried everything in Serverless CORS Survival Guide(https://www.serverless.com/blog/cors-api-gateway-survival-guide/)

Everything working fine in serverless offline

Part 1 Serverless Offline

Here I am getting all the expected response headers for the same

Part 2 Serverless Offline

Error In Browser-

Access to fetch at 'https://XXXXXXXXX.execute-api.us-east-1.amazonaws.com/dev/spaces_config/ap1' from origin 'https://XXXXXXXXX.com' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.

My Setup->

I am using this boilerplate-> https://github.com/serverless/examples/aws-node-rest-api-typescript

Serverless.yml-

provider:
  name: aws
  runtime: nodejs16.x
  memorySize: 512
  timeout: 30
  environment:
    NODE_ENV: dev

plugins:
  - serverless-plugin-typescript
  - serverless-offline

package:
  exclude:
    - config/.env.stg
    - config/.env.pro
  include:
    - config/.env.dev

functions:
  createSpace:
    handler: handler.createSpace
    events:
      - http:
          path: spaces
          method: post
          cors:
            origin: "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
            allowCredentials: true
  findSpace:
    handler: handler.findSpace
    events:
      - http:
          path: spaces
          method: get
          cors:
            origin: "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
            allowCredentials: true

  findSpaceConfig:
    handler: handler.findSpaceConfig
    events:
      - http:
          path: spaces_config
          method: get
          cors:
            origin: "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
            allowCredentials: true
  updateSpaceConfig:
    handler: handler.updateSpaceConfig
    events:
      - http:
          path: spaces_config/{space_id}
          method: put
          cors:
            origin: "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
            allowCredentials: true
  findOneSpaceConfigById:
    handler: handler.findOneSpaceConfigById
    events:
      - http:
          path: spaces_config/{space_id}
          method: get
          cors:
            origin: "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
            allowCredentials: true

Also tried with cors: true

Handler.js File-

import { Handler, Context, APIGatewayEvent } from "aws-lambda";
import dotenv from "dotenv";
// import middy from "middy";
// import { cors } from "middy/middlewares";

// cross-env NODE_ENV=dev ts-node handler.ts
dotenv.config({
  path: `config/.env.${process.env["NODE_ENV"]}`,
});

export * from "./app/connections/mongo";

import { SpacesController } from "./app/controller/spaces";
import { spaces } from "./app/model/spaces";

import { SpacesConfigController } from "./app/controller/spaces_config";
import { spaces_config } from "./app/model/spaces_config";
// Spaces

const spacesController = new SpacesController(spaces);

export const createSpace: Handler = (
  event: APIGatewayEvent,
  context: Context
) => {
  return spacesController.create(event, context);
};

export const findSpace: Handler = () => spacesController.find();

// Spaces Config

const spacesConfigController = new SpacesConfigController(spaces_config);

export const findSpaceConfig: Handler = () => spacesConfigController.find();

export const updateSpaceConfig: Handler = (event: APIGatewayEvent) =>
  spacesConfigController.update(event);

export const findOneSpaceConfigById: Handler = (
  event: APIGatewayEvent,
  context: Context
) => {
  return spacesConfigController.findOne(event, context);
};

// export const findOneSpaceConfigById = middy(findOneSpaceConfigByI).use(cors());

In the boilerplate, there is a file message.ts(https://github.com/serverless/examples/blob/master/aws-node-rest-api-typescript/app/utils/message.ts) where we can customize the response. After middy was not working above, I tried sending response headers manually.

I was not getting any response header when I was using middy.

message.ts-

import { ResponseVO } from "../types/response";

enum StatusCode {
  success = 200,
}

class Result {
  private statusCode: number;
  private code: number;
  private message: string;
  private data?: any;

  constructor(statusCode: number, code: number, message: string, data?: any) {
    this.statusCode = statusCode;
    this.code = code;
    this.message = message;
    this.data = data;
  }

  /**
   * Serverless: According to the API Gateway specs, the body content must be stringified
   */
  bodyToString() {
    return {
      headers: {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Credentials": true,
        "Content-Type": "application/json",
        "Access-Control-Allow-Methods": "GET,PUT,POST,PATCH,OPTIONS",
        "Access-Control-Allow-Headers":
          "Content-Type, Accept, x-access-token ,Authorization",
      },
      statusCode: this.statusCode,
      body: JSON.stringify({
        code: this.code,
        message: this.message,
        data: this.data,
      }),
    };
  }
}

export class MessageUtil {
  static success(data: object): ResponseVO {
    const result = new Result(StatusCode.success, 0, "success", data);

    return result.bodyToString();
  }

  static error(code: number = 400, message: string) {
    const result = new Result(StatusCode.success, code, message);

    console.log(result.bodyToString());
    return result.bodyToString();
  }
}

Dependencies Used-

Dependencies

Stuck on the problem for a very long time.

Any help will be appreciated.

Thanks


Solution

  • This issue is likely occurring because the JavaScript for the browser is sending headers that are not in the allowed list in your serverless.yml. You probably do not need to send those headers either as sending them from the browser doesn't assist in resolving CORS and may complicate it. I would recommend removing all headers from your message.ts except Content-type and trying again.