POST request with updating json file using create-react-app - node.js

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.

Related

React fullstack architecture: When adding a react front-end to a node/express application, what aspects does react's state generally handle?

I have a fully built node/express application that I want to add react to in order to practice that relationship in full stack applications. I've built apps in react before, and in node, but never together and I am confused about how react fits into the MVC architecture.
In a react-node full stack application does react state then handle all of the data I was previously passing into my ejs views?
I have been looking through tutorials on full stack applications with node and react, but they only seem to go into issues like how does react fetch data from the back end, or how to set up the configuration,
but I get that part, I just don't understand what react does in a full stack application, what part of the model-controller-view architecture of a node/express backend app does react take over? How are the responsibilities split between the backend and front end?
So for example, I'm working with a reddit-clone type app so when you click on a post title to see the post my controller queries the database for that post and then passes it to the view as {post}:
show(req, res, next){
postQueries.getPost(req.params.id, (error, post) => {
if(error || post == null){
res.redirect(404, "/");
} else {
res.render("posts/show", {post});
}
});
},
So when I add a front-end with react, would that {post} object then be something handled by react? So react would fetch that data and use it in a post component to create what is currently my view show.ejs file?
So when I add a front-end with react, would that {post} object then be something handled by react? So react would fetch that data and use it in a post component to create what is currently my view show.ejs file?
Yes. The show.ejs would be a React view or a page that contains a component to handle how to show it.
To simplify:
React -- is a virtual DOM, so it'll swap views/containers/components in and out based upon events (like clicking a button), which in turn, will: retrieve, display, manipulate and/or send data to the API. In development, it is completely separate from your back-end. All the routing will be handled by a front-end router package. In production, all of the front-end src code is compiled into a dist or build folder that contains your assets (images, fonts, css) and most importantly bundle.js file(s) that are then served by express.
Express + some database -- will act as your API where it'll CRUD data based upon the front-end request(s). If your app is a MPA (multiple page application), then a common practice is to delineate your back-end routes from your front-end routes with a /api/ prefix. In production, if express doesn't recognize the route (it's not a /api/ request), then it'll fall back into the front-end bundle.js file where it'll be handled by the front-end router.
See a working example here: https://github.com/mattcarlotta/fullstack-mern-kit (client is the frontend, everything else is the backend)
Or
See a working codesandbox (where I'm making a GET request to an API that returns json):
For your example above, your show controller will just be sending JSON (or a string message) back to the frontend (redirects will happen on the frontend via a router -- like react-router-dom):
show(req, res, next){
postQueries.getPost(req.params.id, (error, post) => {
if(error || post == null){
// res.status(404).send("Unable to locate posts.");
res.status(404).json({ err: Unable to locate posts });
} else {
res.status(200).json({ post });
}
});
},
You can even simplify the above using async/await:
const show = async (req, res, done) => {
try {
const post = await postQueries.getPost(req.params.id);
res.status(200).json({ post });
} catch(err) {
// res.status(404).send("Unable to locate posts.");
res.status(404).json({ err: Unable to locate posts });
}
};
And then the React front-end handles the response.

Node.js / Express.js + Angular router - server overwriting client view with response object when using direct link

I am building a node.js app with express, I am hosting an Angular SPA in the public folder.
The app runs and the hosting works fine when I use the angular router for navigation around the website, but when I directly try to access the link, for example: http://192.168.1.4:3000/posts, the entire body of the website is just the JSON response object, without the app
this is the Node.js code handling the get request
postRouter.route('/')
.options(cors.corsWithOptions, (req, res) => {
res.sendStatus(200);
})
.get(cors.cors, (req, res, next) => {
posts.find({})
.then((post) => {
res.status(200);
res.setHeader('Content-Type', 'application/json')
res.send(post);
}, (err) => next(err))
.catch((err) => next(err));
})
this is my angular service sending out the get request
getPosts(): Observable<Post[]> {
return this.http.get(baseURL + 'posts')
.catch(error => { return this.processHttpService.handleError(error); });
}
Post Component .ts file
ngOnInit() {
this.postService.getPosts()
.subscribe(posts => { this.posts = posts, console.log(this.posts); },
errmess => this.errMess = <any>errmess);
}
Again, when i use my Angular 5 client app hosted in the public folder, built with ng build --prod, the JSON object is retrieved from the mongodb database and is displayed correctly on my website, along with the rest of the app, the header, the body, and the footer.
it might also be worth noting that the console.log on the ngOnInit() is not displayed on the browser when using the direct link.
Any advice/fix is greatly appreciated
You have a clash of routes between angular and your express application. Angular is served up on one route (I'm guessing the / route) and then it sort of "hijacks" the users navigation. It does this by not actually changing web pages, instead it just changes the URL in the navigation bar, but never actually makes a web request to get to that resource.
You've then got endpoints on a web server listening on those endpoints. This means the moment you visit the /posts page, you're not asking angular to do anything. In fact, angular isn't even loaded because that only gets loaded on the / route. Instead you're going straight to your API.
There are ways around this, to start with many people put their API fairly separately, either on a subdomain or mounted on /api (such as /api/posts). Then your angular app can be served up on the / route. There are other techniques you can use to then allow a user to go to /posts and still get your angular app loaded.
You can use a few approaches for this such as the hash location strategy, or you can serve up your angular application from any route on the application (* in express) and load the angular app which will then take over. This second approach is most comment, it usually results in hosting your api on a sub domain and then serving your angular app on the * route of the normal domain name. For example: api.myapp.com will serve only JSON responses, but any route on myapp.com will serve the angular app, such as myapp.com/posts.

Webpack dev server "before" functionality

I'm working on an ejected Create React App project, and looking at the docs on webpack dev server, they seem a little bare: https://webpack.js.org/configuration/dev-server/#devserver-before
but I'm trying to see if it's possible to do something like:
before(app){
// read cookie for user session
// send user ID in cookie to external API
// retrieve user object from API
// attach user object to response, to be _somehow_ accessed via the React app client side
}
I know this is pseudo code, but I'm very unclear about what exactly you can do within this middleware, in terms of hooking into Create React App's rendering of the index.html and aforementioned client-side React app
In the docs, it says you could define a route handler, like so:
app.get('/some/path', function(req, res) { }
but I don't feel like that's going to be useful, as you wouldn't then be able to hook back into Webpack dev server's rendering process?
You can try adding this in your webpack config file, and practically you can use it as a mock server. Here is an example of how I configured mine and hope it helps.
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true,
before:(app) => {
app.post('/user/login', function(req, res, next) {
res.json({success: true})
});
}
},
In Webpack 5 you can get this goodness by configuring devServer.onBeforeSetupMiddleware.
Angies' answer applies to Webpack 4 devServer.before configurations.

Making and changing APIs when online in Nodejs

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

create-react-app with Express

I need to query a database and I'm using create-react-app. The library to connect to the DB (pg-promise) does not work with Webpack and needs to be running on a Node server.
So I installed Express and have this:
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '..', 'build', 'index.html'));
})
How can I load data from the database from the React pages? I though of using request but how can I make a request to my own server? And what should I add to the lines of code above? I think it would be something like:
app.get('/query/:querybody', (req, res) => {
// process and return query
})
Is this right? How can I make it work with a SPA?
Probably the most friction-free method would be to have a separate app.js or server.js along side your CRA application. You can use a tool like concurrently to run both your React app and the express app.
The trick is to serve your express app on a different port than the default :8080 that CRA serves on. Usually 8081 is a good choice, as it's a common convention to use port numbers that are close together when developing.
In your React app, you will need to make sure you use the full URL for the express endpoint: http://localhost:8081/query/...
On the server side you are going in the correct direction: you need to setup endpoint which will respond with data based on request. In you example you setup an endpoint for a GET HTTP request. If you will need to pass a complex request (for example add new record to database), consider using POST HTTP requests.
On the client side (in the browser) you will need a library that will assist you in sending requests to your server. I can recommend to try Axios (https://github.com/mzabriskie/axios). Usually if you omit protocol, server name and port, request will be sent to the server from which the page was loaded:
http:127.0.0.1:8001/api/endpoint => /api/endpoint

Resources