Search code examples
node.jscdnnetlify

Issue getting simple Fetch working in Netlify Functions


I've been working through the workshops for Netlify functions and have stumbled getting a simple Fetch response to work. When running the sample at: https://github.com/DavidWells/netlify-functions-workshop/blob/master/lessons-code-complete/use-cases/5-fetching-data/functions/node-fetch/node-fetch.js

const fetch = require('node-fetch')

const API_ENDPOINT = 'https://cat-fact.herokuapp.com/facts'

exports.handler = async (event, context) => {   let response   
  try {
    response = await fetch(API_ENDPOINT)
    // handle response   
  } catch (err) {
    return {
      statusCode: err.statusCode || 500,
      body: JSON.stringify({
        error: err.message
      })
  }   }

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

but I just get the following response:

{"data":{"size":0,"timeout":0}}

The build process is working fine, and I've tried other end points but all give the same results.


Solution

  • Here is a working version of your original code using node-fetch and the callback.

    // import fetch from 'node-fetch';
    const fetch = require('node-fetch')
    
    const checkStatus = (res) => {
      if (res.ok) { // res.status >= 200 && res.status < 300
          return res.json()
      } else {
          throw new Error(res.statusText);
      }
    }
    
    exports.handler = async function(event, context, callback) {
      try {
        const response = await fetch('https://cat-fact.herokuapp.com/facts')
        const data = await checkStatus(response)
        callback(null, {
          statusCode: 200,
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(data)
        })
      } catch (error) {
        callback(error)
      }
    }
    
    

    Note: Netlify functions are just AWS functions and you can read about the AWS Lambda Function Handler in the AWS docs

    For async functions, you return a response, error, or promise to the runtime instead of using callback.

    I like to use the method recommended in the docs, since it is supported and save the callback for non-async functions.

    Here is a working version returning a promise to the async function instead.

    // import fetch from 'node-fetch';
    const fetch = require('node-fetch')
    
    exports.handler = async (event, context) => {
      return new Promise((resolve, reject) => {
        fetch('https://cat-fact.herokuapp.com/facts')
        .then(res => {
          if (res.ok) { // res.status >= 200 && res.status < 300
            return res.json();
          } else {
            resolve({ statusCode: res.status || 500, body: res.statusText })
          };
        })
        .then(data =>{
          const response = {
            statusCode: 200,
            headers: { 'content-type': 'application/json' },
            body: JSON.stringify(data)
          }
          resolve(response);
        })
        .catch(err => {
          console.log(err)
          resolve({ statusCode: err.statusCode || 500, body: err.message })
        })
      })
    }
    
    

    This is just an example and there are other ways to handle the errors, but this example resolved the response rather than rejecting. It is not tested as a production example, but gives you an idea of the difference.

    Here are some examples of Netlify functions.