2

I'm trying to deploy an Angular SSR app to AWS using serverless and lambda. Being a total beginner in this, I'm using this starter project to do so. I did not change any of the code except changing the name of the project to "public". There is a dedicated section in the readme that explains how to do that ("Find & replace the word "ngx-serverless-starter" with your project name").

Afterwards I uploaded the project to AWS. This is the log:

Serverless: Stack update finished...
Service Information
service: public
stage: dev
region: us-east-1
stack: public-dev
resources: 12
api keys:
  None
endpoints:
  GET - https://u3vc0nd65k.execute-api.us-east-1.amazonaws.com/dev
  GET - https://u3vc0nd65k.execute-api.us-east-1.amazonaws.com/dev/{proxy+}
functions:
  api: public-dev-api
layers:
  None

So I'm assuming it worked. If I click on the provided URL though I'm seeing random numbers and characters (I had to truncate it because of the characters limit of Stackoverflow):

PCFET0NUWVBFIGh0bWw+PGh0bWwgbGFuZz0iZW4iPjxoZWFkPgogIDxtZXRhIGNoYXJzZXQ9InV0Zi04Ij4KICA8dGl0bGU+Tmd4U2VydmVybGVzc1N0YXJ0ZXI8L3RpdGxlPgogIDxiYXNlIGhyZWY9Ii8iPgogIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MSI+CiAgPGxpbmsgcmVsPSJpY29uIiB0eXBlPSJpbWFnZS94LWljb24iIGhyZWY9ImZhdmljb24uaWNvIj4KPGxpbmsgcmVsPSJzdHlsZXNo

It looks to me as if there might be some wrong content type being returned, but I can't really make any sense of it.

user3255061
  • 1,757
  • 1
  • 30
  • 50

2 Answers2

2

What is the random string of characters?

The "random numbers and characters" are not actually random but a base64 encoding of the HTML page. If you do a base64 decoding of the response you would get a HTML page.

Why am I getting base64 encoding?

module.exports.handler = serverlessExpress({
  app,
  binarySettings: {
    isBinary: ({ headers }) => true,
    contentTypes: [],
    contentEncodings: [],
  },
});

The base64 encoding is due to binarySettings in lambda.js. From the binarySettings section in serverless-express README:

binarySettings

Determine if the response should be base64 encoded before being returned to the event source, for example, when returning images or compressed files. This is necessary due to API Gateway and other event sources not being capable of handling binary responses directly. The event source is then responsible for turning this back into a binary format before being returned to the client.

By default, this is determined based on the content-encoding and content-type headers returned by your application. If you need additional control over this, you can specify binarySettings.

{   
  binarySettings: {
    isBinary: ({ headers }) => true,
    contentTypes: ['image/*'],
    contentEncodings: []   
  }
}

Any value you provide here should also be specified on API Gateway API. In SAM, this looks like:

ExpressApi:
  Type: AWS::Serverless::Api
  Properties:
    StageName: prod
    BinaryMediaTypes: ['image/*']

Why is there an option to enable base64 encoding?

The base64 encoding option is mainly used for scenarios where you are sending binary data as response through AWS API Gateway (for example: images, GZip files, etc.). For binary content to work correctly, you need to send the response in base64 encoded format for AWS Lambda proxy integration:

To handle binary payloads for AWS Lambda proxy integrations, you must base64-encode your function's response. You must also configure the binaryMediaTypes for your API. Your API's binaryMediaTypes configuration is a list of content types that your API treats as binary data. Example binary media types include image/png or application/octet-stream. You can use the wildcard character (*) to cover multiple media types. For example, */* includes all content types.

Fixing the Content-Type header (optional)

By default, the page response has a Content-Type header of application/json. You can change this to text/html in server.ts and serverless.ts. This isn't really required for solving the issue, but it is better to configure the correct Content-Type.

  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    res.header('Content-Type', 'text/html');
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

Option 1: Disable base64 encoding of response

Comment out binarySettings in lambda.js so that the response isn't base64 encoded.

module.exports.handler = serverlessExpress({
  app,
  // binarySettings: {
  //   isBinary: ({ headers }) => true,
  //   contentTypes: [],
  //   contentEncodings: [],
  // },
});

Option 2: Use API Gateway to convert the base64 response

If you set binaryMediaTypes in API Gateway, it will automatically convert the base64 encoding to a proper response. Use application/json if you haven't changed the Content-Type in the optional step.

provider:
  name: aws
  runtime: nodejs12.x
  lambdaHashingVersion: 20201221
  memorySize: 192
  timeout: 10
  apiGateway:
    binaryMediaTypes:
      - 'text/html'

I created a PR which disables the base64 encoding (Option 1).

Note: I had to do a hard refresh in my browser to correct the response after a new deployment.

Kaustubh Khavnekar
  • 2,553
  • 2
  • 14
1

The accepted answer appeared to solve my problem, it turned out images were not rendered though. Oddly there was no error (status code 200) and yet they would not show up.

This thread led me in the right direction:

I added the binary settings again in the lambda.js:

module.exports.handler = serverlessExpress({
  app,
  binarySettings: {
      isBinary: ({ headers }) => true,
      contentTypes: [],
      contentEncodings: [],
   },
});

Afterwards I changed my API Gateway settings and added */* in Binary Media Types. This did the trick.

user3255061
  • 1,757
  • 1
  • 30
  • 50