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.
MIT License
169 stars 19 forks source link

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

Closed schematical closed 10 months ago

schematical commented 1 year ago

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!

sladg commented 1 year ago

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?

schematical commented 1 year ago

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.

schematical commented 1 year ago

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+"
}
schematical commented 1 year ago

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.

sladg commented 1 year ago

👋 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?

sladg commented 1 year ago

@schematical heya! any news on this?

sladg commented 10 months ago

Closing due to inactivity