sladg / nextjs-lambda

Lambda deployments for Nextjs12 & Nextjs13 (standalone). Easy CLI commands to get your standalone Next output to run in AWS Lambda (not @Edge)! Uses CDK in behind and produces code zips importable to Terraform, Serverless, Azure, etc.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Getting the response body returned with Base64 encoded with `compress: false`

schematical opened this issue · comments

Hey I am getting a Base64 encoded response back. Here is my next.config.js

const path = require('path')

module.exports = {
  compress: false,
  output: 'standalone',
  experimental: {
    esmExternals: false, // optional
    externalDir: true, // optional
    outputFileTracingRoot: path.join(__dirname, '../../'), // monorepo option
  }
}

Here is the package.json

{
  "name": "drawnby-www",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@apollo/client": "^3.7.13",
    "@types/node": "18.16.3",
    "@types/react": "18.2.0",
    "@types/react-dom": "18.2.1",
    "autoprefixer": "10.4.14",
    "bootstrap": "^5.3.0-alpha3",
    "eslint": "8.39.0",
    "eslint-config-next": "13.3.4",
    "filerobot-image-editor": "^4.4.0",
    "next": "^12.3.4",
    "postcss": "8.4.23",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-filerobot-image-editor": "^4.4.0",
    "tailwindcss": "3.3.2",
    "typescript": "5.0.4"
  }
}

Is there anything special I need to do on the APIGateway side for this to work?
I tried playing with the following integration properties:

  passthrough_behavior    = "WHEN_NO_MATCH"
  content_handling        = "CONVERT_TO_TEXT"

but it did not seem to help.

Let me know if I am missing something. Thanks!

commented

Heya! turned-off compression is needed for Cloudfront to work properly.
Can you share your route that returns base64? Aka. is this happening on API? on client? Are you using REST, sockets or similar?

So it is a REST api. Here is the exact terraform I am using

resource "aws_api_gateway_rest_api" "api_gateway" {
  body = jsonencode({
    openapi = "3.0.1"
    info = {
      title   = "example"
      version = "1.0"
    }
    paths = {

    }
  })

  name = "drawnby-www-v1"

  endpoint_configuration {
    types = ["REGIONAL"]
  }
}

resource "aws_api_gateway_method" "api_gateway_method" {
  rest_api_id   = aws_api_gateway_rest_api.api_gateway.id
  resource_id   = aws_api_gateway_rest_api.api_gateway.root_resource_id
  http_method   = "ANY"
  authorization = "NONE"

}

resource "aws_api_gateway_integration" "api_gateway_root_resource_method_integration" {
  rest_api_id          = aws_api_gateway_rest_api.api_gateway.id
  resource_id          = aws_api_gateway_rest_api.api_gateway.root_resource_id
  http_method          = aws_api_gateway_method.api_gateway_method.http_method

  integration_http_method = "POST"
  type                    = "AWS_PROXY"
  passthrough_behavior    = "WHEN_NO_MATCH"
  content_handling        = "CONVERT_TO_BINARY"
  uri = "arn:aws:apigateway:${var.region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${var.region}:${data.aws_caller_identity.current.account_id}:function:sc-drawnby-www-v1-$${stageVariables.ENV}/invocations"
}

The URL is https://dev-v1-us-east-1-api.drawnby.ai/ (NOTE: I am spinning up and tearing down parts of the infrastructure so it might be up or down depending on what I am doing today).

An example body returned is as follows:

PCFET0NUWVBFIGh0bWw+PGh0bWwgbGFuZz0iZW4iPjxoZWFkPjxtZXRhIGNoYXJTZXQ9InV0Zi04Ii8+PG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCIvPjxtZXRhIG5hbWU9Im5leHQtaGVhZC1jb3VudCIgY29udGVudD0iMiIvPjxsaW5rIHJlbD0icHJlbG9hZCIgaHJlZj0iL19uZXh0L3N0YXRpYy9jc3MvZTg0YzFkNTFlMDljZjA4Ni5jc3MiIGFzPSJzdHlsZSIvPjxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iL19uZXh0L3N0YXRpYy9jc3MvZTg0YzFkNTFlMDljZjA4Ni5jc3MiIGRhdGEtbi1nPSIiLz48bGluayByZWw9InByZWxvYWQiIGhyZWY9Ii9fbmV4dC9zdGF0aWMvY3NzLzgxZWIwZDAwMTM0ZGIxY2YuY3NzIiBhcz0ic3R5bGUiLz48bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Ii9fbmV4dC9zdGF0aWMvY3NzLzgxZWIwZDAwMTM0ZGIxY2YuY3NzIiBkYXRhLW4tcD0iIi8+PG5vc2NyaXB0IGRhdGEtbi1jc3M9IiI+PC9ub3NjcmlwdD48c2NyaXB0IGRlZmVyPSIiIG5vbW9kdWxlPSIiIHNyYz0iL19uZXh0L3N0YXRpYy9jaHVua3MvcG9seWZpbGxzLWM2N2E3NWQxYjZmOTlkYzguanMiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSIvX25leHQvc3RhdGljL2NodW5rcy93ZWJwYWNrLTkxOTRlM2E2NjE4MTRjNjkuanMiIGRlZmVyPSIiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSIvX25leHQvc3RhdGljL2NodW5rcy9mcmFtZXdvcmstN2RjOGE2NWY0YTBjZGEzMy5qcyIgZGVmZXI9IiI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9Ii9fbmV4dC9zdGF0aWMvY2h1bmtzL21haW4tYjVhNjQ0MTU1YTIzNzEyZC5qcyIgZGVmZXI9IiI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9Ii9fbmV4dC9zdGF0aWMvY2h1bmtzL3BhZ2VzL19hcHAtOTcxMmZiN2UzZmY1ODJiNS5qcyIgZGVmZXI9IiI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9Ii9fbmV4dC9zdGF0aWMvY2h1bmtzLzEzLWEwMzViNjIwNjA1N2M0ZTcuanMiIGRlZmVyPSIiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSIvX25leHQvc3RhdGljL2NodW5rcy9wYWdlcy9pbmRleC1jMTg2MWQ5ZGJkM2VlMjJkLmpzIiBkZWZlcj0iIj48L3NjcmlwdD48c2NyaXB0IHNyYz0iL19uZXh0L3N0YXRpYy9zTjVsRmNfamJMSFVKNTBLY2RhV20vX2J1aWxkTWFuaWZlc3QuanMiIGRlZmVyPSIiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSIvX25leHQvc3RhdGljL3NONWxGY19qYkxIVUo1MEtjZGFXbS9fc3NnTWFuaWZlc3QuanMiIGRlZmVyPSIiPjwvc2NyaXB0PjwvaGVhZD48Ym9keT48ZGl2IGlkPSJfX25leHQiPjxtYWluIGNsYXNzPSJmbGV4IG1pbi1oLXNjcmVlbiBmbGV4LWNvbCBpdGVtcy1jZW50ZXIganVzdGlmeS1iZXR3ZWVuIHAtMjQiPjxkaXYgY2xhc3M9ImNvbnRhaW5lciI+PGRpdj48aDI+U3RlcCAxOiBVcGxvYWQgMSBvciBtb3JlIGltYWdlcyBvZiB5b3VyIHBldDwvaDI+PGlucHV0IHR5cGU9ImZpbGUiIGFjY2VwdD0iaW1hZ2UvKiIgbXVsdGlwbGU9IiIvPjxidXR0b24gY2xhc3M9ImJ0biIgZGlzYWJsZWQ9IiI+TmV4dDwvYnV0dG9uPjwvZGl2PjwvZGl2PjwvbWFpbj48L2Rpdj48c2NyaXB0IGlkPSJfX05FWFRfREFUQV9fIiB0eXBlPSJhcHBsaWNhdGlvbi9qc29uIj57InByb3BzIjp7InBhZ2VQcm9wcyI6e319LCJwYWdlIjoiLyIsInF1ZXJ5Ijp7fSwiYnVpbGRJZCI6InNONWxGY19qYkxIVUo1MEtjZGFXbSIsIm5leHRFeHBvcnQiOnRydWUsImF1dG9FeHBvcnQiOnRydWUsImlzRmFsbGJhY2siOmZhbHNlLCJzY3JpcHRMb2FkZXIiOltdfTwvc2NyaXB0PjwvYm9keT48L2h0bWw+

I can get you the exact code.zip etc if needed as well.

I will also note that when I trigger the lambda directly with a test APIGatewayProxy payload the body comes back base64 encoded:

{
  "statusCode": 200,
  "headers": {
    "x-powered-by": "Next.js",
    "etag": "\"a6321z8ugo1du\"",
    "content-type": "text/html; charset=utf-8",
    "content-length": "1794"
  },
  "isBase64Encoded": true,
  "body": "PCFET0NUWVBFIGh0bWw+PGh0bWwgbGFuZz0iZW4iPjxoZWFkPjxtZXRhIGNoYXJTZXQ9InV0Zi04Ii8+PG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCIvPjxtZXRhIG5hbWU9Im5leHQtaGVhZC1jb3VudCIgY29udGVudD0iMiIvPjxsaW5rIHJlbD0icHJlbG9hZCIgaHJlZj0iL19uZXh0L3N0YXRpYy9jc3MvZTg0YzFkNTFlMDljZjA4Ni5jc3MiIGFzPSJzdHlsZSIvPjxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iL19uZXh0L3N0YXRpYy9jc3MvZTg0YzFkNTFlMDljZjA4Ni5jc3MiIGRhdGEtbi1nPSIiLz48bGluayByZWw9InByZWxvYWQiIGhyZWY9Ii9fbmV4dC9zdGF0aWMvY3NzLzgxZWIwZDAwMTM0ZGIxY2YuY3NzIiBhcz0ic3R5bGUiLz48bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Ii9fbmV4dC9zdGF0aWMvY3NzLzgxZWIwZDAwMTM0ZGIxY2YuY3NzIiBkYXRhLW4tcD0iIi8+PG5vc2NyaXB0IGRhdGEtbi1jc3M9IiI+PC9ub3NjcmlwdD48c2NyaXB0IGRlZmVyPSIiIG5vbW9kdWxlPSIiIHNyYz0iL19uZXh0L3N0YXRpYy9jaHVua3MvcG9seWZpbGxzLWM2N2E3NWQxYjZmOTlkYzguanMiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSIvX25leHQvc3RhdGljL2NodW5rcy93ZWJwYWNrLTkxOTRlM2E2NjE4MTRjNjkuanMiIGRlZmVyPSIiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSIvX25leHQvc3RhdGljL2NodW5rcy9mcmFtZXdvcmstN2RjOGE2NWY0YTBjZGEzMy5qcyIgZGVmZXI9IiI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9Ii9fbmV4dC9zdGF0aWMvY2h1bmtzL21haW4tYjVhNjQ0MTU1YTIzNzEyZC5qcyIgZGVmZXI9IiI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9Ii9fbmV4dC9zdGF0aWMvY2h1bmtzL3BhZ2VzL19hcHAtOTcxMmZiN2UzZmY1ODJiNS5qcyIgZGVmZXI9IiI+PC9zY3JpcHQ+PHNjcmlwdCBzcmM9Ii9fbmV4dC9zdGF0aWMvY2h1bmtzLzEzLWEwMzViNjIwNjA1N2M0ZTcuanMiIGRlZmVyPSIiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSIvX25leHQvc3RhdGljL2NodW5rcy9wYWdlcy9pbmRleC1jMTg2MWQ5ZGJkM2VlMjJkLmpzIiBkZWZlcj0iIj48L3NjcmlwdD48c2NyaXB0IHNyYz0iL19uZXh0L3N0YXRpYy84THJyamNIUU11Mi1fVk14dVBhb0ovX2J1aWxkTWFuaWZlc3QuanMiIGRlZmVyPSIiPjwvc2NyaXB0PjxzY3JpcHQgc3JjPSIvX25leHQvc3RhdGljLzhMcnJqY0hRTXUyLV9WTXh1UGFvSi9fc3NnTWFuaWZlc3QuanMiIGRlZmVyPSIiPjwvc2NyaXB0PjwvaGVhZD48Ym9keT48ZGl2IGlkPSJfX25leHQiPjxtYWluIGNsYXNzPSJmbGV4IG1pbi1oLXNjcmVlbiBmbGV4LWNvbCBpdGVtcy1jZW50ZXIganVzdGlmeS1iZXR3ZWVuIHAtMjQiPjxkaXYgY2xhc3M9ImNvbnRhaW5lciI+PGRpdj48aDI+U3RlcCAxOiBVcGxvYWQgMSBvciBtb3JlIGltYWdlcyBvZiB5b3VyIHBldDwvaDI+PGlucHV0IHR5cGU9ImZpbGUiIGFjY2VwdD0iaW1hZ2UvKiIgbXVsdGlwbGU9IiIvPjxidXR0b24gY2xhc3M9ImJ0biIgZGlzYWJsZWQ9IiI+TmV4dDwvYnV0dG9uPjwvZGl2PjwvZGl2PjwvbWFpbj48L2Rpdj48c2NyaXB0IGlkPSJfX05FWFRfREFUQV9fIiB0eXBlPSJhcHBsaWNhdGlvbi9qc29uIj57InByb3BzIjp7InBhZ2VQcm9wcyI6e319LCJwYWdlIjoiLyIsInF1ZXJ5Ijp7fSwiYnVpbGRJZCI6IjhMcnJqY0hRTXUyLV9WTXh1UGFvSiIsIm5leHRFeHBvcnQiOnRydWUsImF1dG9FeHBvcnQiOnRydWUsImlzRmFsbGJhY2siOmZhbHNlLCJzY3JpcHRMb2FkZXIiOltdfTwvc2NyaXB0PjwvYm9keT48L2h0bWw+"
}

At the bottom of the code.zip > index.js I saw able to edit it and set the line binary: true to binary: false and it seems to be working kind of:

ar getErrMessage = (e) => ({ message: "Server failed to respond.", details: e });
var nextHandler = new import_next_server.default(config).getRequestHandler();
var server = (0, import_serverless_http.default)(
  async (req, res) => {
    console.log("START HIT!")
    await nextHandler(req, res).catch((e) => {
      console.error(`NextJS request failed due to:`);
      console.error(e);
      res.setHeader("Content-Type", "application/json");
      res.end(JSON.stringify(getErrMessage(e), null, 3));
    });
  },
  {
    // We have separate function for handling images. Assets are handled by S3.
    binary: false, <----- THIS LINE
    provider: "aws",
    basePath: process.env.NEXTJS_LAMBDA_BASE_PATH
  }
);
var handler = server;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  handler
});

After that the test response is as follows:

{
  "statusCode": 200,
  "headers": {
    "x-powered-by": "Next.js",
    "etag": "\"a6321z8ugo1du\"",
    "content-type": "text/html; charset=utf-8",
    "content-length": "1794"
  },
  "isBase64Encoded": false,
  "body": "<!DOCTYPE html><html lang=\"en\"><head><meta charSet=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width\"/><meta name=\"next-head-count\" content=\"2\"/><link rel=\"preload\" href=\"/_next/static/css/e84c1d51e09cf086.css\" as=\"style\"/><link rel=\"stylesheet\" href=\"/_next/static/css/e84c1d51e09cf086.css\" data-n-g=\"\"/><link rel=\"preload\" href=\"/_next/static/css/81eb0d00134db1cf.css\" as=\"style\"/><link rel=\"stylesheet\" href=\"/_next/static/css/81eb0d00134db1cf.css\" data-n-p=\"\"/><noscript data-n-css=\"\"></noscript><script defer=\"\" nomodule=\"\" src=\"/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js\"></script><script src=\"/_next/static/chunks/webpack-9194e3a661814c69.js\" defer=\"\"></script><script src=\"/_next/static/chunks/framework-7dc8a65f4a0cda33.js\" defer=\"\"></script><script src=\"/_next/static/chunks/main-b5a644155a23712d.js\" defer=\"\"></script><script src=\"/_next/static/chunks/pages/_app-9712fb7e3ff582b5.js\" defer=\"\"></script><script src=\"/_next/static/chunks/13-a035b6206057c4e7.js\" defer=\"\"></script><script src=\"/_next/static/chunks/pages/index-c1861d9dbd3ee22d.js\" defer=\"\"></script><script src=\"/_next/static/8LrrjcHQMu2-_VMxuPaoJ/_buildManifest.js\" defer=\"\"></script><script src=\"/_next/static/8LrrjcHQMu2-_VMxuPaoJ/_ssgManifest.js\" defer=\"\"></script></head><body><div id=\"__next\"><main class=\"flex min-h-screen flex-col items-center justify-between p-24\"><div class=\"container\"><div><h2>Step 1: Upload 1 or more images of your pet</h2><input type=\"file\" accept=\"image/*\" multiple=\"\"/><button class=\"btn\" disabled=\"\">Next</button></div></div></main></div><script id=\"__NEXT_DATA__\" type=\"application/json\">{\"props\":{\"pageProps\":{}},\"page\":\"/\",\"query\":{},\"buildId\":\"8LrrjcHQMu2-_VMxuPaoJ\",\"nextExport\":true,\"autoExport\":true,\"isFallback\":false,\"scriptLoader\":[]}</script></body></html>"
}

Not close to a permanent solution, but hopefully that helps us debug this.

commented

👋 I have spent some time on this. I'm thinking that the issue might be caused by you using REST API instead of HTTP API that we implement in this repo.

Is TF construct available for HTTP API? Could you try it out to see if it would resolve the issue?

commented

@schematical heya! any news on this?

commented

Closing due to inactivity