javascriptnode.jsproxynetlify-function

Netlify function: GitHub API proxy request fails with `error decoding lambda response: json`


This Netlify function should run as an endpoint on example.com/.netlify/functions/github and is supposed to proxy a fetch request from my website, reach out to the GitHub API and send data back to the website.

As far as I have understood, I can use to GET data from the GitHub API without authentication. Hitting their API directly in the browser works: https://api.github.com/orgs/github/repos?per_page=2 (also works from Postman).

The data is an array of objects where each object is a repository.

There has been multiple issues the past couple of years where Netlify functions (running on AWS lambdas) have had hickups that resulted in error messages similar to mine, so I'm confused whether this is an error in my code or something weird on their side.

First, the proxy function which – according to the Netlify admin console – runs without error. In a support article Netlify requires the result returned as JSON.stringify(), so I follow that convention here:


const fetch = require('node-fetch')
const url = 'https://api.github.com/orgs/github/repos?per_page=2'

const optionsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'Content-Type'
}

const fetchHeaders = {
  'Content-Type': 'application/json',
  'Host': 'api.github.com',
  'Accept': 'application/vnd.github.v3+json',
  'Accept-Encoding': 'gzip, deflate, br'
}

exports.handler = async (event, context) => {
  if (event.httpMethod === 'OPTIONS') {
    return {
      'statusCode': '200',
      'headers': optionsHeaders,
    }

  } else {

    try {
      const response = await fetch(url, {
        method: 'GET',
        headers: fetchHeaders
      })

      const data = await response.json()
      console.log(JSON.stringify({ data }))

      return {
        statusCode: 200,
        body: JSON.stringify({ data })
      }

    } catch (err) {
      console.log(err)
    }
  }
}

Client fetch that hits https://example.com/.netlify/functions/github. URL is correct, the function is executed (verified that in the Netlify admin panel):

const repos = document.querySelectorAll('.repo')

if (repos && repos.length >= 1) {
  const getRepos = async (url) => {
    try {
      const response = await fetch(url, {
        method: "GET",
        mode: "no-cors"
      })
      
      const res = await response.text() 
      // assuming res is now _text_ as per `JSON.stringify` but neither 
      // that nor `.json()` work

      console.log(res[0].name)
      return res[0].name

    } catch(err) {
      console.log(err)
    }
  }

  const repoName = getRepo('https://example.com/.netlify/functions/github')
  
  repos.forEach((el) => {
    el.innerText = repoName
  })
}

Not 100% sure where this error message originates from, it is probably not the console.log(err) although it displays in the browser console, because the error code is 502 and the error also shows up directly in the response body in Postman.

error decoding lambda response: error decoding lambda response: json: cannot unmarshal
string into Go value of type struct { StatusCode int "json:\"statusCode\""; Headers
 map[string]interface {} "json:\"headers\""; MultiValueHeaders map[string][]interface {}
 "json:\"multiValueHeaders\""; Body string "json:\"body\""; IsBase64Encoded bool 
"json:\"isBase64Encoded,omitempty\""; Metadata *functions.Metadata 
"json:\"metadata,omitempty\"" }

Haven't found any clear information on this issue, could any of you enlighten me?


Solution

  • The only response that don't comply with the schema is the preflight request. From the error message, I assume you need to change:

    'statusCode': '200',
    

    to

    'statusCode': 200, // StatusCode int
    

    Even better, because there's no content, you may want to use 204 instead. If that's still not enough, I may still want to include the body there as well, as it doesn't seem optional:

        return {
          'statusCode': 204,
          'headers': optionsHeaders,
          'body': ''
        }