Netlify Dev Functions Locally troubleshooting

Hi everybody,

In order to solve CORS issues, I am trying to deploy a proxy using node-fetch to call an external API.

I have carefully followed this tutorial, this one too and carefully read this issue.

However, the problem still persists.

When a call my serverless function, I get returned a node readable string. Instead I was expecting to receive an Object containing some data.

I git 2 other developers investing at it and couldn’t understand either.

  1. The package.json includes "node-fetch": "^2.6.0"

  2. The netlify.toml looks like this

[build]
  command = "npm run build"
  functions = "functions"
  publish = "dist"

[dev]
  framework = "vue"
  1. The node-fetch function inside the folder functions called test-api.js looks like this
// functions/test-api.js

const fetch = require("node-fetch");
exports.handler = async () => {
    const response = await fetch("https://icanhazdadjoke.com/")
    .then(res => res.json())
    .catch(err => console.error(err));

  return {
    statusCode: 200,
    body: JSON.stringify(response)
  };
};
  1. The app is compiled with "@vue/cli-service": "~4.4.1"

  2. The webpack.config.js looks like this

// webpack.config.js

module.exports = {
  test: /\.(graphql|gql)$/,
  exclude: /node_modules/,
  loader: "graphql-tag/loader"
};
  1. The vue.config.js file is untouched

  2. When I run netlify dev, the app is deployed on http://localhost:3000 (I specified it)

  3. When I execute the call fetch("/.netlify/functions/test-api").then(res => console.log(res)), I get the following response:

I have tried different approaches and tried to call different APIs, but it always resulted the same.

This situation is embarrassing because it seems to work for everybody except for me. And even my more experienced fellow developers couldn’t help me out.

If somewhat could help me understand how to solve this issue, I would highly appreciate.

Thanks

Hi @mcyouks, welcome to the Community!

Looking at your function, it looks like you are returning the whole response as your returned response body. Note that node-fetch’s response includes other things like status code and other things. You’ll want to return only the payload as your body. In this case, the response.body will have the data you will need. Let me know if that helps.

Hey @Dennis, thank you very much for your response.

Unfortunately, the workaround you propose didn’t solve the core problem.

However, I could further investigate the issue, and here is what I found out:

:pushpin: SETUP

I am have followed the steps in this tutorial (Solve CORS once and for all with Netlify Dev).

:mag_right: OBSERVATION 1)

When developing locally, functions server is listening to a port that is different than the port where the app is running. So the netlify functions cannot be called without triggering CORS issues.

Indeed, while functions server is listening on a given port…

*Note: the port number is not consistent, it changes everytime I run netlify dev

… the app runs on a different port

As a consequence, calling http://localhost:<functions_server_port>/.netlify/functions/node-fetch from http://localhost:3000/ triggers CORS errors.

:mag_right: OBSERVATION 2)

In production, calling /.netlify/functions/node-fetch works as expected.

As you can notice below, calling https://<my_domain>/.netlify/functions/node-fetch returns the expected result (see picture below).

As a result, we can infer that in production, the functions server listen on the same port as the port where the app is running.

The issue thus becomes the following:

:thinking: PROBLEM

How might we force functions server to listen on port 3000 in development?

I have investigated this issue (Netlify Dev Port command not working), and installed the lastest version netlify-cli@2.59.0.

However, running netlify dev -p 3000 still doesn’t have any effect: the functions server still listens to a different port.

Could someone help me figure that out please ?

Thanks

Hey @mcyouks :wave:t2:

I’ll echo Dennis in saying welcome to the community as well :slight_smile:
Let’s see if we can’t get this sorted out. Netlify Functions are designed with “just works” in mind, so let’s see if we can’t get things working for you :grin:

As a side note, if you’re truly just wanting a proxy pass-through for an external API, you may want to look into Netlify Redirects with passing a 200 status - it’s a super simple masked redirect and that may help you out. Check these docs out and see if that fits the need.

Otherwise, there’s no reason you can’t roll your own and by-golly let’s do it!

I’m actually going to jump up to your first notes – before @Dennis’s comment (which I agree with) – but in your screenshot of your dev tools, you’re seeing the response object logged and it actually looks fine. I want to note that you ran fetch("/.netlify/functions/test-api").then(res => console.log(res)) in dev tools and looked at the response. It’s key to recall that the fetch sequence is a two-step sequence, not one step. The object that results from the fetch promise is the response object and it contains metadata about the response - what the url was, what status code the server sent back, and a few other meta-concerns about the ‘stuff’ you asked for, but it doesn’t contain the actual content of what you’re asking for in the request. You have to take the second step and go ahead and retrieve that data. That’s why in your screenshot you see “body: ReadableStream” - because the body is the actual content and it’s a ReadableStream that’s available, but you have to intentionally read it.

We can contrast that with the code you initially used in the function itself.

const response = await fetch("https://icanhazdadjoke.com/")
    .then(res => res.json())

With the key difference here being the .json(). In your console log, you printed off what the res / response object is. In your function, you call .json() which is the intentional action of reading the ReadableStream and converting it to JSON format for use in code.

I believe if you would’ve had your console log statement read:

fetch("/.netlify/functions/test-api").then(res => console.log(res.json()))

instead of the original

fetch("/.netlify/functions/test-api").then(res => console.log(res))

You’d actually be in good shape :slight_smile:


Okay let’s talk about ports!

This can get a little bit complicated so I’m going to pull in a diagram from Netlify Dev’s docs which I highly recommend reading for a better understanding of what’s going on when you run Netlify Dev.

Netlify Dev is basically a local plumbing service. It takes over responsibility for pushing requests and ports and things to where they need to go so that you don’t need to worry about it. In fact, if you do start trying to push requests around yourself, it’s probably not going to go super well :grimacing: – for instance, you pretty much never want to try to hit the local Functions Server Port yourself / manually. The Dev CLI only outputs which port it’s running on for debugging purposes, not for developers to try to use (I think).

Anyway let me walk through that diagram. Sounds like you’re using using Vue and I think Vue’s default port for local development when just running the Vue app is :8080. For the sake of the diagram, let’s assume Vue is actually running on port :5000. The point of Netlify Dev is that it knows what port your project is running on (5000, as shown above) and it acts as a pass-through mechanism for your site generator. So when you run Netlify Dev, you’re going to hit localhost:8888 instead of localhost:5000. They should both load the same site and show the same content, but when you go through localhost:8888, all of the other netlify tooling is hooked up too. This is fundamentally how Netlify Functions works. When you have a script on your page that calls out to (relative link) /.netlify/functions/call-me-im-a-function Netlify Dev (or in production, Netlify’s CDNs) catch that you’re calling /.netlify/functions/* and instead of just fetching your ‘normal’ static content, push that request over to the Functions system to be given to the correct function (call-me-im-a-function, in this case).

This follows the explanation-of-ports-in-netlify-dev on the netlify dev docs (which, I will say, can be tricky to find so don’t feel bad if you never saw them!)

So the point here is that you’re going to want to not worry about what the functions server port is or try to change the Netlify Dev port (this topic you referred to) - you just need to hit your site locally from the Netlify Dev port rather than the normal port for Vue, and everything from there on our should just work.

It’s a different paradigm and a little complex, but I hope that makes some sense. Netlify Dev running locally is awesome, just stick to the url/port that comes up in the box once it’s done initiating:


Happy to go into more detail but I hope that’s helpful :slight_smile:


Jon

1 Like

Hey @jonsull :wave:

Thank you very much for putting so much effort into providing such an insightful answer. I highly appreciate that :slight_smile:

I have carefully read and followed your instructions. However, the problem still persists: my requests are not hitting the functions server (nor in dev, neither in prod) :cry:

:pushpin: Note: I used the default url/port that comes out of the box (namely :8080)

Then, I have tried the following approaches:

  1. I tried calling the external api with the functions server method, using the relative link /.netlify/functions/call-me-a-function.

  2. I also tried calling the external api with the redirects method (configuring both with _redirects file and netlify.toml file), using the relative link /api.

None of those two approaches have been conclusive :disappointed:

In order to provide you with the most insightful information, I am going to decompose the thread as follow:

:control_knobs: Setup: I am going to explicit the function called, the current Vue and Netlify settings.

:toolbox: Functions Server approach: I am going to explicit the approach and show the results (in dev and prod)

:twisted_rightwards_arrows: Redirects approach: I am going to explicit the approach and show the results (in dev and prod)

Let’s go !

:control_knobs: Setup

First, I removed the config PORT=3000 from my .env file, so that the app would naturally run on the default port, namely :8080

The main information response we are going to query is the following variable message defined as follow. This variable is defined inside my setup() method (see Vue 3 composition API setup method) :point_down:

Additionally, the node-fetch function located at functions/node-fetch/node-fetch.js is defined as follow :point_down:

Finally , let’s remind the Netlify configuration file netlify.toml at this point :point_down:

That’s it for the initial setup! Let’s investigate the Functions Server approach :toolbox:

:toolbox: Functions Server approach

First, I am going to run the app using netlify dev. The app is running on port :8080 (Vue’s default port).

In development mode, fetching /.netlify/functions/node-fetch returns a 404 error.

In production mode, fetching /.netlify/functions/node-fetch returns a 404 error as well.

Note 1: In production only, hitting <my_domain>/.netlify/functions/node-fetch returns the desired output :point_down:

Note 2: I am running the app in PWA mode, and I saw that could cause issue in production in this topic, here and here too.

That’s it for the Functions Server approach! Let’s investigate the Redirects approach :twisted_rightwards_arrows:

:twisted_rightwards_arrows: Redirects approach

I have tried two variants (as explained in this article), and both of them gave the same non-conclusive result :frowning:

Since the results are the same, I am going to first explicit the variants.

Variant 1 : Use public/_redirects file

Since I am running a SPA application, I already had the config /* /index.html 200 in the _redirects file, located in the public folder.

Thus, I added the extra line /api/* https://icanhazdadjoke.com/:splat 200 to the file :point_down:

Variant 2 : Use netlify.toml file

I used Netlify configuration file to setup redirects rule. The netlify.toml looked as follow :point_down:

Results

In development mode, fetching /api returns a 404 error.

In production mode, fetching /api returns a 404 error as well.

As a conclusion, after trying all the possible workarounds, I am still stuck on this very simple issue :sleepy:

If anyone knows how to help me solve the situation, I would be the most grateful person ever!

Regards,
Andréas

1 Like

Thanks for all the details :slight_smile:

I want to take the time to iron out how you’re running this project locally before digging into any of the above topics further.

Essentially, you shouldn’t be hitting localhost:8080 - that would be directly hitting the Vue development server. You want to be hitting the Netlify Dev server. Hitting the Vue dev server directly would account for all the problems you’re seeing :stuck_out_tongue:

Can you tell me what command you’re using to fire up your local development process and share the output?

You should get a box displayed in your terminal output like the one I shared in my last reply: the one that says “Server now ready on http://localhost:8888”. From there on out, you’ll want to do all development / in-browser testing by hitting localhost:8888, not localhost:8080


Jon

Hey @jonsully,

Thanks for your quick reply.

I use the command netlify dev to fire up the local development.

I do have a boxed message appearing very early in the logs (Note: this message disappears quicly behind the very long list of deployment logs that come after). :point_down:

However, hitting localhost:8888 loads indefinitely, and nothing shows up in the browser :frowning:

One more thing. After trying several time to hit http://localhost:8888, I had the following error message displaying in the terminal :point_down:

Could you help me better understand what’s going on please?

Regards,


Andréas

Awesome :grin: I think we’re making progress!

So I believe what’s probably happening here (without seeing more of the terminal output from when you begin netlify dev until those ‘socket hang up’ messages) is that the Netlify Dev server isn’t correctly hooking up to the Vue development server. Are you running the Vue development server on its default port? That’s what the Netlify Dev server should be trying to listen on


Jon

Great, thanks :slight_smile:

Since I removed PORT=3000 from the .env file, there is no other setting that should prevent Vue from running on its default port 8080. So I assume it is the case.

Here are the output (it’s pretty long though).

◈ Netlify Dev ◈
◈ Injected build setting env var: VUE_APP_AUTH_DOMAIN
◈ Injected build setting env var: VUE_APP_WEBSOCKET_ENDPOINT
◈ Injected build setting env var: VUE_APP_AUTH_CLIENT_ID
◈ Injected build setting env var: VUE_APP_AUTH_PROFILE_ID
◈ Injected build setting env var: VUE_APP_WORKSPACE_ENDPOINT
◈ Injected build setting env var: VUE_APP_WORKSPACE_ID
◈ Overriding the following env variables with .env file:
◈ Starting Netlify Dev with vue

◈ Functions server is listening on 60589

┌─────────────────────────────────────────────────┐
│ │
│ ◈ Server now ready on http://localhost:8888
│ │
└─────────────────────────────────────────────────┘

vuejs-todos-app@0.1.0 serve <my_app_path>
vue-cli-service serve

e[44me[30m INFO e[39me[49m Starting development server…
To enable GQL files in ESLint, set the pluginOptions.apollo.lintGQL project option to true in vue.config.js. Put false to hide this message.
You also need to install eslint-plugin-graphql and enable it in your ESLint configuration.
[webpack.Progress] 0% compiling
[webpack.Progress] 10% building 0/0 modules 0 active

[…]

webpack.Progress] 70% building 1219/1219 modules 0 active
[webpack.Progress] 70% building 1219/1219 modules 0 active
[webpack.Progress] 70% finish module graph
[webpack.Progress] 70% finish module graph FlagDependencyExportsPlugin
[webpack.Progress] 70% sealing
[webpack.Progress] 70% sealing WarnCaseSensitiveModulesPlugin
[webpack.Progress] 72% basic dependencies optimization
[webpack.Progress] 72% dependencies optimization
[webpack.Progress] 73% advanced dependencies optimization
[webpack.Progress] 73% after dependencies optimization
[webpack.Progress] 71% chunk graph
[webpack.Progress] 71% after chunk graph
[webpack.Progress] 71% after chunk graph WebAssemblyModulesPlugin
[webpack.Progress] 74% optimizing
[webpack.Progress] 74% basic module optimization
[webpack.Progress] 75% module optimization
[webpack.Progress] 75% advanced module optimization
[webpack.Progress] 76% after module optimization
[webpack.Progress] 76% basic chunk optimization
[webpack.Progress] 76% basic chunk optimization EnsureChunkConditionsPlugin
[webpack.Progress] 76% basic chunk optimization RemoveEmptyChunksPlugin
[webpack.Progress] 76% basic chunk optimization MergeDuplicateChunksPlugin
[webpack.Progress] 77% chunk optimization
[webpack.Progress] 77% advanced chunk optimization
[webpack.Progress] 77% advanced chunk optimization SplitChunksPlugin
[webpack.Progress] 77% advanced chunk optimization RemoveEmptyChunksPlugin
[webpack.Progress] 77% after chunk optimization
[webpack.Progress] 78% module and chunk tree optimization
[webpack.Progress] 78% after module and chunk tree optimization
[webpack.Progress] 79% basic chunk modules optimization
[webpack.Progress] 80% chunk modules optimization
[webpack.Progress] 80% advanced chunk modules optimization
[webpack.Progress] 81% after chunk modules optimization
[webpack.Progress] 81% module reviving
[webpack.Progress] 81% module reviving RecordIdsPlugin
[webpack.Progress] 82% module order optimization
[webpack.Progress] 82% advanced module order optimization
[webpack.Progress] 83% before module ids
[webpack.Progress] 83% before module ids NamedModulesPlugin
[webpack.Progress] 83% module ids
[webpack.Progress] 84% module id optimization
[webpack.Progress] 84% module id optimization
[webpack.Progress] 85% chunk reviving
[webpack.Progress] 85% chunk reviving RecordIdsPlugin
[webpack.Progress] 85% chunk order optimization
[webpack.Progress] 85% chunk order optimization OccurrenceOrderChunkIdsPlugin
[webpack.Progress] 86% before chunk ids
[webpack.Progress] 86% before chunk ids NamedChunksPlugin
[webpack.Progress] 86% chunk id optimization
[webpack.Progress] 87% after chunk id optimization
[webpack.Progress] 87% record modules
[webpack.Progress] 87% record modules RecordIdsPlugin
[webpack.Progress] 87% record chunks
[webpack.Progress] 87% record chunks RecordIdsPlugin
[webpack.Progress] 88% hashing
[webpack.Progress] 88% after hashing
[webpack.Progress] 88% after hashing HotModuleReplacementPlugin
[webpack.Progress] 89% record hash
[webpack.Progress] 89% module assets processing
[webpack.Progress] 90% chunk assets processing
[webpack.Progress] 90% additional chunk assets processing
[webpack.Progress] 90% additional chunk assets processing HotModuleReplacementPlugin
[webpack.Progress] 91% recording
[webpack.Progress] 91% recording HotModuleReplacementPlugin
[webpack.Progress] 92% additional asset processing
[webpack.Progress] 92% chunk asset optimization
[webpack.Progress] 93% after chunk asset optimization
[webpack.Progress] 93% asset optimization
[webpack.Progress] 94% after asset optimization
[webpack.Progress] 94% after seal
[webpack.Progress] 95% emitting
[webpack.Progress] 95% emitting HtmlWebpackPlugin
[webpack.Progress] 95% emitting vue-cli:pwa-html-plugin
[webpack.Progress] 95% emitting CopyPlugin
[webpack.Progress] 98% after emitting
[webpack.Progress] 98% after emitting CopyPlugin
e[42me[30m DONE e[39me[49m e[32mCompiled successfully in 17455mse[39me[90m16:02:26e[39m

[webpack.Progress] 100%

App running at:

  • Local: e[36mhttp://localhost:e[1m8081e[22m/e[39m
  • Network: e[36mhttp://192.168.1.61:e[1m8081e[22m/e[39m

Note that the development build is not optimized.
To create a production build, run e[36mnpm run builde[39m.

Does it provide you further information ?

Regards


Andréas

Interesting. So a few things to note - Netlify Dev is connecting to something on port 8080. I’m pretty sure it won’t spit out the big “Server now ready” box unless it does.

Oddly enough though, I don’t think it’s your Vue development server it’s connecting to. I wonder if something else (maybe another service on your machine, maybe a different instance of your Vue dev server that got orphaned or lost somewhere?) is using port 8080. I ask because if you look at the bottom of the log where Webpack finishes building then Vue says “App running at:” it’s actually running on port 8081. :thinking: I’m not a Vue expert but I think that only happens when port 8080 is already taken.

Short of digging down the rabbit hole of what’s using your ports, can you try running

netlify dev --targetPort 8081


Jon

Hey @jonsully ,

Thanks for your answer.

Note: I noticed that the default port went from :8080 no :8081 after I ran pkill node in my terminal (I wanted to make sure all my port were freed).

I could run netlify dev --targetPort 8081 and hit the app on http://localhost:8888.

Fetching /.netlify/functions/node-fetch works fine and returns the following result:

Looks like we are progressing.

Remaining challenges

  1. Getting rid of the Promise Object and get the data

  2. Make it to production

We’re almost there :muscle:

Could you help me run the last mile?

Regards

_
Andréas

Awesome! Glad to see everything lined up and is working well with Netlify Dev :grin: Just remember to do everything on localhost:8888 from here on out and all of the local tooling will be at your disposal :raised_hands:

Could you help me run the last mile?

I can try :stuck_out_tongue:

I think we may just need to tweak the command you’re running in the console. You’re not actually console.log’ing anything right now. Why don’t you try this:

fetch('/.netlify/functions/node-fetch').then(res => res.json()).then(data => console.log(data))

You should see an object logged in the console (data) and if you open that up, you should see that object contains a field called msg :grin:


Jon

True, that worked :+1:

I am just expliciting what I had to do.

Since I am using Vue 3 Composition API

import { ref } from “@vue/composition-api”;

export default {
setup() {

const message = ref(null);

fetch("/.netlify/functions/node-fetch")
  .then(response => response.json())
  .then(({ msg }) => {
    message.value = msg;
  });

return { message }

}
}

Note: This code could be better optimized using the onMounted cycle hook.

_
Andréas

Makes sense! Yeah having an async Vue handler decompose the response into msg and reassign it to a view component is totally normal, we just have to be a little more explicit and pin-pointed when emulating that request directly from the console.

So are you all set? Everything good? :grin:


Jon

I guess we are for the moment :slight_smile:

I’ll have to make it to production. If you remember, we still had some issues in production.

Eventually I’ll be playing with the netlify.toml file and adjust the targetPort option accordingly.

I’ll keep you posted :wink:

Thanks :slight_smile:

Haha well real quick before you get into playing with nuts and bolts there - why don’t you share your netlify.toml and we can make sure it looks good. The targetPort option is just for local dev, but you shouldn’t have any problems when shipping code to Netlify and everything should work the same as far as your app goes.


Jon

Sure.

My netlify.toml looks like follow at the moment:

[build]
command = “npm run build”
functions = “functions”
publish = “dist”

[dev]
framework = “vue”

1 Like

Nice! Well keep it simple and you should be fine once you send things live :slight_smile: Feel free to open up a new topic on the Community if you have any issues!

Cheers :slight_smile:


Jon

Website is live! I barely had nothing to add.

It works just fine! Thanks @jonsully

1 Like

Love to hear it :slight_smile: Glad I could help!

1 Like