Only render page if app.use('/api') on Express 4 - javascript

I have the following:
app.js
...
var api = require('./routes/api');
app.use('/', api);
app.use('/api', api);
./routes/api
...
var router = express.Router();
router.get('/', passport.authenticate('bearer', { session: false }), function (req, res) {
res.json({
msg: 'API is running'
});
});
How would I differentiate between the / and /api route so that I could render a page if i'm on / and return only JSON if i'm on /api?
I was thinking of passing a function to non-api routes to render a json response, and another function to api routes to display the json response?
Any other way to do this, or am I just overthinking it?
Thanks in advance.

Why are you mounting 2 paths on the same route file i.e.
var api = require('./routes/api');
app.use('/', api);
app.use('/api', api);
You can use:
var api = require('./routes/api');
var index = require('./routes/index');
app.use('/', index);
app.use('/api', api);
Now, you can write your routes in these 2 files. / in index file will be use for domain.com/ url, and / route in api file will be used for domain.com/api url. Now, you can handle both requests as differently as you want.
You could also specify both routes in index file(in this case, you do not need to create api file and mount it on a path). e.g.
router.get('/', function( req, res ){
// code for / path here
});
router.get('/api', function( req, res ){
// code for /api path here
});

It really depends what you want.
Example from one of my projects:
We had the frontend under /client/ so when we got / we used a res.redirect('/client/') where your HTML file could be.
Or maybe something like res.render(view [, locals] [, callback]) fits your needs
Take a look at Express Documentaion for res there are many possibilities.

Related

Express Router not showing endpoint requests

I'm new to express and I'm trying to use express.Router() in order to route to different end points. When I follow tutorials online, I can only get text to send from the root '/' and no other endpoint. As far as I can tell my code matches well with the tutorial, so I'm not seeing where something is missing.
express.js
"use strict";
const express = require('express');
const app = express();
const resources = require('../routes/resources');
const commonExpress = require('common/lib/express');
commonExpress.setup_pre(app);
app.use('/', (req, res) => {
res.send('<h1>Hey Socket.io</h1>');
});
app.use('/resources', resources)
//use /routes/resources to handle endpoints
//that start with '/resources'
commonExpress.setup_post(app);
module.exports = app;
resources.js
"use strict";
const express = require('express');
const router = express.Router();
router.use(function(req, res, next) {
console.log(req.url, "#", Date.now());
next();
});
router.route('/messages').get((req, res) => {
res.send("hi get /resources/messages")
}).post((req, res) => {
res.send("hi post /resources/messages");
});
router.route('/messages/:userId').get((req, res) => {
res.send("hi get /resources/messages " + req.params.userId);
}).put((req, res) => {
res.send("hi put /resources/messages " + req.params.userId);
})
module.exports = router;
commonExpress
const express = require('express');
const logger = require('morgan');
const utils = require('./utils');
const cookieParser = require('cookie-parser');
module.exports = {
setup_pre: app => {
app.use(logger('dev'));
app.use(express.json());
app.use(cookieParser());
app.use('/health', (req, res) => res.status(200).send());
},
setup_post: app => {
app.disable('x-powered-by');
app.use(utils.handleMezError);
app.use(utils.handleMongoError);
app.use(utils.handleUnknownError);
app.use(function (req, res, next) {
res.status(404).send();
})
}
};
These are the responses I get when I use the curl command:
tk#tk-desktop:~/messenger$ curl http://localhost:4000/
<h1>Hey Socket.io</h1>tk#tk-desktop:~/messenger$ curl http://localhost:4000/resources
<h1>Hey Socket.io</h1>tk#tk-desktop:~/messenger$ curl http://localhost:4000/resources/messages
<h1>Hey Socket.io</h1>tk#tk-desktop:~/messenger$ curl http://localhost:4000/resources/messages/:userId
Your code here:
app.use('/', (req, res) => {
res.send('<h1>Hey Socket.io</h1>');
});
grabs all possibly URLs and sends a response and does not allow any of the other request handlers to see inbound requests. Since request handlers in Express are processed in the order you register them, the only request handlers that can run before this one grabs the inbound request are the ones in commonExpress.setup_pre(app);.
Probably, you want to change that above route to:
app.get('/', (req, res) => {
res.send('<h1>Hey Socket.io</h1>');
});
So, it only responds to a GET request and so it only matches the exact / URL (see details below).
It may help you to understand a few things:
app.use() is greedy. It will match any inbound URL that starts with the path you use. So, if you have app.use('/', ...), that will match all possible URLs since all URLs start with /. As such, app.use() is usually used for middleware, that prepares a request/response, but doesn't actually send one and then calls next() to continue routing to subsequent handlers and doesn't call res.send() or any other method that sends a response. Once you've sent a response and not called next(), all routing is done for that request.
Inbound requests are processed by Express in the order the request handlers are registered. So, if you have overlapping handlers, you would generally put the more permissive handlers later and the more specific handlers earlier. If you do the reverse which is what you have here, the more permissive handlers will grab everything and the others will never get a shot.
It looks like you can either change your app.use('/', ...) to an app.get('/', ...) which will only match the exact / URL and nothing else or you can change it to be a middleware handler that doesn't send a response and does call next() to continue routing.
It also looks like you may be using app.use() in other places where app.get() is more appropriate. For example with app.use('/health', ...), app.use() will match all HTTP verbs (GET, POST, PUT, DELETE, PATCH, etc...), but if you are sending a response in your route handler, you typically want to be matching only one of those verbs. If it's a request/response type of route where the browser requests content, that would be a GET and should use app.get(), not app.use().

How to manage request data on entry file in node js

In Node Js, on the entry file e.g. index.js, How can I get requested data either as Form-data or Form-URL-encoded or Raw JSON data in middleware?
In my project, I am handling various API request,
Few requests contain file type so requesting as form-data.
Few requests do not contain file type so requests are coming as Form-URL-encoded.
Now the main problem is before routing, I need a specific field from req.body in the middleware.
But I am getting req.body as undefined.
For reference here is my index.js file:
const express = require('express');
const app = express();
app.use(express.raw());
app.use(express.urlencoded({ extended: false }));
app.use((req, res, next) => {
const routes_handler = require('./routes/index.js')(app, express, req);
next();
})
app.listen(3000, () => {
console.log("Server running at Port " + 3000);
});
and the routes/index.js file as follows:
module.exports = function (app, express, req) {
console.log(req.body);
//I need here data of req.body for all requests type (form data, URL-encoded, raw JSON)
app.post('/', function (req, res) {
console.log("Here I can get the requested body easily", req.body)
res.send('Hello World');
});
app.post('*', function (req, res) {
res.send({
code: 0,
message: 'No Content',
status_code: 204,
data: {
error: 'API not found!'
}
});
});
}
Also, I know for file type data, POSTMAN will send the request as Form-data, not as Form-url-encoded. So which I should choose Formidable or Multer?
The way you get all the data in index.js is by creating middlewares for your application, every time any routes that go into your application will be passed through this middleware.
Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle. The next function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware.
The below middleware will simply listen to all routes & adds up request time to request time, here goes the code
let express = require('express')
let app = express()
let bodyParser = require("body-parser")
app.use(bodyParser.json())
let requestTime = function (req, res, next) { // simply updating add the requestBody using the middleware
req.requestTime = Date.now();
req.json_body = req.body;
next()
}
app.use(requestTime) // requestTime is the middleware here
app.get('/', function (req, res) {
var responseText = 'Hello World!<br>'
responseText += '<small>Requested at: ' + req.requestTime + '</small>'
res.send(responseText)
})
app.listen(3000)
Few things to note here
Always add interceptor above all routes
Don't forget to add next() inside the middleware, else it will not go to the next route.
Now, coming to the second part of your question which is accessing body,formdata, etc
You can use body-parser npm module to achieve that, something like this
Starting from Express 4, body-parser comes inbuilt with it, so you can try out something
app.use(express.json());
app.use(
bodyParser.urlencoded({
extended: false
})
);
Now, the last bit, you don't need Multer for formdata, but for file upload like multipart/form-data you will need this. Both are good in their own ways, I would go for Multer.
Hope this will help you :)
I believe the body-parser module is your answer.
https://www.npmjs.com/package/body-parser
Add the following line before the routes in your index.js after installing the body-parser package.
app.use(bodyParser.json())

Angular JS and Node routing/wiring - data only showing after a page refresh

I'm using Node and Anugular, and I have created a RESTful api from my application, and created an angular resource to use this. I'm confused as to how the Angular ui-router directive reconciles with the Node Routing system on the server.
At the moment I have set up my routes/states in ui-router like this:
$stateProvier
.state('admin', {
url:'/admin',
templateUrl:'views/admin.html',
controller: 'adminController'
});
And this loads into the ui-view on my homepage, when I navigate to this url from a link on the loaded page.
HOWEVER, when I manually type in localhost/admin I get the route from Node, rather than the state render through angular.
Now I'd like to Angular to handle all the navigation on my app, and my resource to get the information, even if the address is typed manually into the navigation bar.
I've created a route in Node is for index, which contains my index.html page from angular, which effectively contains the whole app angular code, including all the routing.
My question is, how can I get angular redirect if I manually type the url into the address bar, and still have the data from the $resource.
I'm directing my resource to '/admin' - could this be the problem?
Does this mean that I need to add the contents of /routes/appointments' into the base node file (server.js), and then remove the route? If so then how do i direct my resource to the correct REST api?
app structure
public
-angular app
-app.js //for angular
routes
index.js
appointments.js
models
views
- index.ejs
server.js //node server file
here is my code exerpts
server.js
//standard routing code
var routes = require('./routes/index');
var appointments = require('./routes/appointments');
var app = express();
//configuring Express
app.set('port', process.env.PORT || 3000);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.set('views', __dirname + '/views');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use('/', routes);
app.use('/', appointments);
routes/index.js
var express = require('express');
var router = express.Router();
// ./routes/index.js
router.get('/', function(req, res) {
res.render('index', { title: 'Homepage' });
});
module.exports = router;
routes/appointments.js - this is the basis of my RESTFUL api
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Todo = require('../models/Appointments.js');
/* GET /todos listing. */
router.get('/admin', function(req, res, next) {
Todo.find(function (err, todos) {
if (err) return next(err);
res.json(todos);
});
});
module.exports = router;
One way to do this is via the Accept header. If the request only accepts JSON then let the request go through to your API. If the request accepts HTML then always serve up your index page. Then once the index page loads angular's router will take care of the rest.
// Angular config - default Accept header for all ajax requests
$httpProvider.defaults.headers.common = {
'Accept': 'application/json'
};
// Middleware in Node to "intercept" non JSON requests
// Place this after express.static middleware but before your route definitions.
app.use(function(req, res, next) {
// keep in mind this applies to all requests, so 404s will need to be handled by your angular app since any url will serve up the index page
if(req.header('Accept') !== 'application/json') {
console.log('serving the index page');
req.url = '/'; // force the url to serve the index route.
}
next();
});
One more thing to note about this approach is that obviously you won't be able to see/debug your JSON data by hitting the URL directly anymore. There are several useful tools like Advanced REST Client or POSTman which actually give you better control and more options for things like that. Just make sure you set the Accept header in one of those tools and you'll be able to see the JSON response.
The actual URL is localhost#!/admin, try that. Angular hides the hashbang #!
Angular's URL routing is an "illusion" in that way. It only works on the client-side and only when your Angular app is loaded, which is on the main / route.
A solution could be to conditionally redirect from localhost/admin to localhost#!/admin, i.e. redirecting to your Angular app and passing it the #!/admin path. The condition could be a check for whether or not JSON was requested.
router.get('/admin', function(req, res, next) {
if(req.header('Accept') !== 'application/json')
return res.redirect('/#!/admin');
Todo.find(function (err, todos) {
if (err) return next(err);
res.json(todos);
});
});
You'll also need to configure Angular such that when it requests '/admin' json data from the server, it should only accept json (by setting the request header), which is how the the server will distinguish it from the regular '/admin' request. For that, if you're using $http.get you would do $http.get('/admin', {'Accept':'application/json'})

Handling routes with Angular and Express

I have an app where I am trying to remove the hashbang ( ! and #) prefixes for my routes, but still have people be able to use bookmarked routes. For the most part I have been able to get it work with html5Mode set to true, but there are a few cases where it is not working. Here is how I have my server configured:
var router = require('./router')(app);
app.use(express.static(path.join(__dirname, '../client')));
app.get('*', function (req, res, next) {
res.sendFile('index.html', {root:'../client/app/'});
});
router in this case looks like this:
var express = require('express');
var router = express.Router();
var Products = require('../../database').Products;
router.get('/:flavor', function (req, res) {
var flavor = req.params.flavor;
Products.findOne({flavor:flavor}, function (err, product) {
if (err) {
throw err
}
res.json(product);
});
Getting the flavor routes, is one case where this setup does not work. If someone directly types into the browse, mysite.com/lemon they receive the JSON data back, only (no template or anything). Normally this is used by the angular app, (which would typically make the request to /lemon and implement it into the template). However, if I move the router below the app.get('*'), then any request made by Angular for the data is returned with the entire index.html page. How can I make it so that a direct request by the browser for a route that normally returns JSON sends the index file?

express-subdomain not redirecting subdomain.localhost get request

I am using an express.js package called express-subdomain to facilitate requests to defined subdomains I set up.
As far as I understand, the subdomain constructor function expects an express router object which I pass to it from an exported router module.
What I have tried is as follows:
MAIN APP.JS SERVER FILE
var common = {
express: require('express'),
subdomain: require('express-subdomain')
};
common.app = common.express();
module.exports = common;
common.app.listen(3000, function () {
console.log(('app listening on http://localhost:3000'));
});
var router = require('./router/index');
// Error Handling
common.app.use(function(err, req, res, next) {
res.status(err.status || 500);
});
router/index
module.exports = function (){
var common = require('../app');
var router = common.express.Router();
common.app.get('/', function(req, res) {
res.send('Homepage');
});
common.app.use('/signup', require('./routes/signup'));
common.app.use(common.subdomain('login', require('./routes/login')));
}();
routes/login
var express = require('express');
var router = express.Router();
router.get('/', function (req, res) {
res.send('login working');
});
router.get('/info', function (req, res) {
});
module.exports = router;
I have tried to access the login subdomain at the following urls:
http://login.localhost
http://login.localhost:3000
http://login.localhost.com
http://login.localhost.com:3000
Any clarification or assistance appreciated.
author of express-subdomain here đź‘‹
A couple of things:
Hosts must be setup correctly - I'd recommend something like so in your /etc/hosts file.
127.0.0.1 myapp.local
127.0.0.1 login.myapp.local
For more information on this see https://github.com/bmullan91/express-subdomain#developing-locally
Register the subdomain routes before any others, including the homepage route. The order is very important
The pattern you're using in /routes/index.js is not advised (requiring a self invoking function). Exporting the Router like you done in /routes/login.js is cleaner.
Finally, If you're still stuck take a look at the source for express subdomain and in particular its tests.
Happy coding.

Categories