create-react-app Project reloading when local server generates files - javascript

I am building a developer tool which allows the user to query device data for a given time window from a DB, generates plots of the data using matplotlib, saves them to local files, and renders them on a webpage. I've used create-react-app for the frontend and a Flask backend, both of which are run locally. The issue is that when the backend creates a new directory for the plots, the react app reloads before the plots are even generated, and I get a 'Request aborted' error on the browser. The strangest thing is that it does not break on the first request, and embeds all the plots correctly. It breaks only on subsequent requests. How do I get around this?
Here's the plotter code that creates the directory and saves files:
fig_latest = plt.figure()
ax_latest = fig_latest.add_subplot(projection='3d')
plot_latest = ax_latest.bar3d(x, y, bottom, width, depth, latest_height, color='c')
fig_ani= plt.figure()
ax_ani = fig_ani.add_subplot(projection='3d')
def update_plot(frame, data):
for bar in ax_ani.collections:
ax_ani.collections.pop()
frame_data = data[frame].to_numpy().astype(np.int16)
height = frame_data.ravel()
print(f'Frame: {int(frame)}')
timestamp = time.strftime('%d %b %Y, %I:%M:%S %p', time.localtime(int(frame/1000)))
for text in ax_ani.texts:
text.set_visible(False)
ax_ani.text2D(0.6, 1, f'Time: {timestamp}', transform=ax_ani.transAxes)
return [ax_ani.bar3d(x, y, bottom, width, depth, height, color='c')]
bar_ani = animation.FuncAnimation(fig_ani, update_plot, frames=row_data.keys(), fargs=(row_data,), interval=750)
os.mkdir(path_name)
mpeg_writer = animation.FFMpegWriter(fps=2)
bar_ani.save(os.path.join(path_name, 'animation.mp4'), writer=mpeg_writer)
fig_latest.savefig(os.path.join(path_name, 'latest.png'))
The app reloads right after os.makedir is called.
Flask backend:
from flask import Flask, request
from flask_cors import CORS, cross_origin
import plotter
import json
app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "http://localhost:3001"}})
#app.route('/plots', methods=['POST', 'OPTIONS'])
#cross_origin(origin='localhost')
def respond():
data = request.json
device_id = data['deviceId']
start_time = data['startTime']
end_time = data['endTime']
print(f'{device_id}, {start_time}, {end_time}')
return plotter.build_plots(device_id, start_time, end_time)
Code for the POST request on the frontend:
const baseUrl = 'http://localhost:3001/plots'
const requestPlot = (deviceId, startTime, endTime) => {
const plotRequest = {
deviceId,
startTime,
endTime
}
console.log(plotRequest)
return axios
.post(baseUrl, plotRequest)
.then(response => {
console.log('RECEIVED RESPONSE!')
return response.data
})
}

Seems like you are running your frontend application in "development" mode via "npm start" command. Since webpack is watching all file changes and reloads frontend on each file editied or created. One way to fix it - is to run "npm build" command and setup nginx or run your frontend with SimpleHTTP server of python in "dist" directory

Related

404 Page not found Vercel Deployment - using dynamic routing in Next JS

I am making a full-stack web-application using Next JS where I allow the user to create and manage letters (applications) based on pre-defined templates. So when the user successfully creates an application, it is sent to the database (POSTGRES) which is hosted on Supabase. On the home page, the applications created by the user are fetched and displayed in the form of a list. Here on, when the user chooses to preview an application, dynamic routing is put in place where the application IDs work as the dynamic parameter. By using getStaticPaths() to get the route parameters from the database, and then fetching the data for the page from the database based on the application ID in the getStaticProps() method at build time, we render the page. It works seamlessly on localhost but not on Vercel. The interesting part however is,that dynamic routing works on Vercel for past applications for every deployment, that is if the user wants to preview their past applications they can do so without any problem, but when they create an application and then try to preview it, they are prompted with the 404 error. But if I trigger a redeployment either manually or by a commit to the main branch of my repository, the error is fixed for the particular application which was giving the error. `
export const getStaticPaths = async () => {
var APIendpoint;
if (process.env.NODE_ENV === 'development') {
APIendpoint = 'http://localhost:3000/api/fetchApplication'
}
else if (process.env.NODE_ENV === 'production') {
APIendpoint = 'https://templaterepo.vercel.app/api/fetchApplication';
}
const data = await getPaths(APIendpoint)
const paths = data.map((application) => {
return {
params: { id: application.appid.toString() }
}
})
return {
paths,
fallback: 'blocking'
}
}
export async function getStaticProps(context) {
const appID = context.params.id;
var APIendpoint;
if (process.env.NODE_ENV === 'development') {
APIendpoint = 'http://localhost:3000/api/fetchApplicationwithID'
}
else if (process.env.NODE_ENV === 'production') {
APIendpoint = 'https://templaterepo.vercel.app/api/fetchApplicationwithID';
}
let data = await getPageData(APIendpoint, appID);
return {
props: { data }
}
}
Here is the code for the dynamic [id].js page where in I first get the paths based on the application IDs and then in the getStaticProps() function I fetch the data for the page corresponding to the application ID at build time. It works as expected in localhost but in Vercel deployment, even before the functions are executed, I get a 404 error.
Note: Vercel Framework Preset is set to Next.js.
I tried a variety of solutions including adding to and as parameters in the Link component. Also I changed my vercel.json file to the below configuration
`
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
`
But nothing seems to work.
When they create an application and then try to preview it, they are
prompted with the 404 error. But if I trigger a redeployment either
manually or by a commit to the main branch of my repository, the error
is fixed for the particular application which was giving the error.
This is expected, the data necessary for each dynamic page to be built is fetched ONLY at build time. Since you are using getStaticProps, you can implement ISR by adding a revalidate prop in getStaticProps, that way when a page (like a new application) has not been generated at build-time, Next.js will server-render it on first request and then cache it for subsequent requests.
On development mode, both getStaticProps and getStaticPaths run per-request (much like getServerSideProps), that's why you don't have this issue on the dev environment. Reference to this on the docs.
If you decide to implement ISR and want to display a loading UI while the page is being server-rendered, make sure to set fallback: true on getStaticPaths and at component level, you can access the router.isFallback flag to display the loading UI accordingly, otherwise, leave it as you already have with fallback: 'blocking'.
Also, make sure you write the server-side code directly in getStaticProps and getStaticPaths instead of calling your own API endpoints on these functions. This according to the docs.

Data posted to flask endpoint from JS not processed in endpoint

I have written a simple todo app with react acting as a frontend and flask handling CRUD from a DB. The app is using axios to handle the requests; GET completes fine however when attempting to POST JSON the flask api returns a 400 error. Here's some condensed sample code.
JS POST function.
function testPost(){
axios.post('http://'+window.location.hostname+':8000/todo/', {
title: "test123",
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
}
Serverside
class Todo(Resource):
def post(self): # create a new todo
conn = pool.getconn()
cur = conn.cursor()
app.logger.info(request.form['title'])
cur.execute("INSERT INTO todo (task, done) VALUES (%s, %s)", (request.form['title'], False))
conn.commit()
app.logger.error(e)
cur.close()
pool.putconn(conn)
Other methods not shown
Then the rest of the server code attaching the resource to the api and the CORS setup (not shown in file order)
app = Flask(__name__)
CORS(app, methods=['POST','GET','PUT','DELETE'])
api = Api(app)
api.add_resource(Todo, '/todo/')
app.run(debug = True, host='0.0.0.0', port=port)
Tests
Using python to test the api works fine, running this in a seperate python file will add to the DB.
response = requests.post(URL + "todo/", data={"title": f"test{randint(1, 100)}"})
My best guess is that axios is not adding the data to the request in a way that the backend is unable to process. Before using axios I tried to make the request with XMLHttprequest however this presented the same problem. I swapped to axios on the recommendation of someone else, given its alleged improved simplicity.
request.form['key'] and request.get_json()['key'] are completely different fields python requests in the way I used it posts to the former and js posts to the latter. Modifying the function to use whichever is available fixes this.

Lowdb (json database) with Next.js via Netlify returns internal server error 500 on API route, works locally

I've got a really simple JSON flat file db setup that works when running locally but doesn't work once it's hosted on Netlify. I don't get any other error info besides a 500 error on the server. I get the error even if all I do is import the clusterDB object, so something is happening with the lowdb object. I've also tried using another json db library called StormDB and I get the same issue.
Return my API route with a static import of the json file (no db libraries) also works fine.
I'm new to Next.js and this seems related to maybe the SSR portion of things since the API routes run only on the server? Do I need to structure my files differently? Are these libraries not compatible? Lowdb says it works with Node, and everything works locally for me.
Here is my db init file (root/db/db.js)
import {Low, JSONFileSync} from 'lowdb'
// Cluster DB Setup
const adapter = new JSONFileSync('cluster-db.json')
const clusterDB = new Low(adapter)
// Initialize if empty
clusterDB.read()
clusterDB.data ||= { clusters: [] }
clusterDB.write()
export {clusterDB}
And my only API route (root/pages/api/clusters.js)
import {clusterDB} from '../../db/db'
export default async function handler(req, res) {
await clusterDB.read()
switch(req.method) {
case 'POST':
let newCluster = {severity: req.query.severity, comments: req.query.comments, date: req.query.date}
clusterDB.data.clusters.push(newCluster)
clusterDB.write()
res.status(200).json({status: "Success", cluster: newCluster})
break;
case 'GET':
if(clusterDB.data.clusters) {
res.status(200).json(clusterDB.data.clusters)
} else {
res.status(404).json({status: "404"})
}
break;
}
res.status(200).json({test: "yay"})
}

How to pull data from own Flask JSON route endpoint using Axios

I would like to save JSON data into a variable using Axios in Javascript. The route that produces the JSON endpoint is my own servers route http://123.4.5.6:7890/json. This works successfully with the following function:
async function getClasses() {
const res = await axios.get('http://123.4.5.6:7890/json');
}
However, I figure this won't work with someone else's server when they pull up my project, so what line of code would go into the http:// spot? My mentor recomended using 'http://localhost:5000/json' however this error occurs when I tried this.
Here is the python code for my json route:
#app.route('/json')
def display_json():
"""view/signup for available yoga classes using API"""
serialized_classes = [c.serialize() for c in Classes.query.all()]
return jsonify(serialized_classes)
When I go to the http://123.4.5.6:7890/json route in my browser. JSON does successfully appear in the browser. Thanks, and any help is appreciated
I think it's a CORS issue, you can use the below code to solve this issue.
var config = {
headers: {'Access-Control-Allow-Origin': '*'}
};
async function getClasses() {
const res = await axios.get('http://123.4.5.6:7890/json', config);
}
on your app.py or main.py file
from flask_cors import CORS
app = Flask(__name__)
CORS(app)

how can I get variable from javascript using flask

So I have set up app.py, index.js, index.html in appropriate folder as flask suggests. Index.html gets rendered as when app.py runs then index.html runs index.js which grabs input data from user. I am trying to send this input and send it to python where I can call an API, grab data, and work with it however I cannot think of a way to do this.
my app.py:
import os
from flask import Flask, render_template, jsonify, request, redirect
app = Flask(__name__)
# This will run upon entrance
#app.route("/")
def home():
return render_template("index.html")
#app.route("/stock_data")
def get_stock_data():
# called from index.js Plot function
if __name__ == "__main__":
app.run()
and here is my javascript code:
console.log("everythin works fine.")
d3.select("#stocklabelsubmit").on("click", submitted)
function submitted(){
d3.event.preventDefault();
// grab label inputted.
var inputted_label = d3.select("#stockInput").node().value;
d3.select("#stockInput").node().value = "";
Plot(inputted_label);
};
function Plot(input){
var url = "full url"
// when this function is called call /stock_data function!!
// data is what is returned from python
d3.json(url).then(function(data){
})
}
Everything works fine, when I console log inputted_label at the end of function submitted it works. Now I want to send inputted_label variable to /stock_data. Thanks in advance!
var url = "/stock_data"
This needs to be a valid URL, not just the path to the Flask endpoint. That means it must start with "http://" or "https://" and include a domain. For development purposes, "http://localhost/stock_data" will work. If you ever deploy this to a server, you will want to create a configuration file so that the host name can be configured depending on what environment you are running in.

Categories

Resources