Puppeteer, ExpressJS, React SSR - reactjs

i am trying to achieve Server Side Rendering aka SSR for my react app. I created an express app with puppeteer and got endpoint with following snippets.
const port = 3003
app.get('*', async (req, res) => {
try {
const browser = await puppeteer.launch({headless: true});
const page = await browser.newPage();
const local_url = `http://localhost:${port}${req.originalUrl}`;
await page.goto(local_url, {
waitUntil: "networkidle0",
});
const html = await page.content()
await browser.close()
res.send(html);
} catch(e) {
console.log(e);
res.send("ERROR");
}
});
app.listen(process.env.PORT || port, () => {
console.log(`Web run ${port}`);
});
I just don't know why this is not working. Do I have to run my react app(run on 3000) separate and point local_url variable to there? like
const local_url = `http://localhost:3000${req.originalUrl}`;

You are in a loop where you are requesting same url over and over.
Your react app is in port 3000, and this express app is in port 3003.
You can make things a bit easy to read if you seperate them.
You have to run the react server on port 3000 and point to that otherwise it won't know where to browse.
const react_port = 3000;
const server_port = 3003;
const local_url = `http://localhost:${react_port}${req.originalUrl}`;
and vice varsa.

Related

React/Socket.io chat app not working on heroku

I have a chat app I made using React for the frontend, DRF for the backend and I also have a node.js server within the React app for socket.io The issue is that the chat doesn't work basically (it works fine locally). When a message is sent it's not emitted and only shows up when I refresh since it's then pulled from the DB instead. I have gone through many threads on here for this issue but can't figure out what I'm doing wrong.
My server:
const server = require("http").createServer();
const io = require("socket.io")(server, {
cors: {
origin: "*",
},
});
const PORT = process.env.PORT || 5000;
const NEW_CHAT_MESSAGE_EVENT = "newChatMessage";
io.on("connection", (socket) => {
console.log('Client connected')
// Join a conversation.
const {roomId} = socket.handshake.query;
socket.join(roomId);
// Listen for new messages
socket.on(NEW_CHAT_MESSAGE_EVENT, (data) => {
io.in(roomId).emit(NEW_CHAT_MESSAGE_EVENT, data);
});
// Leave the room if the user closes the socket
socket.on("disconnect", () => {
socket.leave(roomId);
});
});
server.listen(PORT, (error) => {
if (error) throw error;
console.log(`Listening on port ${PORT}`);
});
Hook I made for the frontend:
const NEW_CHAT_MESSAGE_EVENT = "newChatMessage"; // Name of the event
const SOCKET_SERVER_URL = `https://<my-react-frontend>.herokuapp.com`;
export const useChat = () => {
const socketRef = useRef();
const {messages, setMessages, activeConvo, headerConvo, reloadSideBar, setReloadSideBar} = useActiveConvo()
const roomId = activeConvo
useEffect(() => {
console.log('useChat useEffect ran')
// Creates a WebSocket connection
socketRef.current = socketIOClient(SOCKET_SERVER_URL, {
query: {roomId},
});
// Listens for incoming messages
socketRef.current.on(NEW_CHAT_MESSAGE_EVENT, (message) => {
const incomingMessage = {
message: message.body,
created_by: localStorage.getItem('currentUserID'),
};
console.log('messages set in useChat useFfect')
setMessages((messages) => [...messages, incomingMessage]);
});
// Destroys the socket reference
// when the connection is closed
return () => {
socketRef.current.disconnect();
};
}, [roomId]);
// Sends a message to the server that
// forwards it to all users in the same room
const sendMessage = (messageBody) => {
socketRef.current.emit(NEW_CHAT_MESSAGE_EVENT, {
body: messageBody,
senderId: socketRef.current.id,
});
const fetchContents = {
message: messageBody,
created_by: localStorage.getItem('currentUserID'),
convo_id: activeConvo ? activeConvo : headerConvo
}
fetch(`https://<my-drf-backend>.herokuapp.com/api/save-message/`, authRequestOptions(('POST'), fetchContents))
.then(response => response.json())
.then(setReloadSideBar(reloadSideBar + 1))
.catch(error => console.log(error))
};
return {messages, sendMessage};
};
The issue in most of the threads appeared to be either still using the localhost url on the frontend or not using process.env.PORT in the server but it's still busted after fixing that. I also saw someone mention in another thread that the folder structure was the issue so I tried having the server file in the root of the react app and having it in it's own folder under "src", no dice.
In case anyone faces this same issue, I solved it by putting the server in a separate app on heroku

unexpected token in json at position 0 reactjs mongodb

///component
function Home() {
const [show, setShow]= useState([{name:'', info:'', airingDate:'', poster:''}])
useEffect(()=>{
fetch("/home")
//.then(res=> res.json())
.then(res => res.text())
.then(text => console.log(text))
})
return (
<div>
{show.map(a=>
<div>
<h2>{a.title}</h2>
</div>
)}
</div>
)
/////index.js
const TvShows = require("./models/TvShows");
const express = require("express");
const app = express();
const mongoose= require("mongoose")
const dotenv= require("dotenv");
const authRoute = require("./routes/auth");
const { application } = require("express");
const userRoute = require("./routes/users");
const commentRoute = require("./routes/comments");
const tvshowsRoute = require("./routes/tvshows");
const cors = require("cors");
app.use(cors());
console.log(".");
dotenv.config();
app.use(express.json());
mongoose.connect(process.env.MONGO_URL,{
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true,
}).then(console.log("connected to mongoDB"));
app.use("/auth", authRoute);
app.use("/users", userRoute);
app.use("/comments", commentRoute);
app.post("/api/home", tvshowsRoute);
app.use("/api/home", tvshowsRoute);
/*
app.get('/api/home', (req,res)=>{
TvShows.find().then((result)=>{
res.send(result);
})
})
*/
/*
app.use("/",(req,res)=>{
console.log("main url")
})*/
app.listen("3001",()=>{
console.log("backend running");
})
//////route
const router = require("express").Router();
const TvShows = require("../models/TvShows");
router.post("/api/home", async (req, res) => {
console.log("here")
try{
const newTvShow = new TvShows({
title: req.body.title,
poster: req.body.poster,
info: req.body.info
});
const savedTvShows = await newTvShow.save();
res.status(200).json(savedTvShows);
}catch (err) {
res.status(500).json(err);
}
}
);
router.route("/api/home").get((req, res)=>{
TvShows.find()
.then(foundShows=> res.json(foundShows))
})
module.exports = router;
when I change res.json with res.text I see my index.html page on console not the data I want to fetch from mongodb. This error is probably because I didn't use /api/ on root url but I couldn't figure it out where I should write it. I tried but didn't work. It would be so good if someone could've helped. Thank you so much.
Indeed, you are fetching the /home page of your front-end app.
Assuming the api is on a different server, you would need to call the address of that server.
If you have a set up locally with a nodejs server and a react app running separately, you should have them run on two different ports.
If you have react app on http://localhost:3000 (default), then change your api to listen on 3001, then in your react code above, you can use the full uri
http://localhost:3001/api/home
in your fetch call.
I'm making a lot of assumptions about how you have this set up, based on my own experience of local development for similar problems.

Google Spreadsheet Api not working on mobile devices

I'm using Google Spreadsheets as a lightweight database for my React App. The app is a wedding website and the idea is for users to be able to RSVP and the response gets saved in a Google Spreadsheet. See GitHub for a visual representation of what I mean: https://github.com/michelleroos/mimirgettingmarried
It's working as intended on desktop, meaning RSVP responses get saved to the sheet, but not on mobile and I can't figure out why.
Would love your input! If I could even just understand where to start looking that would be great but there is not a lot of information out there on Google Spreadsheet Api.
const express = require("express");
const app = express();
const { google } = require('googleapis');
const PORT = process.env.PORT || 3001; // use live port or the declared one
var cors = require('cors');
app.use(cors())
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const auth = new google.auth.GoogleAuth({
keyFile: "./config/credentials.json",
scopes: 'https://www.googleapis.com/auth/spreadsheets',
});
const spreadsheetId = "1-R0vjBCOD5hcipk9AQbVUwQAH_0dFvQB2qs5gxsxWg0";
const range = "rsvp";
// api endpoint returns a CB. We decide what to do w res thru CB
app.get("/api/rsvp", async (req, res) => {
// create client instance for auth
const client = await auth.getClient();
// create Google sheets API instance
const googleSheets = google.sheets({ version: "v4", auth: client })
// read rows
const getRows = await googleSheets.spreadsheets.values.get({
auth,
spreadsheetId,
range,
});
// express/app sends to the client (the body)
res.send(getRows.data.values);
});
app.post("/api/rsvp", async (req, res) => {
// create client instance for auth
const client = await auth.getClient();
// create Google sheets API instance
const googleSheets = google.sheets({ version: "v4", auth: client })
const writeRows = await googleSheets.spreadsheets.values.append({
auth,
spreadsheetId,
range,
valueInputOption: "USER_ENTERED", // parses info
resource: {
values: [
// Object.keys(req.body), // header
Object.values(req.body) // values
]
}
})
res.send(writeRows);
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});

NextJs - React component methods get called twice per request

I'm experimenting with NextJs Custom Server using Express. And i noticed that on every request, getInitialProps gets called twice. When trying to find out what the problem is i stumbled upon React.Strictmode being the reason for that since it calls certain class methods and constructor twice to help debug. When setting it to false, the getInitialProps method gets called three times. NextJs "pages" folder routing is disabled.
To be mentioned that render() gets called twice or three times too.
My server.js :
require('dotenv').config();
const express = require('express');
const next = require('next');
const dev = process.env.NODE_ENV !== 'production';
const mongoose = require('mongoose');
const app = next({ dev });
mongoose.connect(process.env.DB_URI)
.then(()=>{
console.log('>Succesfully connected to the database');
app.prepare()
.then(() => {
const server = express();
server.listen(3000, (req, res) =>{
console.log('>Listening on port 3000');
});
server.get('*', (req, res)=>{
app.render(req, res, '/index', {something:true});
})
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
})
})
.catch(e=>{
console.log('<!>Error connecting to database: \n' + e);
});
and my GetInitialProps:
static async getInitialProps(ctx){
console.log({...ctx.query});
console.log('once');
return {props:{...ctx.query}};
}
Console when Strictmode on:
{ something: true }
once
{ something: true }
once
Console when Strictmode off:
{ something: true }
once
{ something: true }
once
{ something: true }
once

OAuth oidc getting access to user info on server side in SSR react app

I have SSR react app using redux-oidc I can successfuly log-in using IdentityServer4 as an authorization server with PKCE Authorization Code Flow.
I can get user data on client side of react application but I don't know how to do it on server side of same react app.
I want to show logged-in version of page on first load and for that I need to get user data during the first request.
const Koa = require('koa')
const next = require('next')
const Router = require('koa-router')
const jwt = require('koa-jwt')
const jwksRsa = require('jwks-rsa')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = new Koa()
const router = new Router()
server.use(jwt({
secret: jwksRsa.koaJwtSecret({
jwksUri: 'http://localhost:5000/.well-known/openid-configuration/jwks'
}),
issuer: 'http://localhost:5000',
algorithms: [ 'RS256' ],
passthrough: true
}));
router.get('*', async ctx => {
console.log(ctx.state)
console.log(ctx.header.cookie)
await handle(ctx.req, ctx.res)
ctx.respond = false
})
server.use(async (ctx, next) => {
ctx.res.statusCode = 200
await next()
})
server.use(router.routes())
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`)
})
})
Logicaly only data I can work with during first request are cookies I have just these three .AspNetCore.Identity.Application (id token), idsrv.session and CookieConsent.
How to use them to get user data? User info endpoint requires access token which I don't have because it is stored in sessionStorage on client.
There must be something I am missing.

Resources