I'm pretty new to SSE. Right now, I only know how to keep the stream open using a while(true) loop. How can I have my SSE stream only publish data when the underlying data changes? Below is the code I'm using.
const express = require('express');
run().catch(err => console.log(err));
async function run() {
const app = express();
app.get('/events', async function (req, res) {
console.log('Got /events');
res.set({
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive'
});
res.flushHeaders();
// Tell the client to retry every 10 seconds if connectivity is lost
res.write('retry: 10000\n\n');
let count = 0;
while (true) {
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Emit', ++count);
// Emit an SSE that contains the current 'count' as a string
res.write(`data: ${count}\n\n`);
}
});
await app.listen(3000);
console.log('Listening on port 3000');
}
EDIT:
I think I got what I was wanting. Modified the code to update clients when a new client connects to the stream:
const express = require('express');
run().catch(err => console.log(err));
async function run() {
const app = express();
let connections = 0
app.get('/events', async function (req, res) {
console.log('Got /events');
res.set({
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive'
});
res.flushHeaders();
connections += 1
let localConnections = connections
res.write(`data: ${connections}\n\n`);
while (true) {
await new Promise(resolve => setTimeout(resolve, 1000));
if (localConnections !== connections) {
localConnections = connections
console.log('Emit', connections);
// Emit an SSE that contains the current 'count' as a string
res.write(`data: ${connections}\n\n`);
}
}
});
await app.listen(3000);
console.log('Listening on port 3000');
}
Related
I made get request to Serp api, but learned that it is not possible to directly make fetch to Serp from React application, so I created local server and wrote logic for it, then I tested it with Postman and everything is fine. Then I had problems with CORS, I(guess) fixed them, but now I receive rejected promise on response and can't get rid of this. It gives
Unexpected end of input
Here is my server:
const SerpApi = require('google-search-results-nodejs');
const search = new SerpApi.GoogleSearch("myApiKey");
const express = require('express');
const app = express();
var allowCrossDomain = function (req, res, next) {
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}
app.get('/:id', function (req, res) {
console.log("Made request");
const searchText = req.params.id;
const params = {
q: searchText,
tbm: "shop",
};
const callback = function (data) {
const objects = [...data["shopping_results"]];
console.log("call back worked");
if (!objects) {
console.log("ERROR 404");
res.status(404).send();
}
res.status(200).send(objects);
};
// Show result as JSON
search.json(params, callback);
});
app.use(allowCrossDomain);
app.listen(3001, function () {
console.log("App is listening for queries");
})
and my fetch:
import updateOnSearchRequest from '../redux/actions/updateOnSearchRequest';
export default function searchRequestToApi(queryText, dispatch) {
fetch(`http://localhost:3001/${queryText}`, {
mode: 'no-cors',
})
.then(res => console.log(res.json()))
.then(data => data ? dispatch(updateOnSearchRequest(data)) : {});
}
I receive error at console.log(res.json()) even though server works fine and doesnt give any errors
First of all, you need to remove mode: "no-cors", as mentioned in this answer, using it will give you an opaque response, which doesn't seem to return data in the body.
Second, move your app.use(allowCrossDomain); to the top so it's higher than app.get("/:id", function (req, res) {....
And lastly, you must remove console.log from .then(res => console.log(res.json())).
In summary, your server should be:
const SerpApi = require("google-search-results-nodejs");
const search = new SerpApi.GoogleSearch("myApiKey");
const express = require("express");
const app = express();
var allowCrossDomain = function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
res.header("Access-Control-Allow-Headers", "Content-Type");
next();
};
app.use(allowCrossDomain);
app.get("/:id", function (req, res) {
console.log("Made request");
const searchText = req.params.id;
const params = {
q: searchText,
tbm: "shop",
};
const callback = function (data) {
const objects = [...data["shopping_results"]];
console.log("call back worked");
if (!objects) {
console.log("ERROR 404");
res.status(404).send();
}
res.status(200).send(objects);
};
// Show result as JSON
search.json(params, callback);
});
app.listen(3001, function () {
console.log("App is listening for queries");
});
And your fetch should be:
fetch(`http://localhost:3001/${queryText}`)
.then(res => res.json())
.then(data => data ? dispatch(updateOnSearchRequest(data)) : {});
i had a proplem with making two js files one to be put in 'website' directory and the other outside it and when i add a post request it adds a new item to the array from the server side js file and itried it alot and it didnt work so ..
thats My ServerSide Code
/* Empty JS object to act as endpoint for all routes */
projectData = {};
/* Express to run server and routes */
const express = require('express');
/* Start up an instance of app */
const app = express();
/* Dependencies */
const bodyParser = require('body-parser')
/* Middleware*/
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const cors = require('cors');
app.use(cors());
/* Initialize the main project folder*/
app.use(express.static('website'));
const port = 3000;
/* Spin up the server*/
const server = app.listen(port, listening);
function listening(){
// console.log(server);
console.log(`running on localhost: ${port}`);
};
// GET route
app.post('/add', function (req, res) {
let data = req.body;
console.log(data);
});
// POST an animal
const data = []
app.post('/animal', addAnimal)
function addAnimal (req,res){
data.push(req.body);
console.log(data);
}
and That Is My ClientSide Code
/* Function to POST data */
const postData = async ( url = '', data = {})=>{
console.log(data)
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data), // body data type must match "Content-Type" header
});
try {
const newData = await response.json();
console.log(newData);
return newData
}catch(error) {
console.log("error", error);
// appropriately handle the error
}
}
// TODO-Call Function
postData('/addAnimal', {animal:'Tiger'});
postData('/addAnimal', {animal:'Lion'});
when i run the code inside the vs code editor it displays "{ animal: 'lion' }
{ animal: 'Tiger' }"
But it never console log the data
you forget to send the respone
must the route callback have a res endpoint
function addAnimal (req,res){
data.push(req.body);
console.log(data);
// add res end point
res.json({msg : 'added'})
}
//here too
app.post('/add', function (req, res) {
let data = req.body;
console.log(data);
res.end('hello world')
});
Your route is /add or /animal not /addAnimal
postData('/add', {animal:'Tiger'});
in your ServerSide this function should display a log
app.post('/add', function (req, res) {
let data = req.body;
console.log(data);
});
You can't console.log the data in your try / catch because you don't return any response in your server side. But first try log data in your server side controller for confirm the good serverSide execution, and return your response.
I'm running a React/Node/MariaDB setup, where my Node acts as a getter and setter to my database depending on front end events. So I have React polling Node every 2 seconds to get a value called selectorPos or selPos in some cases. This value needs to be kept up to date with the database value, but also needs to be able to be changed by the GUI components. So I have a button press set to update the value in the database, then it would be read and updated upon the 2 second polling.
My solution works perfectly for a few button clicks. But seemingly randomly it will hang after a database write and nothing else works. Once I force stop the node server and restart, it rapid prints out a bunch of console.log statements I had set up for polling and writing, as if it had a backlog to catch up on, then it works again until another hang and reset.
I feel like there is something quite minor I am overlooking here. The code works, but it seems to get overloaded quite easily.
Here is my code:
React stuff:
componentDidMount() {
this.interval = setInterval(()=> this.getSelPos(), 2000);
}
componentWillUnmount() {
clearInterval(this.interval)
}
getSelPos() {
axios.get("http://localhost:5000/selector", { crossdomain: true }).then(response => {
console.log(response.data);
this.setState({SelectorPos: parseInt(response.data)});
console.log("Selpos is" + selPos);
});
}
setSelPos(i) {
axios.post("http://localhost:5000/selector", { position: i }).then((response) => {
console.log(response);
}, (error) => {
console.log(error);
});
}
handleClick(i){
this.setSelPos(i);
Node:
const express = require('express')
const db = require('./db')
const app = express()
const port = 5000
const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
// GET
app.get('/selector', async (req, res) => {
try {
const result = await db.pool.query("select value from db.tags where tag='1006'");
res.send(result[0].value.toString());
console.log(result[0].value.toString());
} catch (err) {
throw err;
}
});
// POST
app.post('/selector', async (req, res) => {
try {
const result = await db.pool.query("update db.tags set value = " + req.body.position.toString() + " where tag='1006'");
console.log(result);
} catch (err) {
throw err;
}
});
app.listen(port, () => console.log(`Listening on port ${port}`));
This code is not working proper since today, before today this is working well on response and all response.write() is execute on event-stream, but now problem is response.write() execute end of the api response. and all the event listed at a time.
const express = require('express');
const app = express();
app.use(express.static('public'));
app.get('/countdown', function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
'Access-Control-Allow-Origin': '*'
});
countdown(res, 1, 10);
});
function countdown(res, id, count) {
res.write(`id: ${id}\n`);
res.write('event: count\n');
res.write(`data: ${JSON.stringify({ count: count })}\n\n`);
if (count) setTimeout(() => countdown(res, id + 1, count - 1), 1000);
else res.end();
}
app.listen(3000, () => console.log('SSE app listening on port 3000!'));
And in your front page use EventSource :
<script>
var source = new EventSource('http://localhost:3000/countdown');
source.onmessage = function(event) {
console.log(event);
};
</script>
It sounds like this is a "periodically" issue. There is a limit to how many connections you can create, especially when/if the protocol is HTTP (see the warning box in the introduction of EventSource).
This can be mitigated by ensuring that you close the connection each time that you close/reload the webpage. From my experience the browser will eventually clean up the unused connections, but I have no idea when.
var source = new EventSource('http://localhost:3000/countdown');
window.addEventListener('beforeunload', e => {
// if the connection is not already closed, close it
if(source.readyState != source.CLOSED){
source.close();
}
});
I have this code in Ionic app but I don't know how to make an API with Node.js to send this values to sever only by using Node.js.
submitForm() {
let headers = new Headers(
{
'Content-Type': 'application/json',
'Accept': 'application/json'
});
let options = new RequestOptions({ headers: headers });
let data = JSON.stringify({
Value1: this.form.value1,
Value2: this.form.value2,
Value3: this.form.value3
});
console.log(data);
let url = 'http://localhost:3000/calculate';
console.log(url);
return new Promise((resolve, reject) => {
this.http.post(url, data, options)
.toPromise()
.then((response) => {
console.log('API Response : ', response.status);
resolve(response.json());
})
.catch((error) => {
console.error('API Error : ', error.status);
console.error('API Error : ', JSON.stringify(error));
reject(error.json());
});
});
}
You may like to use ExpressJS. Following example may help you
Create a directory lets called api with following 2 files
create app.js in api directory
var http = require('http');
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.post('/calculate', function(req, res) {
var data = req.body;
console.log('Here are your data: ', data);
res.json({message: 'you posted successfully'});
});
var port = process.env.PORT || 3000;
var server = http.createServer(app);
server.listen(port);
server.on('error', function(){
console.error('Error')
});
server.on('listening', function(){
console.log('server started on port ' + port)
});
create package.json file in api directory
{
"name": "api",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"body-parser": "~1.17.1",
"express": "~4.15.2"
}
}
now open command line/terminal and install dependencies by running following command(you must go to inside api directory)
npm install
now you can run by just running either npm start or node app.js
You should google for learning and studying and post questions for bug/issue
Update: without any dependencies or library but not recommended
It will be better to use http framework like express, sailsjs, etc. but if you like to play with nodejs then following example may help you
var http = require('http');
var port = process.env.PORT || 3000;
var server = http.createServer(function(req, res) {
var contentType = req.headers['content-type'];
var rawData = '';
req.on('data', function (chunk) {
rawData += chunk;
});
req.on('end', function () {
if(req.method === 'POST' && req.url === '/calculate' && contentType.indexOf('application/json')>-1){
try {
const data = JSON.parse(rawData);
console.log('Your data is here: ', data);
res.writeHead(200, { 'Content-Type': 'application/json'});
var result = {message: 'you have posted successfully'}
res.end(JSON.stringify(result));
} catch (e) {
console.error(e.message);
res.writeHead(400, { 'Content-Type': 'application/json'});
var result = {message: e.message}
res.end(JSON.stringify(result));
}
} else {
res.writeHead(404, { 'Content-Type': 'application/json'});
var result = {message: 'Url not found'}
res.end(JSON.stringify(result));
}
});
});
server.listen(port);
server.on('error', function(){
console.error('Error')
});
server.on('listening', function(){
console.log('server started on port ' + port)
});
I've created an example Node.js project that illustrates client/server request/response using AJAX and JSON. It "requires" only 'http', 'path', and 'fs'.
It implements a 'calculate' function server-side, and has a web page that presents 3 input boxes and a 'Calculate' button.
It's on Github: "https://github.com/bobwirka/NodeClientServer.git"
Hope this will be of help.