henriquecustodia henriquecustodia - 1 month ago 16
Node.js Question

Heroku always send a response as html instead a json with the data expected

I did deploy my api application to Heroku and it is running normally on dyno.

My problem is when I try to do a request using POSTMAN or my FrontEnd Application, Heroku server always returns that HTML content:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<meta http-equiv='content-type' content='text/html; charset=UTF-8'>
<meta name="VERSION" content="">
<meta name="DATA" content="fwdnode3.registrar-servers.com (208.64.122.246)">
<link href=" " rel="shortcut icon" type="image/x-icon">
<title></title>
</head>
<frameset rows='100%, *' frameborder=no framespacing=0 border=0>
<frame src="https://epiphany-api.herokuapp.com/api/v1/products" name=mainwindow frameborder=no framespacing=0 marginheight=0 marginwidth=0></frame>
</frameset>
<noframes><h2>Your browser does not support frames. We recommend upgrading your browser.</h2><br><br>
<center>Click <a href="https://epiphany-api.herokuapp.com/api/v1/products" >here</a> to enter the site.</center>
</noframes>
</html>
</pre>


The http code that response above is 200(OK) and the response header is:

Server: nginx
Date: Wed, 12 Oct 2016 12:56:23 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive


But when I do a request using a browser, Heroku server returns the data correctly.

Well, sometimes I receive a 400 (Bad Resquest) code as well, from that Nginx server.

My api is a node application, so I do not understand why that Nginx server is intercepting and sending responses instead my Node App.

Edit - added description

This is NodeJS code in my api:

'use strict';

const express = require('express'),
app = express(),
consign = require('consign'),
bodyParser = require('body-parser'),
cors = require('cors'),
compression = require('compression'),
port = process.env.PORT || 9000;

app.use(cors());
app.use(compression());
app.use(bodyParser.json({ limit: '50mb' }));

consign({ cwd: 'project' })
.include('common/config')
.then('common/helpers')
// middlewares should be loaded individually
.then('common/middleware/authenticationV1.js')
.then('modules/products/validationsV1.js')
.then('modules/products/repositoryV1.js')
.then('modules/products/modelV1.js')
.then('modules/products/controllerV1.js')
.then('modules/products/routesV1.js')
.then('modules/categories/repositoryV1.js')
.then('modules/categories/modelV1.js')
.then('modules/categories/controllerV1.js')
.then('modules/categories/routesV1.js')
.then('modules/checkout')
.then('modules/colors/repositoryV1.js')
.then('modules/colors/modelV1.js')
.then('modules/colors/controllerV1.js')
.then('modules/colors/routesV1.js')
.then('modules/sizes/repositoryV1.js')
.then('modules/sizes/modelV1.js')
.then('modules/sizes/controllerV1.js')
.then('modules/sizes/routesV1.js')
.then('modules/users/modelV1.js')
.then('modules/users/controllerV1.js')
.then('modules/users/routesV1.js')
.then('modules/adminUsers/repositoryV1.js')
.then('modules/adminUsers/modelV1.js')
.then('modules/adminUsers/controllerV1.js')
.then('modules/adminUsers/routesV1.js')
//the error handler has to be last middleware loaded, pay attention :)
.then('common/middleware/error-handlerV1.js')
.into(app);

app.listen(port, () => {
console.log('running server port %s', port);
});


Just to test, send a GET request to that endpoint:
http://api.epiphany.store/api/v1/products


Edit - added description

modules/colors/routesV1.js


'use strict';

module.exports = app => {
const colors = app.modules.colors.controllerV1;
const auth = app.common.middleware.authenticationV1;

app.get('/api/v1/colors', colors.getAll);
app.get('/api/v1/colors/:id', colors.get);
app.post('/api/v1/colors', auth.admin, colors.post);
app.put('/api/v1/colors', auth.admin, colors.put);
app.delete('/api/v1/colors/:id', auth.admin, colors.delete);
}


modules/colors/controllerV1.js


'use strict';

module.exports = app => {
const Colors = app.modules.colors.modelV1;

this.getAll = (req, res, next) => {
Colors.find()
.then(result => {
res.json(result);
})
.catch(err => next(err));
};

this.get = (req, res, next) => {
Colors.findById(req.params.id)
.then(result => {
if (!result) {
res.status(404).end();
}

res.json(result);
})
.catch(err => next(err));
};

this.post = (req, res, next) => {
Colors.create(req.body)
.then(result => {
res.status(204).json(result);
})
.catch(err => next(err));
};

this.put = (req, res, next) => {
Colors.update(req.body)
.then(result => {
res.status(200).end();
})
.catch(err => next(err));
};

this.delete = (req, res, next) => {
Colors.findById(req.params.id)
.then(result => {
if (!result) {
res.status(404).end();
}

result.remove(err => {
if (err) {
next(err);
}

res.status(200).end();
});
})
.catch(error => next(error));
};

return this;
}


modules/colors/modelV1.js


'use strict';

module.exports = app => {
const mongoose = app.common.config.mongooseV1;
const repository = app.modules.colors.repositoryV1;
const Product = app.modules.products.modelV1;
const validationError = app.common.helpers.errors.validationV1;

repository.pre('remove', function (next) {
Product.count({ 'stock.color': this._id })
.then(quantity => {
if (!quantity) {
next();
}

let error = validationError(this, {
action: 'remove',
errors: {
impossibleToRemove: {
message: 'This registry is being used by others registries'
}
}
});

next(error);
})
.catch(error => next(error));
});

return mongoose.model('colors', repository);
}


modules/colors/repositoryV1.js


'use strict';

module.exports = app => {
const mongoose = app.common.config.mongooseV1;

const schema = mongoose.Schema({
description: {
type: String,
required: [true, 'Required field'],
unique: [true, 'Unique field']
},
color: {
type: String,
required: [true, 'Required field']
}
});

return schema;
}


The workflow is
route -> controller -> model -> repository
.

Improvements that workflow structure is very welcome :)

Thank you for all help!

Answer

I've fixed my problem, by following way:

httpProxy.createProxyServer({
    target: 'http://my.app.com',
    toProxy: true,
    changeOrigin: true,
    xfwd: true
});

Heroku has a proxy to redirect for my app, I have fixed my problem adding the toProxy and changeOrigin properties.

Ah, yeah I am using the that Node module https://github.com/nodejitsu/node-http-proxy

I hope that answer help someone!

Comments