ExpressJS API routes when hosting app in subdirectory - node.js

I have one app hosted at the root and I would like to host this new app in a subdirectory /rps using proxypass etc. Everything is working fine except when I try to call my api I get 404 errors in routes for example https://myapp.com/rps/api/posts/. The front end app is basically static served from the public folder of express.
Open to any ideas what I may doing wrong or alternate methods. I'd really like not to have a new server for this small application and I can't really build it into the already existing app. Of course, the routes were working locally, just not when I host in my strange scenario on the server...
See code:
(server) index.js:
const config = require('./config.js');
const app = express()
const port = process.env.PORT || 5000;
var indexRouter = express.Router();
const posts = require('./routes/api/posts')
indexRouter.use('/api/posts', posts)
app.use(express.static(__dirname+ '/public'))
indexRouter.get(/.*/, (req, res) => res.sendFile(__dirname+'/public/index.html'))
app.use(config.baseUrl, indexRouter);
(server) config.js:
module.exports = {
baseUrl: '/rps/',
}
(server) ./routes/api/posts.js:
const router = express.Router()
router.post('/', (req, res) => {
//gets my thang in action...
})
module.exports = router
(client) main.js:
axios.post('/api/posts/', { // /rps/api/posts ??
stuff: things,
})
.then(function (response) {
// do something else
})
.catch(function (error) {
console.log(error);
})
Thank you in advance!

Related

Nextjs Routes are not working when deployed to Azure App Service Linux Instance

I wanted to deploy a nextjs app to Azure App Service with Linux instance.
I followed the instructions here: https://developinjay.com/deploying-nextjs-app-to-azure-app-service-linux-77a43353e761
The app is live here https://interviewramp.azurewebsites.net and https://interviewramp.herokuapp.com.
Routes work in Heroku. So if user goes here https://interviewramp.herokuapp.com/books/interviewramp/introduction it works.
Routes don't work in Azure App service. So, if user goes here https://interviewramp.azurewebsites.net/books/interviewramp/introduction, I get 404 document not found error, which means code is trying to serve an introduction.html file in Azure and returning 404 because there is no html file like that.
All links work locally when I npm run start or npm run dev.
Pasting url like this to browser gives 404 : https://interviewramp.azurewebsites.net/login
I have never done next export in my code. I was wondering if you could help me?
Below is my server.js
const express = require('express');
const session = require('express-session');
const mongoSessionStore = require('connect-mongo');
const next = require('next');
const api = require('./api');
require('dotenv').config();
const dev = process.env.NODE_ENV !== 'production';
const MONGO_URL = dev ? process.env.MONGO_URL_TEST : process.env.MONGO_URL;
const port = process.env.PORT || 8000;
const ROOT_URL = getRootUrl();
const URL_MAP = {
'/login': '/public/login',
'/my-books': '/customer/my-books',
};
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(async () => {
const server = express();
server.use(helmet({ contentSecurityPolicy: false }));
server.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
server.use(express.json());
// give all Nextjs's request to Nextjs server
server.get('/_next/*', (req, res) => {
handle(req, res);
});
api(server);
routesWithSlug({ server, app });
server.get('*', (req, res) => {
const url = URL_MAP[req.path];
if (url) {
app.render(req, res, url);
} else {
handle(req, res);
}
});
server.listen(port, (err) => {
if (err) throw err;
logger.info(`> Ready on ${ROOT_URL}`);
});
});
I was able to solve this problem and I am leaving this answer in case it is helpful to someone else. The Azure web app is showing correct paths now. I used node server.js instead of next start in my startup command in Azure and it fixed the issue.

Can't retrieve information from mongodb when deploying on heroku

The problem is as the title suggests. When I run my app locally, I'm able to retrieve information from MongoDB but on Heroku, undefined is returned. Should I connect to MongoDB in another way because if I hardcode some text everything works just fine. Here are my scripts:
function to get data
const MongoClient = require("mongodb").MongoClient;
const dbConnectionUrl = "mongodb+srv://xxxxxxx#cluster0.ro4dz.mongodb.net/data?retryWrites=true&w=majority";
const saySomething = (req, res, next) => {
// res.status(200).json({
// body: 'Hello from the server!'
// });
login()
.then(val=>res.send(val))
};
async function login(){
const client = new MongoClient(dbConnectionUrl)
try{
await client.connect();
const database = client.db("data");
const movies = database.collection("movies");
const query = { name: "toke" };
const movie = await movies.findOne(query);
return movie
}catch(err){
console.log(err)
}
}
module.exports.saySomething = saySomething;
router
const express = require('express');
const router = express.Router();
const controllers = require('./../controllers/controllers');
router.get('/say-something', controllers.saySomething);
module.exports = router;
server
// Import dependencies
const express = require('express');
const cors = require('cors');
const path = require('path');
// Create a new express application named 'app'
const app = express();
// Set our backend port to be either an environment variable or port 5000
const port = process.env.PORT || 5000;
// This application level middleware prints incoming requests to the servers console, useful to see incoming requests
app.use((req, res, next) => {
console.log(`Request_Endpoint: ${req.method} ${req.url}`);
next();
});
// Configure the CORs middleware
// Require Route
app.use(cors());
const api = require('./routes/routes');
// Configure app to use route
app.use('/api', api);
// This middleware informs the express application to serve our compiled React files
if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging') {
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
};
// Catch any bad requests
app.get('*', (req, res) => {
res.status(200).json({
msg: 'Catch All'
});
});
// Configure our server to listen on the port defiend by our port variable
app.listen(port, () => console.log(`BACK_END_SERVICE_PORT: ${port}`));
front
import { useEffect, useState } from 'react';
import './App.css';
import axios from 'axios'
function App(){
useEffect(()=>{
get()
})
const[text, settext] = useState('')
async function get(){
let request = await axios.get('/api/say-something')
console.log(request.data.name)
settext(request.data.name)
}
return(
<div>{text}</div>
)
}
export default App;
I solved the issue! The first thing I did was that I added MongoDB connection URI as an environmental variable in my app via Heroku. Secondly, I added an option in MongoDB so that the cluster can be accessed from any computer. By default, the access is set to the local computer so I added another IP, namely 0.0.0.0/0 to my cluster, and now everything works just fine.

Expressjs server and external api calls

I'm new to frontend development and express server. When I tried to start an express.js server with react (with axios calls to external apis), it seems express.js is adding 'localhost:3000' in front of the external API calls so they fail.
In my server.js:
const path = require('path');
const express = require('express');
const app = express();
const publicPath = path.join(__dirname, '.', 'dist');
const port = process.env.PORT || 3000;
app.use(express.static(publicPath));
app.get('*', (req, res) => {
res.sendFile(path.join(publicPath, 'index.html'));
});
app.listen(port, () => {
console.log('Server is up!');
});
Which leads to the API call to www.example.com/api/ to become http://localhost:3000/www.example.com/api/
I also tried to filter the req by writing:
app.get('*', (req, res) => {
if (req.url.match(/\/api\//) === null) {
res.sendFile(path.join(publicPath, 'index.html'));
}
});
But it does not change things...
Can anyone help out this newbie that is me?
Update1 Adding the code for calling the api:
This is the api call:
const getSomething = () => {
try {
const url = endpoints.GET_SOMETHING;
return axios.get(url);
} catch (err) {
console.log(err);
}
};
endpoints.GET_SOMETHING is the api URL: www.example.com/api/getSomething
You need to put a / in the url
app.get('/*', (req, res) => {
res.sendFile(path.join(publicPath, 'index.html'));
});
and also your endpoint url should start with https://, http:// or //

How to make express routing work in electron

This is my code:
<script type="text/javascript">
const express = require('express'),
router = express.Router();
var hosSchemaModel = require('../schema.js')
var path = require("path")
// const app = express()
var port = process.env.PORT || 8080;
router.listen(port);
router.get('/requests/:_id', (req, res, next) => {
console.log('Dynamic Link WORKS!!');
hosSchemaModel.findOne({ _id: req.params._id }, function(err, request){
res.json(request)
// res.sendFile(path.join(__dirname+'../homePage.html'))
});
});
router.get('/all', (req, res) => {
console.log('Dynamic Link WORKS!!');
res.send('WORKS!!');
// hosSchemaModel.findOne({ _id: req.params._id }, function(err, request){
// res.json(request)
// res.sendFile(path.join(__dirname+'../homePage.html'))
// });
});
module.exports = router;
</script>
I tried to put it in the main process as well but it didn't work and I figured it should be implemented in the client-side. The routers doesn't work.
when I use <a href="/all" it takes me to an empty page and nothing is printed to the console
What am I doing wrong here?
This code is Node related and should be in a separated file. Let's call it server.js.
It should also load your static files and become your main server.
Then in the electron main.js file, you should require your express server and load:
const server = require('./server');
...
mainWindow.loadUrl('http://localhost:5000')
Complete example here: https://gist.github.com/maximilian-ruppert/a446a7ee87838a62099d

How to include route handlers in multiple files in Express? [duplicate]

This question already has answers here:
How to separate routes on Node.js and Express 4?
(9 answers)
Closed 1 year ago.
In my NodeJS express application I have app.js that has a few common routes. Then in a wf.js file I would like to define a few more routes.
How can I get app.js to recognize other route handlers defined in wf.js file?
A simple require does not seem to work.
If you want to put the routes in a separate file, for example routes.js, you can create the routes.js file in this way:
module.exports = function(app){
app.get('/login', function(req, res){
res.render('login', {
title: 'Express Login'
});
});
//other routes..
}
And then you can require it from app.js passing the app object in this way:
require('./routes')(app);
Have a look at these examples: https://github.com/visionmedia/express/tree/master/examples/route-separation
In Express 4.x you can get an instance of the router object and import another file that contains more routes. You can even do this recursively so your routes import other routes allowing you to create easy-to-maintain URL paths.
For example, if I have a separate route file for my /tests endpoint already and want to add a new set of routes for /tests/automated I may want to break these /automated routes out into a another file to keep my /test file small and easy to manage. It also lets you logically group routes together by URL path which can be really convenient.
Contents of ./app.js:
var express = require('express'),
app = express();
var testRoutes = require('./routes/tests');
// Import my test routes into the path '/test'
app.use('/tests', testRoutes);
Contents of ./routes/tests.js:
var express = require('express'),
router = express.Router();
var automatedRoutes = require('./testRoutes/automated');
router
// Add a binding to handle '/tests'
.get('/', function(){
// render the /tests view
})
// Import my automated routes into the path '/tests/automated'
// This works because we're already within the '/tests' route
// so we're simply appending more routes to the '/tests' endpoint
.use('/automated', automatedRoutes);
module.exports = router;
Contents of ./routes/testRoutes/automated.js:
var express = require('express'),
router = express.Router();
router
// Add a binding for '/tests/automated/'
.get('/', function(){
// render the /tests/automated view
})
module.exports = router;
Building on #ShadowCloud 's example I was able to dynamically include all routes in a sub directory.
routes/index.js
var fs = require('fs');
module.exports = function(app){
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
Then placing route files in the routes directory like so:
routes/test1.js
module.exports = function(app){
app.get('/test1/', function(req, res){
//...
});
//other routes..
}
Repeating that for as many times as I needed and then finally in app.js placing
require('./routes')(app);
If you're using express-4.x with TypeScript and ES6, this would be the best template to use:
src/api/login.ts
import express, { Router, Request, Response } from "express";
const router: Router = express.Router();
// POST /user/signin
router.post('/signin', async (req: Request, res: Response) => {
try {
res.send('OK');
} catch (e) {
res.status(500).send(e.toString());
}
});
export default router;
src/app.ts
import express, { Request, Response } from "express";
import compression from "compression"; // compresses requests
import expressValidator from "express-validator";
import bodyParser from "body-parser";
import login from './api/login';
const app = express();
app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());
app.get('/public/hc', (req: Request, res: Response) => {
res.send('OK');
});
app.use('/user', login);
app.listen(8080, () => {
console.log("Press CTRL-C to stop\n");
});
Much cleaner than using var and module.exports.
Full recursive routing of all .js files inside /routes folder, put this in app.js.
// Initialize ALL routes including subfolders
var fs = require('fs');
var path = require('path');
function recursiveRoutes(folderName) {
fs.readdirSync(folderName).forEach(function(file) {
var fullName = path.join(folderName, file);
var stat = fs.lstatSync(fullName);
if (stat.isDirectory()) {
recursiveRoutes(fullName);
} else if (file.toLowerCase().indexOf('.js')) {
require('./' + fullName)(app);
console.log("require('" + fullName + "')");
}
});
}
recursiveRoutes('routes'); // Initialize it
in /routes you put whatevername.js and initialize your routes like this:
module.exports = function(app) {
app.get('/', function(req, res) {
res.render('index', { title: 'index' });
});
app.get('/contactus', function(req, res) {
res.render('contactus', { title: 'contactus' });
});
}
And build yet more on the previous answer, this version of routes/index.js will ignore any files not ending in .js (and itself)
var fs = require('fs');
module.exports = function(app) {
fs.readdirSync(__dirname).forEach(function(file) {
if (file === "index.js" || file.substr(file.lastIndexOf('.') + 1) !== 'js')
return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
I am trying to update this answer with "express": "^4.16.3". This answer is similar to the one from ShortRound1911.
server.js:
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const db = require('./src/config/db');
const routes = require('./src/routes');
const port = 3001;
const app = new express();
//...use body-parser
app.use(bodyParser.urlencoded({ extended: true }));
//...fire connection
mongoose.connect(db.url, (err, database) => {
if (err) return console.log(err);
//...fire the routes
app.use('/', routes);
app.listen(port, () => {
console.log('we are live on ' + port);
});
});
/src/routes/index.js:
const express = require('express');
const app = express();
const siswaRoute = require('./siswa_route');
app.get('/', (req, res) => {
res.json({item: 'Welcome ini separated page...'});
})
.use('/siswa', siswaRoute);
module.exports = app;
/src/routes/siswa_route.js:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.json({item: 'Siswa page...'});
});
module.exports = app;
If you want a separate .js file to better organize your routes, just create a variable in the app.js file pointing to its location in the filesystem:
var wf = require(./routes/wf);
then,
app.get('/wf', wf.foo );
where .foo is some function declared in your wf.js file. e.g
// wf.js file
exports.foo = function(req,res){
console.log(` request object is ${req}, response object is ${res} `);
}
One tweak to all of these answers:
var routes = fs.readdirSync('routes')
.filter(function(v){
return (/.js$/).test(v);
});
Just use a regex to filter via testing each file in the array. It is not recursive, but it will filter out folders that don't end in .js
I know this is an old question, but I was trying to figure out something like for myself and this is the place I ended up on, so I wanted to put my solution to a similar problem in case someone else has the same issues I'm having. There's a nice node module out there called consign that does a lot of the file system stuff that is seen here for you (ie - no readdirSync stuff). For example:
I have a restful API application I'm trying to build and I want to put all of the requests that go to '/api/*' to be authenticated and I want to store all of my routes that go in api into their own directory (let's just call it 'api'). In the main part of the app:
app.use('/api', [authenticationMiddlewareFunction], require('./routes/api'));
Inside of the routes directory, I have a directory called "api" and a file called api.js. In api.js, I simply have:
var express = require('express');
var router = express.Router();
var consign = require('consign');
// get all routes inside the api directory and attach them to the api router
// all of these routes should be behind authorization
consign({cwd: 'routes'})
.include('api')
.into(router);
module.exports = router;
Everything worked as expected. Hope this helps someone.
index.js
const express = require("express");
const app = express();
const http = require('http');
const server = http.createServer(app).listen(3000);
const router = (global.router = (express.Router()));
app.use('/books', require('./routes/books'))
app.use('/users', require('./routes/users'))
app.use(router);
routes/users.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'John Smith'})
}
module.exports = router
routes/books.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'Dreams from My Father by Barack Obama'})
}
module.exports = router
if you have your server running local (http://localhost:3000) then
// Users
curl --request GET 'localhost:3000/users' => {name: 'John Smith'}
// Books
curl --request GET 'localhost:3000/books' => {name: 'Dreams from My Father by Barack Obama'}
I wrote a small plugin for doing this! got sick of writing the same code over and over.
https://www.npmjs.com/package/js-file-req
Hope it helps.
you can put all route functions in other files(modules) , and link it to the main server file.
in the main express file, add a function that will link the module to the server:
function link_routes(app, route_collection){
route_collection['get'].forEach(route => app.get(route.path, route.func));
route_collection['post'].forEach(route => app.post(route.path, route.func));
route_collection['delete'].forEach(route => app.delete(route.path, route.func));
route_collection['put'].forEach(route => app.put(route.path, route.func));
}
and call that function for each route model:
link_routes(app, require('./login.js'))
in the module files(for example - login.js file), define the functions as usual:
const login_screen = (req, res) => {
res.sendFile(`${__dirname}/pages/login.html`);
};
const forgot_password = (req, res) => {
console.log('we will reset the password here')
}
and export it with the request method as a key and the value is an array of objects, each with path and function keys.
module.exports = {
get: [{path:'/',func:login_screen}, {...} ],
post: [{path:'/login:forgotPassword', func:forgot_password}]
};

Resources