Reputation: 2259
So I have a Next.js React component that is using getInitialProps to call the backend for some data before initial page load. Here is what that looks like:
static async getInitialProps({req, query}){
console.log('inside getInitialProps')
if (req){
var pinReturn = await axios.post(process.env.serverADD+'getPinData', {withCredentials: true})
.then(response=>{
console.log('value of response: ', response)
return response.data.pins
})
.catch(error=>{
console.log('value of error: ', error)
return({})
})
}
return {pinData: pinReturn}
}
This is pretty self explanatory I think - and it works when my backend is a simple docker container. However, now I am using nginx and docker-compose to spin up my front and back end. Currently my process.env.serverADD
is equal to http://localhost:80/back/
.
My NGINX is :
server {
listen 80;
listen [::]:80;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name example.com www.example.com localhost;
location /back {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://nodejs:8000;
}
location / {
proxy_pass http://nextjs:3000;
}
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
}
And here is my docker-compose (this is similar to what is here: https://www.digitalocean.com/community/tutorials/how-to-secure-a-containerized-node-js-application-with-nginx-let-s-encrypt-and-docker-compose#step-2-%E2%80%94-defining-the-web-server-configuration):
version: '3'
services:
nodejs:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
container_name: nodejs
restart: unless-stopped
networks:
- app-network
nextjs:
build:
context: ../.
dockerfile: Dockerfile
ports:
- "3000:3000"
container_name: nextjs
restart: unless-stopped
networks:
- app-network
webserver:
image: nginx:mainline-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- web-root:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/lib/letsencrypt
depends_on:
- nodejs
- nextjs
networks:
- app-network
volumes:
certbot-etc:
certbot-var:
web-root:
driver: local
driver_opts:
type: none
device: /
o: bind
networks:
app-network:
driver: bridge
If I call http://localhost:80/back/getPinData
from curl then I get the data I'm expecting. It routes it through nginx, hits my docker-compose, and then routes to the correct location in my nodejs app. However, if I call that API from getInitialProps
I get the following error:
inside getInitialProps
value of error: { Error: connect ECONNREFUSED 127.0.0.1:80
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1104:14)
errno: 'ECONNREFUSED',
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 80,
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=utf-8',
'User-Agent': 'axios/0.18.0',
'Content-Length': 24 },
method: 'post',
url: 'http://localhost:80/back/getPinData',
data: '{"withCredentials":true}' },
request:
Writable {
_writableState:
WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
emitClose: true,
bufferedRequestCount: 0,
corkedRequestsFree: [Object] },
writable: true,
_events:
[Object: null prototype] {
response: [Function: handleResponse],
error: [Function: handleRequestError] },
_eventsCount: 2,
_maxListeners: undefined,
_options:
{ maxRedirects: 21,
maxBodyLength: 10485760,
protocol: 'http:',
path: '/back/getPinData',
method: 'post',
headers: [Object],
agent: undefined,
auth: undefined,
hostname: 'localhost',
port: '80',
nativeProtocols: [Object],
pathname: '/back/getPinData' },
_ended: false,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 24,
_requestBodyBuffers: [ [Object] ],
_onNativeResponse: [Function],
_currentRequest:
ClientRequest {
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: true,
socket: [Socket],
connection: [Socket],
_header:
'POST /back/getPinData HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/json;charset=utf-8\r\nUser-Agent: axios/0.18.0\r\nContent-Length: 24\r\nHost: localhost\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/back/getPinData',
_ended: false,
res: null,
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Circular],
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]: [Object] },
_currentUrl: 'http://localhost:80/back/getPinData' },
response: undefined }
What is going on here? Why does getInitialProps appear to work when not using NGINX, but fails when using the reverse-proxy?
Upvotes: 4
Views: 2970
Reputation: 16502
When resolving URLs from within your container, localhost
is no longer your actual host machine. Instead, it's the loopback adapter of the container itself.
If you're trying to call the Nginx server from within the Docker container, you need to use http://webserver:80/back/getPinData
.
You've already set up your docker-compose.yml
to use a bridge network, so this should work right away.
Upvotes: 7