0

I'm using Angular 7 to send a http request to an Express 4 backend code, but keep getting a 404 response in return. I think it might be an issue with the way I've listed the path for the http request, but not sure. This is the first time I'm attempting something like this. I have set up a proxy.conf.js file in Angular to allow from cross origin communication (angular runs on localhost:4200, express on localhost:8085). My Express code is saved under C:/ABC/myapp. Here's the relevant code:

Angular proxy.conf.js file:

{
  "/": {
    "target": "http://localhost:8085",
    "secure": false,
    "logLevel": "debug"
  }
}

Angular service code which sends the http request:

export class ContactUsService {
private url = '/contactus';
private result: any;
message: string;

addMessage(contactUsMessage: contactUsInterface): Observable<contactUsInterface> {
  return this.http.post<contactUsInterface>(this.url, contactUsMessage). (retry(3));
};

constructor(private http: HttpClient) {}
};

Express app.js code:

var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var createError = require('http-errors');
var express = require('express');
var logger = require('morgan');
var mongoose = require('mongoose');
var path = require('path');
var winston = require('winston');
var contactUsRouter = require(path.join(__dirname, 'routes', 'contactUs'));
var app = express();

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/contactus', contactUsRouter);

app.use(function(req, res, next) {
  next(createError(404));
});
app.use(function(err, req, res, next) {
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

contactUs.js code (for contactUsRouter):

var express = require('express');
var router = express.Router();
var contactUsMessage = require('../models/contactUsMessage');

  router.route('/contactus')
    .get(function(req,res,next){
      res.send("Hello")
    })
    .put(function(req,res,next){
      res.send("Hello")
    });

module.exports = router;

When I reach the contactus page (url: localhost:4200/contactus) and execute the submit button for the form, I get the following errors: In the browser console: "HTTP404: NOT FOUND - The server has not found anything matching the requested URI (Uniform Resource Identifier). (XHR)POST - http://localhost:4200/contactus"

In the npm log: "POST /contactus 404 3.081 ms - 2128 Error: Failed to lookup view "error" in views directory "C:\ABC\myapp\views".

Any words of wisdom on what I'm doing incorrectly?

Alexander Staroselsky
  • 37,209
  • 15
  • 79
  • 91
coder101
  • 383
  • 4
  • 21

2 Answers2

1

Currently you are exposing a route of POST /contactus/contactus because you are specifying a base route with the statement app.use('/contactus', contactUsRouter);, then adding/appending an additional/extra /contactus with the registration of the POST/PUT routes.

Try change the POST route to path to just '/':

var express = require('express');
var router = express.Router();
var contactUsMessage = require('../models/contactUsMessage');

  router.route('/')
    .get(function(req,res,next){
      res.send("Hello")
    })
    .post(function(req,res,next){
      res.send("Hello")
    });

module.exports = router;

Hopefully that helps!

Alexander Staroselsky
  • 37,209
  • 15
  • 79
  • 91
  • Oops, I meant to write "post" instead of "put". That seems to have resolved at least the error I inquired about. Now I get this error in the npm log: "GET /contactus 500". And the browser console gives this error: "HTTP500: SERVER ERROR - The server encountered an unexpected condition that prevented it from fulfilling the request. GET - http://localhost:4200/contactus". Also, does the router.route act upon the get request even though the angular app isn't making a get request? Node log shows: "[HPM] GET /contactus -> http://localhost:8085" when I first retrieve website.com/contactus. – coder101 May 08 '19 at 20:21
  • Navigating to website.com/contactus, even without any `HttpClient.get()`, will trigger the express `GET /contactus` to trigger. As mentioned in another answer, it may be advisable to put API routes under some kind of base/prefix such as `/api/` to avoid creating conflicts like this. – Alexander Staroselsky May 08 '19 at 20:27
  • can you please elaborate on this point? How does using /api/ avoid such a conflict? And to do so, do I then have to change all the paths in angular app to include "/api/" so that the url passed to the http request is something like "/api/contactus"? – coder101 May 08 '19 at 20:34
  • Yes, you would update all REST API routes in your express application to use the /api prefix and update all requests in Angular to use the /api prefix for any HttpClient requests as well. Usually you would add catch-all logic to express to load (`sendFile()`) your Angular's index.html anytime none of the API endpoints were not hit/matched. – Alexander Staroselsky May 08 '19 at 20:36
  • Thanks. What is it about "/api" that avoids these types of conflicts? And I'm presuming this catch-all logic with sendFile() would be in the error handler in app.js or would I have to include it in every single router code in the express app? – coder101 May 08 '19 at 20:48
  • There is nothing special about the string/characters "/api. It's just a prefix to separate the API endpoint from the front-end route. So that when someone loads website.com/contactus in their browser, it doesn't trigger the API endpoint `GET /contactus` from executing because simply navigating to website.com/contactus is a GET to /contactus. I assume you ONLY want to make an actual `GET /contactus` when you specifically execute it from your `HttpClient.get()` right? Also https://stackoverflow.com/questions/26349497/node-express-with-static-html-how-to-route-all-requests-to-index-html – Alexander Staroselsky May 08 '19 at 21:03
  • Yes, that's correct. Looks like adding the "/api" to proxy.conf.js, the http request in the angular service code and to the path in app.js resolves this issue because now the npm log shows a status 200 each time I call the http post request. But I still get an error on the angular side: ""Http failure during parsing for http://localhost:4200/api/contactus". I'm presuming that has something to do with the way my request is set up in the angular service code and the component (which subscribes to the request in the angular service code). Will create another issue for that. Thanks. – coder101 May 09 '19 at 00:03
  • @user6860460 The error regarding parsing is most likely related to the data you are returning. Angular HttpClient expects `application/json` to be returned. In your example you are effectively sending back plain text, which needs to be handled in Angular in a "special" way. It's effectively behind the scenes doing `JSON.parse()`. Try instead sending back `res.send({ foo: "bar" })`; or something JSON like. – Alexander Staroselsky May 09 '19 at 00:08
1

Your proxy conf file is not working properly.

The requests are still trying to look for a POST method route /contactus in angular application running on port 4200 ((XHR)POST - http://localhost:4200/contactus) which does not exist and hence you are getting a 404.

Make sure you have followed the steps mentioned on official angular website - Add Proxy for backend server - angular.io

It will be advisable if you use proxy path /api instead of / . Something like this

{
  "/api": {
    "target": "http://localhost:8085",
    "secure": false
  }
}

Hope it helps

  • Actually, that's one thing I've struggled to understand. Why use "/api" in the proxy.conf file when there's no "/api" in the url? – coder101 May 08 '19 at 20:22
  • The only purpose is from a perspective where you might want to send request to another backend server. If you have another backend server at port 8086 you might want to send some request to that server and some of them to port 8085. Having a /api kind of identifier might help to distinguish to handle such scenarios. – Nikhil Verma May 18 '19 at 19:16