So were trying to develop an application (or Service) with Node.js that provides each user a custom API that can be called from {theirUserName}.ourwebsite.com. Users will be able to change/edit/remote the endpoints of the API within the application through our editor. They can add params to the endpoints, add auth, etc.
Now my question is, how can we make the API online at first, then how can we change the endpoints online without stopping the API application and running again?
P.S: APIs configuration will be saved into a JSON that will be saved to the DB and once the configuration change an event will be raised that tells us the endpoints have changed.
Using Express, you can add routes after the server is listening, so it's not a problem. Beware of precedence as it will be added at the bottom of the stack.
I would advise to have a db storing routes, and when running the node app (before listening) load all the routes in db and add them to the router. In order to be able to scale your app as well as being able to restart it safely.
Then start listening, and have a route for adding routes, deleting routes, updating routes etc.
Here is a simple example of adding a route after listening :
const app = require('express')();
const bodyParser = require('body-parser');
app.use(bodyParser.json());
const someGenericHandler = function(req, res) {
return res.json({ message: 'foobar' });
};
// it creates a route
app.post('/routes', function(req, res) {
try {
const route = req.body;
app[route.method](route.path, someGenericHandler);
return res.json({ message: `route '${route.method}${route.path}' added` });
} catch(err) {
return res.status(500).json({ message: err.message || 'an error occured while adding the route' });
}
});
app.listen(process.env.PORT);
You can try this code, paste it in a file, let say index.js.
Run npm i express body-parser, then PORT=8080 node index.js, then send a POST request to http:/localhost:8080/routes with a json payload (and the proper content-type header, use postman)
like this: { method: 'get', path:'/' } and then try your brand new route with a GET request # http://localhost:8080/'
Note that if you expect to have hundreds of users and thousands of requests per minute, I would strongly advise to have a single app per user and a main app for user registering and maybe spawn a small VPS per app with some automation scripts when a user register, or have some sort of request limit per user.
Hope this helps
Related
I have a 4 year old Express project running in production and using Express 4.14. Various developers have kept on adding new features but some old code also remains inside. Is there a way to find unused code- code which is not getting used in a production application ?.
I would like to start by identifying routes that are not being called. We do use logs and logs are ingested in Kibana. We also use APM with Kibana.
Since you log data you can create simple middleware to log every request to your application. After a while (days or weeks, depending on how sure you will feel about this process) you can collect and parse logs to get all requested routes. Then compare requested routes with all routes available in your application and delete unused ones.
The middleware can be as simple as:
// runs for every request
app.use('/', function (req, res, next) {
console.log(`Received request: ${req.method} ${req.path}`);
next();
});
To get all routes registered in your application use this development-only code (inspired by this answer):
console.log(app._router.stack.forEach(middleware => {
if (middleware.route) {
console.log(`${middleware.route.stack[0].method} ${middleware.route.path}`);
} else if (middleware.name === 'router') {
middleware.handle.stack.forEach(route => {
if (route.route) {
console.log(`${route.route.stack[0].method} ${route.route.path}`);
}
});
}
}));
Ive been trying to deploy a Twitch like application using react, redux, node media server and json server module to Heroku. However, I keep running into a issue when trying to connect my react client and express server via a api request, during production.
Im trying to make the actual request through my action creators and by using axios with a base url of http://localhost:4000, however that only works on my local machine.
const response = await streams.get("/streams");
dispatch({ type: FETCH_STREAMS, payload: response.data });
};
You can view my full repo at https://github.com/XorinNebulas/Streamy
You can also view my current deployed version of the site on Heroku at
https://streamy-app.herokuapp.com/
here is my api/server.js file. My express server will be watching on a random port equal to process.env.PORT, so I have no way of knowing how to make a network request via my action creators to that random port during production.
const path = require("path");
const cors = require("cors");
const jsonServer = require("json-server");
const server = jsonServer.create();
const router = jsonServer.router("db.json");
const middlewares = jsonServer.defaults({
static: "../client/build"
});
const PORT = process.env.PORT || 4000;
// Set default middlewares (logger, static, cors and no-cache)
server.use(cors());
server.use(middlewares);
if (process.env.NODE_ENV === "production") {
// Add custom routes before JSON Server router
server.get("*", (req, res) => {
res.sendFile(
path.resolve(__dirname, "../", "client", "build", "index.html")
);
});
}
// Use default router
server.use(router);
server.listen(PORT, () => {
console.log(`JSON Server is listening on port ${PORT}`);
});
I expected the request to go thru and load up some data from api/db.json, with a resquest url of https://streamy-app.herokuapp.com/streams but instead i got a request url of http://localhost:4000/streams, which of course leads to the CORS issue below
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:4000/streams. (Reason: CORS request did not succeed).
I would truly appreciate any suggestions, been working on this for days.
Alright looks like I figured it out. I simply went into streams/client/package.json and added
"proxy":"http://localhost:4000"
I then went into streams\client\src and deleted the api folder which contained my custom axios with a base url. using instead axios out of the box for my action creators
const response = await axios.get("/streams");
dispatch({ type: FETCH_STREAMS, payload: response.data });
};
Now while running locally in development mode, I'm able to make a request to http://localhost:4000/streams, but after deploying my node app to Heroku I successfully make a request over to https://streamy-app.herokuapp.com/streams
hope this helps someone with slimier issues.
First, you should know that Heroku doesn't allow to expose multiple ports, which means you should change the approach of multiple ports to something else (see this answer).
Second, the file client/src/apis/streams.js is hard-coded configured to send requests to http://localhost:4000/ - which is not a good idea.
Whatever approach you choose - even deploying to another host server - you will need to dynamically configure the API endpoint, per environment.
I would also recommend you to:
Change the way you deploy react, as explained here.
After doing the above, consider consolidating your API service with the static server, so that you don't need multiple ports, and then everything becomes easier.
I have express on back-end and react.js on frontend, but i also have admin page with pug view engine, working on express routes, how can i use these in one domain
Expressjs is composable in a really nice way. You can have a top level express application which routes off to sub-express apps and serve your individual services.
Lets say you want to serve your react frontend from www.example.com, your admin (pug views) from www.example.com/admin, and you also want to have an api which serves the react frontend at www.example.com/api`.
You would want something a bit like the following code sample which demonstates the composition of express applications. I've not run the code but it should be enough to get you going.
// This parent app acts as a parent layer and router
// for all your "sub apps". Any middleware you apply
// to this express app will apply to *all your other
// sub-apps*.
const parentApp = express();
// We now create another express instance, this will
// house the API. It can be in another file and you
// could require in something like "require('api');"
// instead but for brevity we'll keep it all in one
// file.
const apiApp = express();
apiApp.get('/info', (req, res, next) => {
console.log('/info');
return res.sendStatus(200);
});
// Mount the sub app on the /api route. This means
// you can how hit 'www.example.com/api/info' and
// you'll get back a 200 status code.
parentApp.use('/api', apiApp);
// Now we setup the admin app which we'll add pug
// views into. This is an example so just pretend
// the views exist.
const adminApp = express();
adminApp.set('views', './views');
adminApp.set('view engine', 'pug');
adminApp.get('/login', (req, res, next) => {
return res.render('login', { title: 'Hey' });
});
// Mount the sub app on the /admin route. This way
// we can hit www.example.com/admin/login to get
// our login page rendered.
parentApp.use('/admin', adminApp);
// Now we create and mount the frontend app that
// serves our fully built react app. You could do
// this with nginx instead but you wanted to do
// it with express so lets do it that way.
const frontendApp = express();
frontendApp.use(express.static('/frontend));
parentApp.use('/', frontendApp);
If you'd rather not create yourself a top level express app (and thus creating a bit of a monolith application) then I'd recommend checking out the nginx documentation, or the docs for the HTTP server you use. You should be able to direct requests to particular endpoints to different node applications running on different ports. Static files can then be served natively by your HTTP server. This is definetely a more efficient and elegant approach, but since you asked about express I wanted to showcase that approach primarily.
I'm making a project using create-react-app. There is a configured server and so on. I'm using react-router-dom for routing in my app. There is 'Comments' component. When it starts render itself it goes to my local json file and takes comments from there using ajax. When user clicks 'submit' It sends POST request with form's fields to the same json file. I have code for adding a new object to my json file. It should work when user in '/api/comments' route . This is the code for adding a new object to my json file (requires express):
`app.post('/api/comments', function(req, res) {
fs.readFile(COMMENTS_FILE, function(err, data) {
if (err) {
console.error(err);
process.exit(1);
}
var comments = JSON.parse(data);
var newComment = {
id: Date.now(),
author: req.body.author,
text: req.body.text,
};
comments.push(newComment);
fs.writeFile(COMMENTS_FILE, JSON.stringify(comments, null, 4),
function(err) {
if (err) {
console.error(err);
process.exit(1);
}
res.json(comments);
});
});
});`
But I don't know where I shoud put this code if I'm using 'create-react-app' and it uses it's own configured server (as far I know). Maybe there is a way to change server which 'create-react-app' uses and put there this code to handle this route? Or maybe there is a way to handle this route using 'react-router'?
If I understand your question correctly the code you have posted here is server side code. The app you have made using create-react-app is a front end application and therefore does not have any server side code. You could however host a second server that would expose the api routes you need and then call into that server using a http library like axios.
I'm using a create-react-app and express as my api server. Setting up express to run alongside webpack-dev-server is a supported feature of create-react-app.
I use npm-run-all to fire-up both the client and proxy express api server in my start-up script defined in package.json. Here is what I believe is all that I needed to do:
In my webpack.config.dev.json file I defined a proxy setting in the devServer json block. Specifically:
proxy: { "/api": "http://localhost:3001" },
In my package.json file I configured a start script that uses npm-run-all to fire up both the react-app and express simultaneously.
I use server.js to fire-up express; this is where I store the equivalent of the code you outlined in your question.
What is the best way to send POST request from node server which has received the request parameter from a client? Reason I am asking for best practice because it should not affect the response time if multiple clients are calling the node service.
Here is the Backbone Model which sends the request to node server:
var LoginModel = Backbone.Model.extend({
url:'http://localhost:3000/login',
defaults: {
email:"",
password:""
},
parse: function(resp) {
return resp;
},
login: function() {
console.log('Here in the model'+JSON.stringify(this));
this.save();
}
});
var loginModel = new LoginModel();
Node Server
var http = require('http'),
express = require('express');
var app = express();
app.listen(3000);
app.post('/login', [express.urlencoded(), express.json()], function(req, res) {
console.log('You are here'); console.log(JSON.stringify(req.body));
//Send the post request to third party service.
});
Should I use something like requestify inside app.post() function and make a call to third party service?
I like superagent personally but request is very popular. hyperquest is also worth consideration as it resolves some issues with just using the node core http module for this.
Reason I am asking for best practice because it should not affect the response time if multiple clients are calling the node service.
First, just get it working. After it's working you can consider putting a cache somewhere in your stack either between your clients and your api or between your server and the third party api. I'm of the opinion that if you don't know exactly where you need a cache, exactly why, and exactly how it will benefit your application, you don't need a cache, or at the very least, you aren't prepared instrumentation-wise to understand whether your cache is helping or not.