Hi @danalloway
Good news: I’ve got it working so it will be a case of seeing what I’m doing compared to your app to see what differs.
To test it, I’ve made a web and api Fly app, with Node 17, using Fastify. The web app calls the api app using the .internal
hostname and I get a response back from it
See https://fastify-web.fly.dev/call-api
(I’ll delete that app at some point, but it’s there as of now)
The files from the two apps to compare to yours - of course it’s pretty basic!
API
package.json
{
"name": "fastify-api",
"version": "1.0.0",
"description": "",
"main": "server.js",
"type": "module",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"fastify": "^3.27.1"
}
}
server.js
'use strict'
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
fastify.get('/api', async function (request, reply) {
return { hello: 'this is a response from the api' }
})
const start = async () => {
try {
fastify.log.info('Starting api server ...');
await fastify.listen(8080, '::')
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
fly.toml
# fly.toml file generated for fastify-api on 2022-02-05T17:38:04Z
app = "fastify-api"
[experimental]
private_network = true
[[services]]
internal_port = 8080
protocol = "tcp"
[services.concurrency]
hard_limit = 50
soft_limit = 25
[[services.tcp_checks]]
interval = 5000
timeout = 2000
Dockerfile
FROM node:17-slim
USER node
RUN mkdir -p /home/node/app
WORKDIR /home/node/app
COPY --chown=node:node package.json .
COPY --chown=node:node package-lock.json .
RUN npm ci --only=production
COPY --chown=node:node . .
CMD ["node","server.js"]
… and then the web app calls that api. Its files are as follows …
WEB
package.json
{
"name": "fastify-web",
"version": "1.0.0",
"description": "",
"main": "server.js",
"type": "module",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"fastify": "^3.27.1",
"node-fetch": "^3.2.0"
}
}
server.js
'use strict'
import Fastify from 'fastify'
import fetch from 'node-fetch';
const fastify = Fastify({
logger: true
})
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
fastify.get('/call-api', async function (request, reply) {
try {
fastify.log.info('Call the api app using fetch ...');
const results = await fetch('http://fastify-api.internal:8080/api')
return results.json()
} catch (err) {
fastify.log.error(err)
return { success: false }
}
})
const start = async () => {
try {
fastify.log.info('Starting web server ...');
await fastify.listen(8080, '::')
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
fly.toml
# fly.toml file generated for fastify-web on 2022-02-05T17:08:46Z
app = "fastify-web"
[experimental]
private_network = true
[[services]]
internal_port = 8080
protocol = "tcp"
[services.concurrency]
hard_limit = 50
soft_limit = 25
[[services.http_checks]]
interval = 5000
method = "get"
path = "/"
protocol = "http"
timeout = 2000
tls_skip_verify = true
[[services.ports]]
handlers = ["tls", "http"]
port = "443"
[[services.tcp_checks]]
interval = 5000
timeout = 2000
Dockerfile
FROM node:17-slim
USER node
RUN mkdir -p /home/node/app
WORKDIR /home/node/app
COPY --chown=node:node package.json .
COPY --chown=node:node package-lock.json .
RUN npm ci --only=production
COPY --chown=node:node . .
CMD ["node","server.js"]
So … it could be your fetch
needs http as the protocol and the port? For me, it did not work without the port appended (which makes sense, as I guess it defaults to 80 otherwise).
And/or change to listen on ‘::’ rather than 0.0.0.0 (which suggests to me IPv4, not IPv6).
I’m actually not sure if the private networking in my fly.toml
is even needed: that may be a legacy of when they first added it, and I’ve left it in ever since. But I have that too.
Finally, if still no luck, you can double-check the private networking is working by adding an extra route in your fastify server to check it resolves e.g
import dns from 'dns';
fastify.get('/test-the-dns', async (request, reply) => {
let records = [];
try {
records = await dns.promises.resolveTxt(`_apps.internal`)
fastify.log.info(records)
} catch (err) {
fastify.log.error(err)
return { "error": err }
}
let appset = records[0][0]
if (appset == "") return [];
return appset.split(",");
})
Good luck!