I'm using Node/Express as an API proxy server and want to know if it's possible to render HTML (index.html) page for all GET routes as well as expose API endpoints on the same port. I'm using client side routing (react-router).
For example:
// Always return the main index.html, so react-router render the route in the client
app.get('*', (req, res) => {
const html = renderHTML(req, res);
res.send(html)
});
// Expose API
app.all('/api/*', (req, res, next) => {
proxyRequest(...)
});
The problem is that doing when doing a GET request, the first app.get() catches it (for example: fetch('/api/accounts')).
I need to be able to make any request to any API endpoint route (besides /) and have the client send any method (GET, PUT, POST...).
Can I serve index.html to all client routes and have GET endpoints on the same port? Can I serve html to all routes except ones prefixed with /api/?
If you expose your endpoints first and then serve your html, it should do the job.
// Expose API
api.all('/api/*', cb);
// Serve HTML
api.get('*', cb);
Make a directory called public.
Put your HTML and supporting files in it.
app.use(express.static(path.resolve('./public')));
//Your API routes here
The express.static will serve the static HTML files and then you can add your route statements. This will work on the same port.
Related
Background
I have a NUXT application that renders vue templates as you would expect. I have a need to hit some Express routes in the application from external applications client side.
I can hit GET routes from external applications but POST requests fail with a error 404.
Example
Express
This works
router.get('/test/get', (req, res, next) => {
res.json({ message: "Global PDF Generator is configured correctly", status: "operational" })
});
This fails with 404
router.post('/test/post', (req, res, next) => {
res.json({ message: "Global PDF Generator is configured correctly", status: "operational" })
});
Inside of the Nuxt application and within any of the vue components I can hit the POST routes like this,
fetch('api/v1/pdf', { method: 'POST' }
But if we try to do something like this it fails,
fetch('localhost:3000/api/v1/pdf', { method: 'POST' }
The second example is important because obvioulsy that is how I would have to hit an end point in this application from an outside application.
What I can not figure out is why the GET requests work and do not get a 404 while the POST requests continue to get a 404 from external applications.
Question
How can I create an externally accessible Express POST end point in my NUXT application so that it will be directly accessible from an external source?
Not as an answer but just so I can format things and demonstrate that something else must be going on. This minimal example works fine for me.
vue init nuxt-community/express-template sample_post
cd sample_post
npm install
Modify api/routes/users.js adding a post route:
router.post('/test', function(req, res, next) {
res.json(message: 'hello');
});
Start up the service:
npm run dev
Verify it returns a post from an external request successfully:
curl -X POST http://localhost:3000/api/test
{"message":"hello"}
So something else must be going on somewhere.
This is because when the NuxtJs app on production it sees only page routes not server route. You should use NuxtJs serverMiddleware. You can find it here from NuxtJs API
I am writing a web application using react for client side and express for server side.
I am using react-router (link) routing the client pages.
Working with the hashHistory it was simple and worked fine, but now I want to use browserHistory.
As mentioned in the react-router tutorial, I need to tell my server to expect the client request, so when we render a client page it will server the index.html.
I can't find a way to handle both requests from the client for pages and requests for server processing.
For example, I have a page in the path /product, but I also have an endpoint for the server processing /authenticate.
In the react-router tutorial it says to use the following:
// server.js
// ...
// add path.join here
app.use(express.static(path.join(__dirname, 'public')))
// ...
app.get('*', function (req, res) {
// and drop 'public' in the middle of here
res.sendFile(path.join(__dirname, 'public', 'index.html'))
})
But this way will result every request (including /authenticate) to send it back to the index.html. How can I merge those two?
You have to define the /authenticate before the * (which is basically a Catch-All and has to come last)
app.use(express.static(path.join(__dirname, 'public')))
ap.get('/authenticate', function (req, res) {
res.sendStatus(200)
});
// ...
app.get('*', function (req, res) {
// and drop 'public' in the middle of here
res.sendFile(path.join(__dirname, 'public', 'index.html'))
})
So I've read almost every SO answer/question to this topic, but still I have many questions in my head.
First, the problem:
I have an AngularJS app with html5 enabled, so I can get rid of the '#' sign.
$locationProvider.html5Mode({ enabled: true, requireBase: true });
$locationProvider.hashPrefix('!');
This is the important part in my index.html:
<!DOCTYPE html>
<html ng-app="application" ng-controller="ApplicationController as app">
<head>
<meta name="fragment" content="!">
<title>LivingRoomArt</title>
<meta charset="UTF-8">
<base href="/index.html" />
I am communicating with a NodeJS server which is using express:
router.route('/events')
.post(authController.isAuthenticated, eventController.postEvent)
.get(eventController.getEvents);
// Register all our routes with /api
app.use('/api', router);
// Start the server
app.listen(process.env.PORT || 5000);
So, the usual problem:
After reloading, I am getting an 404 from the server. I get the concept of this here, the suggested solution everywhere:
// This route deals enables HTML5Mode by forwarding missing files to the index.html
app.all('/*', function(req, res) {
res.sendfile('index.html');
});
});
The thing is, I don't have an index.html file on my server, neither do
I want to duplicate it on my server.
So how do I tell Node to handle requests properly without storing html-files on my server?
I am hosting the Node app on Heroku, if this helps.
When you say you don't serve static files, you're saying that the node.js API isn't right?
I guess you end up with two distinct urls, let's call them http://api.com and http://client.com.
I don't understand why your API should handle the 404. Do you load http://api.com in your browser and expecting your index.html? If it's really your use-case, I would advice a simple routing to declare in your API like:
app.all('/*', function (req, res) {
res.redirect('http://client.com');
});
Which will redirect all requests not catched by your previous routes declaration to your client website.
Then, there is two options:
If the server that serves your static files is another Node.Js server using express, you could perfectly do the sendfile, since you now have access to the index.html
If you're using Nginx, (which I strongly recommend if you don't) for the statics, you could do a configuration like this to redirect all failed requests (missing files / routes) to the index.html
server {
listen 80;
root /app/www;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
I'm using passport and some static .html files. I want to include them as express views or static files. Now I want SOME .html files like login.html to be available for everyone, but the other view files like secret.html should need passport.
I know, that you can use
app.get('somePath', ensureAuthenticated, function (req, res, next) {} for routes, but I want it for static files.
How to do it on a non-hacky way?
If all "secure" files share a distinct URL prefix, such as /public/secure, you can use() ensureAuthenticated with this path:
app.use('/public/secure', ensureAuthenticated);
app.use('/public', express.static(__dirname + '/public'));
Otherwise, the express.static() middleware doesn't offer many options for controlling access. For the most part, it assumes all available files from the path given to it are meant to be "public."
Though, it does normally refuse to send hidden files, following the *nix standard of a . prefix:
~/path/to/.secret.html
Then, to actual serve it, you can send the file yourself in the route you suggested with the middlware attached:
app.get('/path/to/secret.html', ensureAuthenticated, function (req, res) {
res.sendfile(__dirname + '/path/to/.secret.html');
});
And, you may want to consider moving it out of your static() directory,
This is the root of the app (http://site.com), that runs when requesting the domain. If I wanted to add only one file say robots.txt (http://site.com/robots.txt) to be requested using this http server, how would I do that? It would pull from the contents of robots.txt and echo it out.
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('homepage');
res.end('');
}).listen(process.env.VMC_APP_PORT || 1337, null);
Thanks
Instead of implementing such a functionality manually, you can build your application with http://expressjs.com/ framework and use static middleware documented at http://expressjs.com/guide.html to server static files. But I would personally prefer to put Nginx in front of Node to serve static file, because it has proven to be efficient for this job and it is all about configuration rather than programming then. Configuration can be different depending on the specific purpose and environment, but mine is documented at http://skovalyov.blogspot.com/2012/07/deploy-multiple-node-applications-on.html