Serving a React app from Node while also having an API

Viewed 62

So I am trying to have a Node/React setup on Ubuntu, inside an Nginx server.

The React app works fine, however when I try to have API endpoints (in Node) for the React app to call, those endpoints don't work - neither for the app, nor for going to those endpoints from a browser.

This is what some of the code looks like:

const express = require('express');
const path = require('path');

const app = express();

app.use(express.static(path.join(__dirname, 'client/build')));
app.use(express.json());

app.get('/api/contactinfo', async (req, res) => {
    let contactinfo = await Information.findAll({
        plain: true,
        attributes: ["phone", "email", "address"],
    });
    res.json(contactinfo);
});

app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname + '/client/build/index.html'));
});

const port = process.env.PORT || 5000;
app.listen(port);

So for example, in this part, I might go to the React app's contact page (example.com/contact), and that loads fine. But the API call that the React app makes to the node server fails. So it seems like the React routing is working, but not the Node routing.

Likewise, if I go to just the Node API directly (example.com/api/contactinfo), that fails with a 502 bad gateway.

My Nginx setup looks like this:

location / {
    proxy_pass http://localhost:5000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

I've also got some SSL cert setup stuff as well, though I'm not sure if that is relevant.

When I look at the Nginx error.log, this is what I see:

2020/09/02 15:36:54 [error] 1424#1424: *325 upstream prematurely closed connection while reading response header from upstream, client: 35.3.25.220, server: exampledomain.com, request: "GET /api/contactinfo HTTP/1.1", upstream: "http://127.0.0.1:5000/api/contactinfo", host: "exampledomain.com"

What exactly is causing my Node app API endpoints to fail? I've tried increasing the timeout, and several other things and nothing seems to be working - I've been trying to fix this problem for hours, but for some reason, despite the fact that I can successfully get React to load, I can't get any Node endpoints to do so.

How do I fix this?

4 Answers

The “problem” is the certbot. If you have a self sign certificate it means that .... depending on how you configúrate certbot, basically now everything runs under port 443 not 80. So if on yow request you have some like `http://www domain com/api/endpoint. You’ll get the error you are getting. What you need to do is to use the https module from node.

import bodyParser from "body-parser";
import express from "express";
import fs from "fs";
import path from "path";
// import https
import https from "https";

import { routes } from "./routes";
import { logger } from "./utils/logger";
// The paths of those files keys will depend on where certbot stored them
const servOptions = {
    cert: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/fullchain.pem"),
    key: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/privkey.pem"),
    ca: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/chain.pem"),
};

/**
 * Createas an instance of the framework `fsexpress`.
 *
 * @returns {import("Express").Express} `express` instance.
 */
logger.info("express::expressApp");

const app: Express = express();

const build = path.join(__dirname, "../html");

app.use(express.static(build));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true, limit: "5m" }));
app.use("/static", express.static(build));
app.use(`${process.env.ENETO_CURRENT}`, routes());
app.use("*", function (req, res) {
    return res.status(200).sendFile(`${build}/index.html`);
})

const secure = https.createServer(servOptions, app);

secure.listen(Number(process.env.PORT), () => {
    console.log("servOptions: ", servOptions);
    logger.info("APP RUNNING");
});

I don't know the error in your implementation but here how I would do it.

// Nginx
server {
  charset utf-8;
  listen 80 default_server;
  server_name _;

  # front-end files
  location / {
    root /opt/front-end;
    try_files $uri /index.html;
  }

  # node api reverse proxy
  location /api/ {
    proxy_pass http://localhost:5000/;
  }
}
// folder structure
.
opt
+-- front-end
|   +-- react app build
+-- back-end
    +-- node app
// Node app
...
app.listen(5000);

Based on the article How to Deploy a MEAN Stack App to Amazon EC2

The issue was that I had a misunderstanding of how Node logging worked under Nginx.

I thought any problems or console logs with the Node setup would be logged to the nginx/error.log.

This was not in fact the case.

The Node setup had another problem, which made trying to access my endpoints crash.

The solution here is better logging that is not dependent on any sort of Nginx logs.

Solution

I just got this problem, and there are a couple of things you need.

First

you still need this

import bodyParser from "body-parser";
import express from "express";
import fs from "fs";
import path from "path";
// import https
import https from "https";

import { routes } from "./routes";
import { logger } from "./utils/logger";
// The paths of those files keys will depend on where certbot stored them
const servOptions = {
    cert: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/fullchain.pem"),
    key: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/privkey.pem"),
    ca: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/chain.pem"),
};

/**
 * Createas an instance of the framework `fsexpress`.
 *
 * @returns {import("Express").Express} `express` instance.
 */
logger.info("express::expressApp");

const app: Express = express();

const build = path.join(__dirname, "../html");

app.use(express.static(build));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true, limit: "5m" }));
app.use("/static", express.static(build));
app.use(`${process.env.ENETO_CURRENT}`, routes());
app.use("*", function (req, res) {
    return res.status(200).sendFile(`${build}/index.html`);
})

const secure = https.createServer(servOptions, app);

secure.listen(Number(process.env.PORT), () => {
    console.log("servOptions: ", servOptions);
    logger.info("APP RUNNING");
});

Second

on yow location

location / {
    proxy_pass http://localhost:5000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

more specific on the proxy_pass http://localhost:5000; change the http or add an s at the end like this proxy_pass https://localhost:5000;

location / {
    proxy_pass https://localhost:5000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}
Related