Setting up static assets paths, routing endpoints with koa and various middleares - javascript

Question:
How can I set up my static files so that both directories are visible to my index.html.
How can I send my index.html when you hit the default route using koa-router vs. just a .json file when I make an AJAX Get request?
Requirements:
I need static directories to be visible in my apps src/index.html
node_modules needs to be open for js libs.
src/assets needs to be open for images.
I need a router for 2 purposes :
1) serving up the initial index.html
2) CRUD endpoints to my DB.
Notes: I'm totally willing to add/subtract any middleware. But I would rather not change how I organize my directories.
Directory Structure:
Middleware:
koa-static // cant serve node_modules + src directory.
koa-send // can send static files but then breaks koa-static
koa-router // cannot
app.js
var serve = require('koa-static');
var send = require('koa-send');
var router = require('koa-router')();
var koa = require('koa');
var app = koa();
// need this for client side packages.
app.use(serve('./node_modules/'));
// need this for client side images, video, audio etc.
app.use(serve('./src/assets/'));
// Will serve up the inital html until html5 routing takes over.
router.get('/', function *(next) {
// send up src/index.html
});
// will serve json open a socket
router.get('/people', function *(next) {
// send the people.json file
});
app.use(router.routes()).use(router.allowedMethods());
// errors
app.on('error', function(err, ctx){
log.error('server error', err, ctx);
});
app.listen(3000);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Morningharwood</title>
</head>
<body>
<h1>suchwow</h1>
<img src="./assets/image1.png" alt="butts"> <!-- will 404 with routing -->
<script src="./node_modules/gun/gun.js"></script> <!-- will always 404 -->
<script>
var gun = Gun(options);
console.log(gun);
</script>
</body>
</html>

Well. it happens that I'm developing a similar kind of app
There's no problem on using koa-static to serve you static content and koa-router for you api endpoint. I never used koa-send directly. but I think you doesn't need too, given your set up
The only thing that matters is the order when attaching middleware to koa app. Try to attach koa-static first for your assets ( and maybe even index.html) and later use the koa-router for your api. Requests trying to get some static file never reach the router. and this way the router only responsibility will be serving your api
If that's not posible ( for example, because you have a bunch of non-static html files to server, consider taht you can have more than one router per app, even nesting one inside the other
( If the answer is not enough, give some time to cook a simple example. I'll post it as soon as possible)
EDIT: added a quick and dirty example here. Probably it doesn't work out of the box, but it's enough to get the idea

Related

NodeJs: How do I access the functions of my JavaScript backend from my HTML frontend?

Here is my HTML code in index.html.
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="stuff()">Click</button>
<script>
async function stuff() {
await connectToServer();
}
async function connectToServer() {
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
alert(this.responseText);
};
xhttp.open('GET', 'C:/Users/myName/myFolder/index.js', true);
xhttp.send();
return;
}
</script>
</body>
</html>
Then, here is my backend code in index.js.
const express = require('express');
const axios = require('axios');
const port = process.env.PORT || 8080;
const app = express();
app.get('/', (req, res) => {
res.sendFile('C:/Users/myName/myFolder/views/index.html');
});
app.listen(port, () => console.log(`Listening on port ${port}`));
I can type node index.js on the command line and run this program and go to http://localhost:8080/ . When I do this, the html page shows up as intended. However, when I click the button in order to make a GET request to the server side, I get a console error saying Not allowed to load local resource: file:///C:/Users/myName/myFolder/index.js . I'm using Google Chrome by the way.
I know that it is a security thing, and that you are supposed to make requests to files that are on a web server (they begin with http or https). I suppose then, my question is:
How do I make it so that my server file index.js can be viewed as being on a server so that I can call functions on the backend from my frontend?
You have to make an HTTP request to a URL provided by the server.
The only URL your server provides is http://localhost:8080/ (because you are running an HTTP server on localhost, have configured it to run on port 8080, and have app.get('/', ...) providing the only path.
If you want to support other URLs, then register them in a similar way and write a route to handle them.
The express documentation will probably be useful.
You should not need to load your server-side code into the browser. It's server-side code. It runs on the server. It isn't client-side code. It doesn't run in the browser. The browser does not need access to it.
If you want to load some actual client-side JS from the server, then use <script src="url/to/js"></script> (and not Ajax) and configure express' static middleware.
Let's improve your current flow by separating your backend API process from frontend hosting process. While backend can, it's not good in serving static html files (especially for local development purposes).
Run your backend as usual, node index.js. But as soon as this command will become more complicated, you will probably want to use npm scripts and do just npm start)
Run separate server process for frontend. Check out parcel, snowpack, DevServer. It can be as easy as npx parcel index.html, but this command is likely to change frequently with your understanding of your tool features.
To call backend, just add an API endpoint to an express app (just like you already did for serving static content), and call it, using backend process URL.
Usually, you will see your app on http://localhost/ and it should do requests to http://localhost:8080/.
If for some strange reason you will want to dynamically download js file from your server to execute it, you just need to serve this file from your frontend hosting process. In order to do so, different development servers have different techniques, but usually you just specify file extensions and paths you want to be available.
After editing frontend files, you will see hot-reload in browser. You can achieve the same for node process with various tools (start googling from nodemon)
If you find this way of operating not ideal, try to improve it, and check what people already did in this direction. For example, you can run two processes in parallel with concurrently.

Serving create-react-app build files on express server

I want to serve build files of my react app on my other express application. So I copied all the static files inside build folder to public folder inside my express application and I used.
app.use(express.static(path.join(__dirname, './public')));
app.get("/",(req,res)=>{
res.send()}
)
app.get("/differentRoute",(req,res)=>{
res.send()}
)
app.listen(4000)
When I make a get request on http://localhost:4000/ my react page loads perfectly, however when I make the same request on http://localhost:4000/differentRoute, nothing loads, can someone explain me how it works?
When you request / it finds the index.html file in the public directory, and serves it up using the static handler.
Nothing get ever get to the explicit handler for / since the static route always catches it first.
When you request /differentRoute, there is no matching file in the public directory so the explicit route for that is triggered. You call res.send() with no arguments, which doesn't make sense.
If you want to have URL based routing and don't want to use server side rendering, then use the HashRouter.
If you want to have real paths for the different pages in the app, then use server side rendering (e.g. with Next.js.

Why will my MEAN app only (partially) run when I set the Angular build directory to /src/ instead of /dist/?

I'm following Heroku's tutorial to create a contact list using the MEAN stack (Heroku's running example here). I'm able to deploy it to Heroku and it works there. But when I run it locally on my machine, the browser (Chrome 67.0.3396.87 on macOS High Sierra) only displays a "Cannot GET /" message.
I believe it's related to how the Angular build directory /dist/ referenced in line 12 of server.js does not exist (as far as I can tell). The beginning of server.js looks like this:
var express = require("express");
var bodyParser = require("body-parser");
var mongodb = require("mongodb");
var ObjectID = mongodb.ObjectID;
var CONTACTS_COLLECTION = "contacts";
var app = express();
app.use(bodyParser.json());
// Create link to Angular build directory
var distDir = __dirname + "/dist/";
app.use(express.static(distDir));
// Create a database variable outside of the database connection callback to reuse the connection pool in your app.
var db;
I looked into it and found that Angular deletes the /dist/ directory upon ng serve. I also found that there is a flag --delete-output-path whose default is true.
I set the --delete-output-path flag to false in .angular-cli.json as recommended by this answer as well as in /node_modules/#angular/cli/lib/config/schema.json. Despite those changes (trying to set the flag in one file, or the other file, or both files at the same time), I'm still getting the "Cannot GET /" message and the /dist/ directory still doesn't appear to be there.
The only way I've been able to even run part of the app is to change server.js's line 12 reference from /dist/ to /src/. This allows /src/index.html to begin loading at localhost:5000/ (the browser displays the text "Loading..." as specified in line 16 of index.html) and gets the contacts API up and running at localhost:5000/api/contacts/. But the Angular components (the list of contacts that is the purpose of the tutorial) don't load. Maybe because I changed the build directory to a totally different location.
Is there something with the /dist/ directory that I'm missing? Or does my issue with getting the app to run locally have nothing to do with /dist/ at all?
Notice that you don't have a way of handling requests to the route '/' since the line:
app.use(express.static(distDir));
only ensures that all bundled files generated in your "dist" folder are accessible when your index.html requires them, but you still have to serve the index.html itself. When using the MEAN stack one normally would do something like this:
app.use ('/api', yourApiRouter);
//and for everything else let the client-side routing handle the route:
app.get ('*', function(req, res) {
res.sendFile(distDir + 'index.html');
}
I recommend to use the native "path" module to join your __dirname with your "dist" folder and your index.html location rather than simple concatenation.
You can use an arrow function instead of a callback when using app.get function if you are using ES6

Basic Locally Hosted Web-server Functionality

I am working on an addition to a project to create dynamic HTML reports from data that is stored in a SQLite database. Initially, I tried to do everything client-side using things like browserify and sql.js, but I ran into a lot of issues trying to read from the .db file locally.
For that reason, I have now decided to spin up a very basic web server that will be locally hosted. Essentially, I want the user to be able to navigate to http://localhost:3000 and hit a landing page which is the home page of the report.
I have set up a very basic HTTP server using express with the following code running in node:
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.listen(process.env.PORT || 3000);
This works fine, and exposes the /public directory on port 3000, which has a placeholder index.html as of right now. My problem is, that when I try to start adding my code that reads from the SQLite database, none of the necessary require() functions work (specifically, require('fs'), due to it not being defined.
At a basic level, my question boils down to this:
How can i have the ability to read from the SQLite database file in the HTML/Javascript pages that live on the webserver? Whenever I try to use the necessary functions, it tells me that require() is not defined, or other similar errors.
Any help would be appreciated.

Enabling html5 mode with AngularJS and a external NodeJS server

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;
}
}

Categories

Resources