Node JS : Allow only server side calls to my api

You can use the express-ipfilter package and only apply it to certain routes you want to protect:

const express = require('express'),
      ipfilter = require('express-ipfilter').IpFilter;

// Whitelist the following IPs
const ips = ['127.0.0.1'];

// Create the route
app.get("/securePath", ipfilter(ips, {mode: 'allow'}), (req, res) => {
  // only requests from 127.0.0.1 (localhost/loopback) can get here
});

app.get("/openPath", (req, res) => {
  // all requests can get here
});

app.listen(3000);

If you are using Node behind a proxy, you may need to configure the proxy to set a header with the actual IP and then pass the ipfilter function a function in the detectIp property to the second parameter.

Let's say you are using nginx and have it configured to send the original IP through the x-Real-IP header, you can pass this function to ipfilter:

const express = require('express'),
  ipfilter = require('express-ipfilter').IpFilter,
  ips = ['127.0.0.1'];

app.get("/securePath", ipfilter(ips, {mode: 'allow', detectIp: getIp}), (req, res) => {
  // only requests from 127.0.0.1 (localhost/loopback) that go through the proxy can get here.
});

app.get("/openPath", (req, res) => {
  // all requests can get here
});

app.listen(3000);

function getIp(req) { return req.headers["X-Real-IP"] }

You should use a similar authentication/authorization as for the routes that have JWT authentication from the clients.

This means that the caller service should also authenticate using a JWT token, having a special role of service or something like that (this is 100% your decision on what convention you choose). That token should be signed by the caller and verified by the receiving microservice.

This solution has the advantage that it does not depends on the infrastructure, it works the same no matter where the services are deployed.