Netlify function running in netlify dev but error 502 with netlify deploy

Hi,

I’m running a function, login.ts, which works great when using netlify dev. However, when I run netlify deploy I get a 502 error and after a few hours am at a loss on how to solve it.

I’m using babel via .babelrc in my functions folder and they compile down to JavaScript.

I did try what was recommended in this thread and tried Node versions 12 and 14 which should be supported, with no luck.

Here’s my .babelrc

{
  "presets": [
    "@babel/preset-typescript",
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "12"
        }
      }
    ]
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-object-assign",
    "@babel/plugin-proposal-object-rest-spread",
    "@babel/plugin-proposal-optional-chaining",
    "@babel/plugin-transform-runtime"
  ],
  "ignore": ["node_modules"]
}

And here’s my function, login.ts

import { APIGatewayEvent } from 'aws-lambda'
import axios from 'axios'

import {
  statusReturn,
  preparePayload,
  shopifyConfig,
  SHOPIFY_GRAPHQL_URL,
  CUSTOMER_QUERY,
  CUSTOMER_TOKEN_QUERY,
} from './requestConfig'

let data: {
  email?: string
  password?: string
}

let accessToken

export const handler = async (event: APIGatewayEvent): Promise<any> => {
  if (event.httpMethod !== 'POST' || !event.body) return statusReturn(400, {})

  try {
    data = JSON.parse(event.body)
  } catch (error) {
    console.log('JSON parsing error:', error)
    return statusReturn(400, { error: 'Bad Request Body' })
  }

  const payload = preparePayload(CUSTOMER_TOKEN_QUERY, {
    input: {
      email: data.email,
      password: data.password,
    },
  })
  try {
    console.log('GRAPHQL URL', SHOPIFY_GRAPHQL_URL)
    const token = await axios({
      url: `https://${SHOPIFY_GRAPHQL_URL}`,
      method: 'POST',
      headers: shopifyConfig,
      data: payload,
    })
    console.log('TOKEN', token.data.data.customerAccessTokenCreate)
    if (token.data.data.customerAccessTokenCreate.customerUserErrors.length > 0) {
      throw token.data.data.customerAccessTokenCreate.customerUserErrors
    } else {
      accessToken = token.data.data.customerAccessTokenCreate.customerAccessToken.accessToken
    }
  } catch (err) {
    return statusReturn(200, { error: 'Problem with email or password' })
  }

  const payloadCustomer = preparePayload(CUSTOMER_QUERY, {
    customerAccessToken: accessToken,
  })

  try {
    let customer = await axios({
      url: `https://${SHOPIFY_GRAPHQL_URL}`,
      method: 'POST',
      headers: shopifyConfig,
      data: payloadCustomer,
    })
    customer = customer.data.data.customer
    return statusReturn(200, {
      token: accessToken,
      customer,
    })
  } catch (err) {
    return statusReturn(500, { error: err.message })
  }
}

Really appreciate any help narrowing this down!

Hey @proton,
Any errors in your function logs? You’d find those in your site dashboard here: https://app.netlify.com/sites/YOUR-NETLIFY-SUBDOMAIN/functions

Also, could you please share your Netlify URL and function name so we can take a look in our logs? Thanks!

1 Like

Of course, thanks for the help.

Current deploy

Function name → login

Function log:

10:06:55 AM: 2021-03-25T03:06:55.654Z	undefined	ERROR	Uncaught Exception 	{"errorType":"TypeError","errorMessage":"Cannot read property 'replace' of undefined","stack":["TypeError: Cannot read property 'replace' of undefined","    at Module.<anonymous> (/var/task/src/login.js:1:4248)","    at n (/var/task/src/login.js:1:158)","    at Module.<anonymous> (/var/task/src/login.js:1:43995)","    at n (/var/task/src/login.js:1:158)","    at /var/task/src/login.js:1:957","    at Object.<anonymous> (/var/task/src/login.js:1:967)","    at Module._compile (internal/modules/cjs/loader.js:999:30)","    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)","    at Module.load (internal/modules/cjs/loader.js:863:32)","    at Function.Module._load (internal/modules/cjs/loader.js:708:14)"]}
10:06:55 AM: 2021-03-25T03:06:55.896Z	undefined	ERROR	Uncaught Exception 	{"errorType":"TypeError","errorMessage":"Cannot read property 'replace' of undefined","stack":["TypeError: Cannot read property 'replace' of undefined","    at Module.<anonymous> (/var/task/src/login.js:1:4248)","    at n (/var/task/src/login.js:1:158)","    at Module.<anonymous> (/var/task/src/login.js:1:43995)","    at n (/var/task/src/login.js:1:158)","    at /var/task/src/login.js:1:957","    at Object.<anonymous> (/var/task/src/login.js:1:967)","    at Module._compile (internal/modules/cjs/loader.js:999:30)","    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)","    at Module.load (internal/modules/cjs/loader.js:863:32)","    at Function.Module._load (internal/modules/cjs/loader.js:708:14)"]}
10:06:55 AM: 794ffe97 Duration: 180.75 ms	Memory Usage: 15 MB	10:06:55 AM: Unknown application error occurred
TypeError
10:07:17 AM: 2021-03-25T03:07:17.774Z	undefined	ERROR	Uncaught Exception 	{"errorType":"TypeError","errorMessage":"Cannot read property 'replace' of undefined","stack":["TypeError: Cannot read property 'replace' of undefined","    at Module.<anonymous> (/var/task/src/login.js:1:4248)","    at n (/var/task/src/login.js:1:158)","    at Module.<anonymous> (/var/task/src/login.js:1:43995)","    at n (/var/task/src/login.js:1:158)","    at /var/task/src/login.js:1:957","    at Object.<anonymous> (/var/task/src/login.js:1:967)","    at Module._compile (internal/modules/cjs/loader.js:999:30)","    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)","    at Module.load (internal/modules/cjs/loader.js:863:32)","    at Function.Module._load (internal/modules/cjs/loader.js:708:14)"]}
10:07:17 AM: fe1c089d Duration: 174.63 ms	Memory Usage: 15 MB	10:07:17 AM: Unknown application error occurred
TypeError
10:07:18 AM: 2021-03-25T03:07:18.011Z	undefined	ERROR	Uncaught Exception 	{"errorType":"TypeError","errorMessage":"Cannot read property 'replace' of undefined","stack":["TypeError: Cannot read property 'replace' of undefined","    at Module.<anonymous> (/var/task/src/login.js:1:4248)","    at n (/var/task/src/login.js:1:158)","    at Module.<anonymous> (/var/task/src/login.js:1:43995)","    at n (/var/task/src/login.js:1:158)","    at /var/task/src/login.js:1:957","    at Object.<anonymous> (/var/task/src/login.js:1:967)","    at Module._compile (internal/modules/cjs/loader.js:999:30)","    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)","    at Module.load (internal/modules/cjs/loader.js:863:32)","    at Function.Module._load (internal/modules/cjs/loader.js:708:14)"]}

Seems to be a TypeError and I understand that maybe I have my Babel configuration wrong, but what’s confusing me is that it’s working with dev and not deploy

I spent a while digging into this but didn’t come up with much more than we already know. The difference between local and production is likely because we simulate function calls with the CLI- those functions are not actually deployed to AWS- whereas when you deploy, the function bundles are sent to AWS; they’re AWS lambdas under the hood.

I’m not a TypeScript expert so can’t tell from reading your code- are you sure you’re returning the right types in the responses? It looks like they’re all wrapped in statusReturn. Here’s what responses should look like:

{
    "isBase64Encoded": true|false,
    "statusCode": httpStatusCode,
    "headers": { "headerName": "headerValue", ... },
    "multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... },
    "body": "..."
}

And are you running into this with all your functions or just this one? Wish I had more for you but hopefully this will help move us along!

Thanks for looking into this and explaining the difference between the two. I’ll do some more testing with other functions as well and update you here.

Is it possible for the CLI to not minify the code?

hey proton, i can’t really speak to the functions part of your question, but if you set the post-processing settings via the netlify.toml file, the CLI should respect that:

1 Like

I’ve managed to narrow this down finally. I got rid of Babel and converted to CommonJs and it was workin great up until one point. It turns out that once the function is up on AWS, it does not like this:

const SHOPIFY_GRAPHQL_URL = process.env.SHOPIFY_GRAPHQL_URL.replace('API_VERSION', GATSBY_SHOPIFY_STOREFRONT_API_VERSION)

It also does not like this:

const SHOPIFY_GRAPHQL_URL = `${GATSBY_SHOP_NAME}.myshopify.com/api/${GATSBY_SHOPIFY_STOREFRONT_API_VERSION}/graphql.json`

Both .replace() and template literals do not work, specifically when using environment variables in them.

When I use template literals, this is the error:

FetchError: invalid json response body at https://undefined.myshopify.com/api/undefined/graphql.json reason: Unexpected token < in JSON at position 0

As you can see, when doing it this way, they come through as undefined.

Also, 'X-Shopify-Storefront-Access-Token': GATSBY_SHOPIFY_STOREFRONT_TOKEN also creates a problem outside the context of string manipulation, which means I’m missing something about how environment variables are suppose to work with Netlify functions.

Thoughts?

Hey @proton,
I just tested template literals and .replace() and both worked for me, so I think the error you’re seeing is related to something else. Here’s the function I used (PROD_API_LOCATION is "pizza p a r t y)

exports.handler = async function(event, context) {
  console.log(process.env.PROD_API_LOCATION) // pizza p a r t y

  let NEW_VAR = `lets go to ${process.env.PROD_API_LOCATION}`
  console.log(NEW_VAR) // lets go to pizza p a r t y

  let OTHER_VAR = process.env.PROD_API_LOCATION.replace('pizza', 'javascript')
  console.log(OTHER_VAR) // javascript p a r t y

  return {
    statusCode: 200
  };
}

We see that “Unexpected token < in JSON…” error quite a bit: Search results for 'Unexpected token < in JSON' - Netlify Support Forums It’s usually related to how response fields are structured (not in the shape we expect).

Can you say more about what you meant re: X-Shopify… token? Are you passing that in as a header or trying to set it as a header or something else?

hey @jen ,

Thanks for the clarification there. After a little more playing around, I realized that I can pass in environment variables to functions, but not if I’m passing them in from a local .env file while using netlify build and netlify deploy. However, if I specify env variables with the Netlify UI and use netlify deploy, they do make it into the function.

The .replace() issues I thought I was experiencing were just do to the ${ENV_VARIABLE} being undefined since I was trying to pass it in from a local .env file.

At the moment, now that I have env variables in the Netlify UI, things are working as they should :slight_smile:

Thanks for the help

1 Like

Ah yeah I should’ve remembered and mentioned that point about a local .env file- sorry that you had to keep testing to find that limitation. Super glad to hear that things are working better now- yay!

Hey @jen no worries at all, I learned a lot in the process.

Are there any workarounds to get local .env variable into AWS functions on deploy? Or is that a feature planned for the future?

Currently, not for a production deploy. We’ll pick up an .env file for local testing: cli/netlify-dev.md at main · netlify/cli · GitHub

but for production, you have to add environment variables in our UI.

That said, this is something that’s being looked at by the product team right now- I can add this thread to that conversation so we can be sure to follow up with you if ship any updates.

1 Like