How to reflect changes in client when database changes? - node.js

So here's my scenario: I have a client page, where any one can fill and submit a form. the form data is stored in a database. There is a separate admin pc which is on an admin page, where every new form entry is displayed. How to update the front end of the admin everytime a new form is submitted, without refreshing/re hitting the API?
I am using React with express and MongoDB.

You can use TanStack Query (also called react query) ,
it's useQuery (a data fetching method) has a build-in property called isFetching which allow automatic fetching in a specific interval you want, like :
const { data,isLoading,isFetching } = useQuery(
['random-data'],
async () => {
const { data } = await axios.get(
'https://random-data-api.com/api/v1/random_data'
);
return data;
},
{
// refetch data every sec.
refetchInterval: 1000,
}
);
you can check more here

Related

How to load selected item from database?

So I have a default model set up for viewing my data, and a form for inputting the data. I want to know what the best practice is for retrieving the one item of selected data? it's for a MERN stack
Currently I am using window hash and adding the id onto the url and retrieving from database that way, I feel this is janky though and trying to add update functionality it seems like it might get confusing.
I've thought about adding a currentID to redux, but then I can see problems occurring when that is persisted and you go to create a recipe after viewing and end up editing instead of creating.
retrieving id from url
const recipeId = window.location.hash.substr(1);
const recipe = useSelector((state) =>
state.recipes.find((r) => r._id === recipeId)
);
I get my recipes from mongo
export const recipeList = async (req, res) => {
try {
const recipes = await recipeSheet.find();
res.status(200).json(recipes);
} catch (error) {
res.status(404).json({ message: error.message });
}
};
and store to redux
export const getRecipes = () => async (dispatch) => {
try {
const { data } = await api.fetchRecipes();
dispatch({ type: "FETCH_ALL_RECIPES", payload: data });
} catch (error) {
console.log(error.message);
}
};
It depends on how large is your data. It'd better define a new GET path to retrieve a single record, like BASE_URL/api/recipes/123 or you can add query acceptance for the current endpoint to find a specific id in DB and return it, like BASE_URL/api/recipes?id=123. The reason for that is besides the optimization (for large data sets), the record may change after you store all records to the redux store, and by the current solution, you show the old data to the user. Best practices tell us to choose the first way as your solution, the second way is usually for filtering the data. Then simply by sending the new URL by the user, trigger a new API call to the new endpoint and get the single record.

Node / Express generate calendar URL

I have a database with a bunch of dates and an online overview where you can view them, now I know I can copy a URL from my Google Agenda and import this in other calendar clients so I can view the events there.
I want to generate an Express endpoint where I fetch every event every time the endpoint is called and return it in a format that can be imported by other calendar clients. Now with packages like iCal-generator I could generate, read, and return the file whenever a user requests the URL. but it feels redudent to write a file to my storage to then read it, return it and delete it every time it's requested.
What is the most effiecent way to go about this?
Instead of generating the file/calendar data on every request, you could implement a simple caching mechanism. That is, upon start of your node app you generate the calendar data and put it in your cache with corresponding time to live value. Once the data has expired or new entries are inserted into your DB you invalidate the cache, re-generate the data and cache it again.
Here's a very simple example for an in-memory cache that uses the node-cache library:
const NodeCache = require('node-cache');
const cacheService = new NodeCache();
// ...
const calendarDataCacheKey = 'calender-data';
// at the start of your app, generate the calendar data and cache it with a ttl of 30 min
cacheCalendarData(generateCalendarData());
function cacheCalendarData (calendarData) {
cacheService.set(calendarDataCacheKey, calendarData, 1800);
}
// in your express handler first try to get the value from the cache
// if not - generate it and cache it
app.get('/calendar-data', (req, res) => {
let calendarData = cacheService.get(calendarDataCacheKey);
if (calendarData === undefined) {
calendarData = generateCalendarData();
cacheCalendarData(calendarData);
}
res.send(calendarData);
});
If your app is scaled horizontally you should consider using redis.
100% untested, but I have code similar to this that exports to a .csv from a db query, and it might get you close:
const { Readable } = require('stream');
async function getCalendar(req, res) {
const events = await db.getCalendarEvents();
const filename = 'some_file.ics';
res.set({
'Content-Type': 'text/calendar',
'Content-Disposition': `attachment; filename=${filename}`,
});
const input = new Readable({ objectMode: true });
input.pipe(res)
.on('error', (err) => {
console.error('SOME ERROR', err);
res.status(500).end();
});
events.forEach(e => input.push(e));
input.push(null);
}
if you were going to use the iCal generator package, you would do your transforms within the forEach method before pushing to the stream.

how to refresh page when hitting api endpoint (nuxt.js)

I'm making an app that displays a list of orders. The problem is when I submit a new order by hitting an endpoint with a post request with the data for a new order, the page or the components need to refresh automatically to display this new order. I don't know how this is achieved with nuxt. The client-side HTML rendering needs to be actively reacting to events happening on the server-side.
So, you don't actually need to refresh the page to do this, you can use axios to POST an order and update your page with that particular order
export default {
data(){
return {
newOrder: null
}
},
methods:{
newOrder(){
const newOrder = await this.$axios.post(...)
this.newOrder = newOrder
},
}
}

Add active connections to channel when it's created

I am trying to prevent who can see events for a particular page, so I am determining and storing the ids of which users have access to a particular page. In channels.js, I can add users to the new page-level channel in app.on('connection') by iterating through the pages like this: app.channel('page-' + pageId).join(connection)
The problem is that a user doesn't start getting broadcasts from that page until after they refresh the browser and re-connect.
What I want to happen is have all connections for a an allowed user start getting broadcasts when the page is created. Is there a way to do that in channels.js, or can I tell it who to start broadcasting to in a hook for the page creation?
Editing to add the last thing I tried. "Users-pages" is an associative entity that links Users and Pages.
app.service('users-pages').on('created', userPage => {
const { connections } = app.channel(app.channels).filter(connection =>
connection.user.id === userPage.userId
)
app.channel('page-' + userPage.pageId).join(connections)
})
The keeping channels updated documentation should have the answer you are looking for. It uses the users service but can be updated for pages accordingly which could look like this:
app.service('pages').on('created', page => {
// Assuming there is a reference of `users` on the page object
page.users.forEach(user => {
// Find all connections for this user
const { connections } = app.channel(app.channels).filter(connection =>
connection.user._id === user._id
);
app.channel('page-' + pageId).join(connections);
});
});
Using app.channel('page-' + pageId).join(connections) wasn't working for me. But looping through each connection works fine:
connections.forEach(connection => {
app.channel('page-' + userPage.pageId).join(connection)
})

KendoReact upload - passing file Id to React after saving file

I'm a bit stuck with the kendo react upload control.
I need to customise the rendering of the kendo react upload control.
After the user upload the file, I want to save the file in database. Then I need to pass the database Id back to the client because if the user then wants to remove the file I also need to clear the database.
To give you an idea this is what I would like to achieve.
https://stackblitz.com/edit/react-ghna5h
(When you start stackblitz open file app/main.jsx)
Is it possible?
Thanks for your help
I solved this particular issue using a combination of the onStatusChange and onBeforeRemove events. The server's 'save' endpoint returns an id (responseUID) that can be accessed through the onChange event's response object. I set this id on the 'file' object. Then when you're removing the file I pass the responseUID along in the additionalData field which gets put into the POST body.
const onStatusChange = (e) => {
if (e.response) {
const fileId = e.response.response.responseUID;
// This does not deal with multiple or batch uploads.
e.affectedFiles[0].responseUID = fileId;
}
};
const onBeforeRemove = (e) => {
e.additionalData.responseUID = e.files[0].responseUID;
};
return (
<Upload
batch={false}
defaultFiles={[]}
withCredentials={false}
saveUrl="https://localhost/upload/save"
removeUrl="https://localhost/upload/remove"
onStatusChange={onStatusChange}
onBeforeRemove={onBeforeRemove}
/>
);

Resources