Get the Message notification from Nodejs REST API using Angular8 in background - node.js

I am working on an application where I want to implement the message Inbox. I have created the message inbox using Angular8 and NodeJS REST API. Now I want to get the on inbox message in every 30 Second on the background when user login also it doesn't want to affecting the performance of the Angular app.
So I want to Implement the Web-worker with Angular8 to get the Data from NodeJS REST API but I am unable to create.
I have added following code in Angular 8 App
Add this code to app.component.ts
getWorker(token){
if (typeof Worker !== 'undefined') {
// Create a new
const worker = new Worker('../../web-worker/messenger.worker', { type: `module` });
worker.postMessage(token);
worker.onmessage = (e) => {
setTimeout(() => {
worker.postMessage(token)
}, 15000);
};
} else {
// Web Workers are not supported in this environment.
// You should add a fallback so that your program still executes correctly.
}
}
Created worker file with fetch
/// <reference lib="webworker" />
addEventListener('message', ({ data }) => {
const response = `worker response to ${data}`;
postMessage(response);
});
import { environment } from "src/environments/environment";
onmessage = (message:any) => {
fetch(environment.apiUrl +'messages/notification/1/1',
{ method:'GET',
headers:new Headers({
Authorization: `Bearer ${message.data}`
})
}
)
.then(response => {
return response.json()
})
.then(commits => {
// self.onmessage(message)
return commits
});
};
but it shows the type as fetch is should be show web worker Right?
Can anyone help me with this?

Related

How to properly handle errors on subscriptions with Apollo Server?

I have an Express + Apollo Server backend. I enabled subscriptions on it using ws and graphql-ws. Everything is working fine.
Now, I would like to handle resolvers errors properly: hide backend details in production, change message based on error type, add a unique ID, etc. On regular mutations, I'm able to do so using the formatResponse function.
On subscriptions, I can't find where I could do it. All I need is a function called before sending data to the client where I have access to data and errors.
How can I do that?
Here's how the WS Server is created:
// Create Web Socket Server
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql'
});
const serverCleanup = graphqlWS.useServer(
{
schema: graphqlApp.schema,
context: async (ctx: any) => {
try {
// ...Some auth checking...
return context;
} catch (e) {
throw new ApolloAuthenticationError('you must be logged in');
}
}
},
wsServer
);
And an example of event sending:
import {PubSub} from 'graphql-subscriptions';
// ...
Subscription: {
tree: {
subscribe: withFilter(
() => pubsub.asyncIterator('some_id'),
(payload, variables) => {
const canReturn = true;
//...Some filtering logic...
return canReturn;
}
)
}
},

Cloud Run Readablestream takes 5 minutes to cancel enqueue

I created an adapter-node Sveltekit API endpoint, which streams quotes using a readable stream. When I quit the client route The streaming has to stop. This works fine in development using Sveltekit "npm run dev" (vite dev) or using a windows desktop container (node build).
onDestroy(async () => {
await reader.cancel(); // stop streaming
controller.abort(); // signal fetch abort
});
But when I build and deploy the node container on Google Cloud Run the streaming works fine. Except when I quit the client route: the API endpoint keeps on streaming. The log shows: enqueus for 5 more minutes followed by a delayed Readablestream cancel() on the API server.
Why this 5 minutes between the client cancel / abort and the cancel on the server?
The API +server.js
import { YahooFinanceTicker } from "yahoo-finance-ticker";
/** #type {import('./$types').RequestHandler} */
export async function POST({ request }) {
const { logging, symbols } = await request.json();
const controller = new AbortController();
const ticker = new YahooFinanceTicker();
ticker.setLogging(logging);
if (logging) console.log("api ticker", symbols);
const stream = new ReadableStream({
start(controller) {
(async () => {
const tickerListener = await ticker.subscribe(symbols);
tickerListener.on("ticker", (quote) => {
if (logging) console.log("api", JSON.stringify(quote, ["id", "price", "changePercent"]));
controller.enqueue(JSON.stringify(quote, ["id", "price", "changePercent"]));
});
})().catch(err => console.error(`api listen exeption: ${err}`));
},
cancel() { // arrives after 5 minutes !!!
console.log("api", "cancel: unsubscribe ticker and abort");
ticker.unsubscribe();
controller.abort();
},
});
return new Response(stream, {
headers: {
'content-type': 'text/event-stream',
}
});
}
Route +page.svelte
const controller = new AbortController();
let reader = null;
const signal = controller.signal;
async function streaming(params) {
try {
const response = await fetch("/api/yahoo-finance-ticker", {
method: "POST",
body: JSON.stringify(params),
headers: {
"content-type": "application/json",
},
signal: signal,
});
const stream = response.body.pipeThrough(new TextDecoderStream("utf-8"));
reader = stream.getReader();
while (true) {
const { value, done } = await reader.read();
if (logging) console.log("resp", done, value);
if (done) break;
... and more to get the quotes
}
} catch (err) {
if (!["AbortError"].includes(err.name)) throw err;
}
}
...
The behavior you are observing is expected, Cloud Run does not support client-side disconnects yet.
It is mentioned in this article, that
Cloud Run (fully managed) currently only supports server-side
streaming. Having only "server-side streaming" basically means when
the "client" disconnects, "server" will not know about it and will
carry on with the request. This happens because "server" is not
connected directly to the "client" and the request from the "client"
is buffered (in its entirety) and then sent to the "server".
You can also check this similar thread
It is a known issue, there is already a public issue exists for the same. You can follow that issue for future updates and also add your concerns there.

Remix js localstorage alternative for server side

I have a remix application to act like frontend.
I load data from my backend and for some data I need to load it only once and reuse it on different pages.
In previous frontend we used localstorage but here is server side which returns me ReferenceError: window is not defined
import {LoaderFunction} from "#remix-run/node";
import authenticator from "~/services/auth.server";
import Layout from "~/src/Layout";
import {fetchData} from "~/services/fetch.service";
export let loader: LoaderFunction = async ({request}) => {
const user = await authenticator.isAuthenticated(request, {failureRedirect: "/login",});
const configs = await fetchData('GET', request, 'api/configs/all')
.then((response) => {
return response;
})
.catch(async error => {
await authenticator.logout(request, {redirectTo: "/login"});
});
try {
localStorage.setItem('parameters', configs);
} catch (e) {
console.log(e);
}
return {
user: user,
request: request
};
};
export default function DashboardPage() {
const data = useLoaderData();
return (
<Layout user={data?.user} request={data.request}>
</Layout>
);
}
I need the config to be accessable at any time, it's not usefull if I need to load it all the time.
You can't use localStorage on the server-side, since it is first set on the client. You could use cookies since they are accessible on the server-side.

What is the process for sending a axios POST method using reactjs to a node path?

I am trying to send a POST request using axios to the backend but it is throwing a 404 for the path and i dont know why
Here is the react/redux code calling the axios request
export const addGoal = (newGoal: Goal) => {
return (dispatch: any) => {
authMiddleWare(history)
const newValues = newGoal
const authToken = localStorage.getItem('AuthToken')
axios.defaults.headers.common = { Authorization: `${authToken}` }
axios
.post('/goal', newValues)
.then((response) => {
console.log('success', response.data)
dispatch({
type: ADD_GOAL,
payload: response.data,
})
})
.catch((err) => {
console.error('\nCould not submit goal\n', err.response)
})
}
}
This is the nodejs path i have in my main backend file for calling the paths
app.post("/goal", auth, postOneGoal);
This is the backend function for the node path
// ADDS A SINGLE WORKOUT
exports.postOneGoal = (request, response) => {
if (request.body.id.trim() === "" || request.body.text.trim() === "") {
return response.status(400).json({ body: "Must not be empty" });
}
const newGoalItem = {
username: request.user.username,
id: request.body.id,
text: request.body.text
};
db.collection("goals")
.add(newGoalItem)
.then((doc) => {
const responseNewGoalItem = newGoalItem;
responseNewGoalItem.id = doc.id;
doc.update(responseNewGoalItem);
return response.json(responseNewGoalItem);
})
.catch((err) => {
response.status(500).json({ error: "Couldn't add the goal" });
console.error(err);
});
};
I am using a firebase url proxy in my package.json as well.
Let me know if any more info is needed
Posting this as Community Wiki, based in the comments.
Considering the fact that you are using Cloud Functions, you will need to redeploy the functions everytime you update your code. You can check more details on deploying your functions in the official documentation accessible here. There you will have the options regarding how and where you can deploy your functions for better testing.

How to deal with backend dynamically allocated ports on react.js side using axios?

This morning I deployed a MERN stack login app in heroku successfully. But, when I tried to login
GET http://localhost:5000/user/login/email/password net::ERR_CONNECTION_REFUSED
in the console.
I understood that that the error is because I am making get request in axios using
axios.get("http://localhost:5000/user/login/" + this.state.email + "/" + this.state.password).then((res) => {
if (res.status === 200) {
this.setState({ status: res.status, name: res.data.name });
console.log(res.data);
}
else
throw new Error(res.status);
}).catch((err) => {
this.setState({ isInvalid: true });
})
But, the port is being dynamically allocated on the server side.
const port = process.env.PORT||5000;
app.listen(port, () => {
console.log("Server started on port:" + port);
});
Tried allocating only hardcoded value to the port. Still no luck
There are lots of mistakes in your code. You have deployed your app but your URL is still localhost which is not Heroku URL. First of all you need to setup env variables for your application like this.
You can put this in some constant file from where you get your end point. Don't write END POINTS directly in the ajax calls. Use constant and create a single file for from where you do all the ajax calls of the application.
You can set the env for both frontend and backend and this is how you should work. The development env should be separate from production one.
if (process.env.NODE_ENV === "development") {
API = "http://localhost:8000";
} else if (process.env.NODE_ENV === "production") {
API = "https://be-prepared-app-bk.herokuapp.com";
}
Don't use GET for the login and sending email and password in parameters. You should use POST and send all the data in body.
Here's how you single ajax file should look alike:
import { API_HOST } from "./constants";
import * as auth from "../services/Session";
const GlobalAPISvc = (endPoint, method, data) => {
const token = auth.getItem("token");
const uuid = auth.getItem("uuid");
return new Promise((resolve, reject) => {
fetch(`${API_HOST}${endPoint}`, {
method: method,
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
"x-authentication": token,
uuid: uuid
}
})
.then(res => {
return res.json();
})
.then(json => {
resolve(json);
})
.catch(error => {
reject(error);
});
}).catch(error => {
return error;
});
};
export default GlobalAPISvc;
I have created an application in MERN which I made public on GitHub. Feel free to take help from that. Repository Link
Firstly, I would suggest you, not to use get request method for login.
Secondly, if you've deployed your backend code then use dynamic url provided by heroku for login request.
e.g. if your url is xyz.heroku.com then axios.get('xyz.heroku.com/user/login/'+email+'/'+password);
as now you don't need to hard-code the port or use localhost.

Resources