Node/Express send static file on request - node.js

I'm really new to node/express and I'm trying to understand how sending static files work. I managed to serve my index file, but I cannot serve other files as response of a GET request.
app.use(express.static(path.join(__dirname, '/client/build')))
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, '.', 'client/build/', 'index.html'))
res.end()
})
app.get('/portfolio', (req, res) => {
const person = req.query.name
var filePath = __dirname + '/client/build/' + person + '/' + 'index.html'
res.sendFile(filePath)
res.end()
})
I found similar questions but nothing seems to work.
The request I send is:
fetch(`portfolio?name=${who}`)

There's a few problems with your code. One of them is that you end the request before the file is even done sending and another problem is that you're not using the res.sendFile method properly.
try something like this:
app.get('/', (req, res) => {
const fileDirectory = path.resolve(__dirname, '.', 'client/build/');
res.sendFile('index.html', {root: fileDirectory}, (err) => {
res.end();
if (err) throw(err);
});
})
app.get('/portfolio', (req, res) => {
const person = req.query.name
const fileDirectory = __dirname + '/client/build/' + person + '/';
res.sendFile('index.html', {root: fileDirectory}, (err) => {
res.end();
if (err) throw(err);
});
})
I don't recommend throwing an error whenever you get one but it should at least give you an idea how you can get this to work.

I finally solved it by using this on the server:
app.use( express.static(path.join(__dirname, 'client/build/person1')))
app.use( express.static(path.join(__dirname, 'client/build/person2')))
and calling it in the view like this:
<a href='/person1'></a>
<a href='/person2'></a>
As it seems, express.static resolves the paths on its own, so you don't need to handle the request yourself to serve the static files.
If this is not a good solution please coment

Related

Fetching from the root '/' route seems to not work?

I have a very simple server in expressjs from which I'll be getting some data, so far I just created basic routes and checked if they work, but I have an issue with the main '/' one. I added some extra routes for comparison
app.get('/', (req, res) => {
console.log('request at /');
})
app.get('/ree1', (req, res) => {
console.log('ree1');
})
app.get('/ree2', (req, res) => {
console.log('ree2')
})
app.get('/ree3', (req, res) => {
console.log('ree3')
})
Every time I fetch (from the browser console even) all the "/ree" routes log in the console, but the '/' one doesn't react at all. What could be the cause of that? Perhaps I missed something when I was learning basics.

How to add routes for a static directory and an Angular client on the same server?

I have an API written in Node.JS/Express and an Angular client that talks to it, both in the same server. All HTTP calls that are not recognized by the API are automatically redirected to the client.
My current code looks like this and it is working as expected so far:
app.get('/api/something', (req, res) => {
//...
});
app.get('/api/foo/bar', (req, res) => {
//...
});
// (...)
app.use(express.static('client'));
app.get('/*', (req, res) => {
res.sendFile('client/index.html', { root: path.join(__dirname, '..') });
});
Now I need to add to this a route to a directory with static files (for example, stuff, so that URLs like /stuff/xxx/yyy.png would return the corresponding file), but still falling back to the Angular client for everything else.
How can I achieve this? So far, everything I tried would either not work at all or break the client routes.
Edit: Based on the answer by joyBlanks, this is what ended up working for me:
(The redirect is necessary to correctly handle URLs that point to a directory name without the trailing slash)
app.get('/*', (req, res, next) => {
if (req.url.startsWith('/stuff')) {
res.sendFile(
req.url.substr(1), { root: path.join(__dirname, '..') },
err => {
if (err) {
if (err.code == 'EISDIR')
res.redirect(req.url + '/');
else
next(err);
}
}
);
}
else {
res.sendFile('client/index.html', { root: path.join(__dirname, '..') });
}
});
app.get('/*', (req, res, next) => {
if(req.url.contains('/stuff/xxx')){ // or regex match maybe
next();
} else {
res.sendFile('client/index.html', { root: path.join(__dirname, '..') });
}
});
You can add a condition matching the URL. inside this all block

Express.js serve static folder at specific url only

I have an existing express app I'm using for an API but I also want to deliver a folder with an HTML file and some images/css/js. I can get it to serve the HTML page but it does so at / while I only want it served at /manual. Here is my stripped down code with just express.static, my / route and the catch all redirect to /
const app = express();
app.use(express.static('manual'));
app.get('/', (req, res) => {
res.status(403);
res.send('Access denied.');
return;
});
app.get('*', (req, res) => {
res.redirect(301, '/');
return;
});
What currently happens: Going to mysite.com/manual redirects to mysite.com/ which serves the index.html in the manual folder.
What I want to happen: Going to mysite.com/manual serves the index.html in the manual folder. And going to mysite.com/ throws a 403 status code.
Middleware/handlers are applied in the order you declare them, so you just need to change the order
const app = express();
app.get('/', (req, res) => {
res.status(403);
res.send('Access denied.');
return;
});
// adding a first parameter lets you apply the middleware to a route
app.use('/manual', express.static('manual'));
app.get('*', (req, res) => {
res.redirect(301, '/');
return;
});

How do I automatically return a 404 when a GET path doesn't exist?

I am using NodeJS, Express and Handlebars (template engine) to build a web application. Currently I'm trying to automatically redirect users whenever they enter an URL that does not exist (or whenever they might not have access to it).
The following returns the index page:
router.get('/', (req, res) => {
res.render('index/index');
});
But how do I make something like this:
router.get('/:ThisCouldBeAnything', (req, res) => {
res.render('errors/404');
});
The following example is from Github:
Say that I enter this URL:
https://github.com/thispagedoesnotexist
It automatically returns a 404. How do I implement this in my application?
Thanks in advance.
Use a middleware just after all route handlers to catch non existing routes:
app.get('/some/route', function (req, res) {
...
});
app.post('/some/other/route', function (req, res) {
...
});
...
// middleware to catch non-existing routes
app.use( function(req, res, next) {
// you can do what ever you want here
// for example rendering a page with '404 Not Found'
res.status(404)
res.render('error', { error: 'Not Found'});
});
After all your other routes you can add:
app.get('*', (req, res) => {
res.render('errors/404');
});
Alternately, you can use a middleware function after all your other middleware and routes.
app.use((req, res) => {
res.render('errors/404');
});
So you might end up with something that looks like:
//body-parser, cookie-parser, and other middleware etc up here
//routes
app.get('/route1', (req, res) => {
res.render('route1');
});
app.get('/route2', (req, res) => {
res.render('route2');
});
//404 handling as absolute last thing
//You can use middleware
app.use((req, res) => {
res.render('errors/404');
});
//Or a catch-all route
app.get('*', (req, res) => {
res.render('errors/404');
});
I see that you have express tagged. All you have to do is include a default handler that includes
res.status(404).render('404template')
For example
app.get('*', (req, res,next) => {
res.status(404).render('error.ejs')
});

What is the smartest way to handle robots.txt in Express?

I'm currently working on an application built with Express (Node.js) and I want to know what is the smartest way to handle different robots.txt for different environments (development, production).
This is what I have right now but I'm not convinced by the solution, I think it is dirty:
app.get '/robots.txt', (req, res) ->
res.set 'Content-Type', 'text/plain'
if app.settings.env == 'production'
res.send 'User-agent: *\nDisallow: /signin\nDisallow: /signup\nDisallow: /signout\nSitemap: /sitemap.xml'
else
res.send 'User-agent: *\nDisallow: /'
(NB: it is CoffeeScript)
There should be a better way. How would you do it?
Thank you.
Use a middleware function. This way the robots.txt will be handled before any session, cookieParser, etc:
app.use('/robots.txt', function (req, res, next) {
res.type('text/plain')
res.send("User-agent: *\nDisallow: /");
});
With express 4 app.get now gets handled in the order it appears so you can just use that:
app.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nDisallow: /");
});
1. Create robots.txt with following content :
User-agent: *
Disallow: # your rules here
2. Add it to public/ directory.
3. If not already present in your code, add:
app.use(express.static('public'))
Your robots.txt will be available to any crawler at http://yoursite.com/robots.txt
Looks like an ok way.
An alternative, if you'd like to be able to edit robots.txt as regular file, and possibly have other files you only want in production or development mode would be to use 2 separate directories, and activate one or the other at startup.
if (app.settings.env === 'production') {
app.use(express['static'](__dirname + '/production'));
} else {
app.use(express['static'](__dirname + '/development'));
}
then you add 2 directories with each version of robots.txt.
PROJECT DIR
development
robots.txt <-- dev version
production
robots.txt <-- more permissive prod version
And you can keep adding more files in either directory and keep your code simpler.
(sorry, this is javascript, not coffeescript)
Here is what I use
router.use('/robots.txt', function (req, res, next) {
res.type('text/plain')
res.send(
`User-agent: *
Disallow: /admin`);
});
For choosing the robots.txt depending the environment with a middleware way:
var env = process.env.NODE_ENV || 'development';
if (env === 'development' || env === 'qa') {
app.use(function (req, res, next) {
if ('/robots.txt' === req.url) {
res.type('text/plain');
res.send('User-agent: *\nDisallow: /');
} else {
next();
}
});
}
This is what I did on my index routes. You can just simply write down in your codes what I does given down below.
router.get('/', (req, res) =>
res.sendFile(__dirname + '/public/sitemap.xml')
)
router.get('/', (req, res) => {
res.sendFile(__dirname + '/public/robots.txt')
})
I use robots.txt as a normal file for Prod, and a middleware for other envs.
if(isDev || isStaging){
app.use('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nDisallow: /");
});
}
app.use(express.static(path.join(__dirname, 'public')));
Focusing more on the most convenient and simple solution instead of the "best" or "smartest". I simply added the following to the server.ts file.
server.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nAllow: /");
})
What this does is create a robots.txt file on the fly and sends it whenever the /robots.txt file is called for.
Now to get this to work, the code fragment must be placed before the other server.get function calls (so it takes priority). I'm implementing Express with Angular, for which the full code fragment for me ended up being:
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/sophisticatedPrimate/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found # https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
server.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nAllow: /");
})
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
app.use(express.static('public'))
app.use('/images', express.static('public/images'))
app.use('/videos', express.static('public/videos'))

Resources