I want to fetch some data using query parameters on the server for a page.
However, my query is empty inside getInitialProps when rendered through server. Why could this be happening?
Moreover, I have noticed this only happens in production server and not in the dev or prod env on my local.
Here's some code
import React from 'react';
import Config from 'component/Config';
import { withAuthSync } from 'util/auth';
import apiUrl from 'util/apiUrl';
function ResourceConfigPage({ data }) {
return <Config data={data} />;
}
ResourceConfigPage.getInitialProps = async ctx => {
const { resourceId } = ctx.query;
try {
const response = await fetch(`${apiUrl}/resource/config?resourceId=${resourceId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
const data = await response.json();
return { data };
}
}
return {};
};
export default withAuthSync(ResourceConfigPage);
My next app uses a custom express server and here's how it looks.
const express = require('express');
const next = require('next');
const compression = require('compression');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
const server = express();
app.setAssetPrefix(dev ? '' : 'https://example.com');
server.use(compression());
server.get('/healthcheck', (req, res) => {
res.status(200).json({});
});
server.get('/', (req, res) => {
if (req.cookies.sessionId) {
res.redirect('/dashboard');
}
res.redirect('/login');
});
server.get('*', (req, res) => {
return handle(req, res);
});
server.listen(3000, err => {
if (err) throw err;
console.log('> Ready on http://localhost:3000');
});
})
.catch(ex => {
console.error(ex.stack);
process.exit(1);
});
Related
I am using axios to make api calls using a proxy server with nodeJS as backend and reactJS as frontend.
node js file
const express = require('express');
const axios = require('axios').default;
const app = express();
require('dotenv').config();
const PORT = process.env.PORT || 8000;
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();
});
app.get('/', (req, res) => {
res.redirect('/api')
})
// get anime on front page
app.get(`/api`, async (req, res) => {
try {
const response = await axios.get('https://api.jikan.moe/v4/anime');
const data = response.data
res.json(data.data)
}
catch (err) {
console.log(err.message)
}
})
// get anime by id
app.get('/api/anime/:id' ,async (req,res)=>{
try {
const id = req.params.id
const response = await axios.get(`https://api.jikan.moe/v4/anime/${id}`)
const data = response.data
res.json(data);
console.log(data)
}
catch (err) {
console.log(err)
}
})
app.listen(PORT, () => {
console.log('connected')
})
Here I am using Two routes, one for the main homepage to show all the data, and then one route to retrieve data by ID, but when i try to retrieve the data using ID it shows null in console.
import useFetch from "./usefetch";
import { useParams } from "react-router-dom";
import './style/animeDetails.css'
const AnimeDetails = () => {
const play = (e) => {
console.log(e.target)
}
const {id} = useParams()
const { data: animeDet, isLoading, error } = useFetch('/api/anime/' + id);
console.log(animeDet.data)
return (
<div className="details">
</div>
);
}
export default AnimeDetails;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Here i am making a call to the proxy server to retrieve the data by ID.
try to check isLoading first, like:
const { data: animeDet, isLoading, error } = useFetch('/api/anime/' + id);
if (isLoading) {
return null
}
console.log(animeDet.data)
return (
<div className="details">
</div>
);
I'm using redis for the first time, and I can't quite figure out why my middleware 'cache' function is breaking my code? It works great without it, displays in the browser, if I go to my terminal and check for a key value pair it works great.
Here is my setup:
const express = require("express");
const redis = require("redis");
const axios = require("axios").default;
const PORT = process.env.PORT || 5000;
const REDIS_PORT = process.env.PORT || 6379;
const client = redis.createClient(REDIS_PORT);
client.connect();
const app = express();
function setResponse(username, repos) {
return `<h2>${username} has ${repos} Github repos</h2>`;
}
// make req to github
async function getRepos(req, res, next) {
try {
console.log("fetching data...");
const { username } = req.params;
const response = await axios.get(
`https://api.github.com/users/${username}`
);
const data = response.data;
const repos = data.public_repos;
// set to redis
client.set(username, repos);
res.send(setResponse(username, repos));
} catch (err) {
console.log(err);
res.status(500);
}
}
// Cache middleware
function cache(req, res, next) {
const { username } = req.params;
client.get(username, (err, data) => {
if (err) throw err;
if (data !== null) {
res.send(setResponse(username, data));
} else {
next();
}
});
}
app.get("/repos/:username", cache, getRepos);
app.listen(5000, () => {
console.log(`App listening on port ${PORT}`);
});
Any advice would be much appreciated!
Your cache function
function cache(req, res, next) {
const { username } = req.params;
client.get(username, (err, data) => {
if (err) throw err;
if (data !== null) {
res.send(setResponse(username, data));
} else {
next();
}
});
}
uses node redis 3 "logic" so it's hanging
redis 4 is promise led, so you need to use async/await or .then/.catch
So something like this should work for the .then/.catch approach
// Cache middleware
function cache(req, res, next) {
console.log('caching for', req.params);
const { username } = req.params;
client.get(username)
.then((data) => {
if (data !== null) {
res.send(setResponse(username, data));
} else {
next();
}
})
.catch(err => {
if (err) throw err;
});
}
This will solve your initial problem of "why it's getting stuck"
I need to make unit tests for some post requests but i dont understand how.I tried with mswjs but the test passes because i'm missing something and i dont know what.I tried to test the requests in an usual way but i wasnt able to put my conditions there and it was sending only 200 status code..
To start with,this is my folder structure:
+main folder
++nodeServer
+++public
+++routes
++public
++src
+++tests
This is my try for testing the post request to /subscribe endpoint,where i should send an email as a payload and get the response that the payload was received succesefully.
subscribeFetch.test.js:
import {setupServer} from 'msw/node'
import {rest} from 'msw'
const handlers = [
rest.post("/api/subscribe",(req,res,context)=>{
if (!req.body || !req.body.email) {
return res(context.status(400).json({ error: "Wrong payload" }));
}
if (req.body.email === 'forbidden#email.com') {
return res(context.status(422).json({ error: "Email is already in use" }));
}
return res(
context.status(200),
context.json({email:'gigi#gmail.com'})
)
})
]
const server = setupServer(...handlers)
beforeAll(()=>server.listen())
afterAll(()=>server.close())
afterEach(()=>server.resetHandlers())
test('should send post request to the server',async()=>{
server.use(
rest.post('/api/subscribe',(req,res,ctx)=>{
return res(
expect (ctx.status()).toBe(200)
)
}
)
)
})
//export {handlers,rest}
This is the subscribe post request function that i need to test:
import { validateEmail } from './email-validator.js'
export const sendSubscribe = (emailInput) => {
const isValidEmail = validateEmail(emailInput)
if (isValidEmail === true) {
sendData(emailInput)
}
}
export const sendHttpRequest = (method, url, data) => {
return fetch(url, {
method: method,
body: JSON.stringify(data),
headers: data
? {
'Content-Type': 'application/json'
}
: {}
}).then(response => {
if (response.status >= 400) {
return response.json().then(errResData => {
const error = new Error('Something went wrong!')
error.data = errResData
throw error
})
}
return response.json()
})
}
const sendData = (emailInput) => {
sendHttpRequest('POST', '/api/subscribe', {
email: emailInput
}).then(responseData => {
return responseData
}).catch(err => {
console.log(err, err.data)
window.alert(err.data.error)
})
}
Files from the server:
app.js:
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const indexRouter = require('./routes/index');
const communityRouter = require('./routes/community');
const analyticsRouter = require('./routes/analytics');
const app = express();
global.appRoot = path.resolve(__dirname);
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/community', communityRouter);
app.use('/analytics', analyticsRouter);
module.exports = app;
index.js from routes folder in the server folder:
const express = require('express');
const router = express.Router();
const FileStorage = require('../services/FileStorage');
/* POST /subscribe */
router.post('/subscribe', async function (req, res) {
try {
if (!req.body || !req.body.email) {
return res.status(400).json({ error: "Wrong payload" });
}
if (req.body.email === 'forbidden#email.com') {
return res.status(422).json({ error: "Email is already in use" });
}
const data = {email: req.body.email};
await FileStorage.writeFile('user.json', data);
await res.json({success: true})
} catch (e) {
console.log(e);
res.status(500).send('Internal error');
}
});
/* GET /unsubscribe */
router.post('/unsubscribe', async function (req, res) {
try {
await FileStorage.deleteFile('user.json');
await FileStorage.writeFile('user-analytics.json', []);
await FileStorage.writeFile('performance-analytics.json', []);
await res.json({success: true})
} catch (e) {
console.log(e);
res.status(500).send('Internal error');
}
});
module.exports = router;
Please guys,help me write unit test for subscribe endpoint to match the conditions from index.js file from routes folder in the server folder,thank you in advance!
So,i got the expected result without any library,but i dont know if its a good aproach,but at least it works :
const app = require('../../../personal-website-server/app')
const request = require('supertest')
describe('POST /subscribe', () => {
it('should give 400 status code when email is empty', async () => {
const email = { email: '' }
const response = await request(app).post('/subscribe').send(email)
if (!request.body || !request.body.email) {
expect(response.status).toBe(400)
}
})
it('should give 422 status code when email is forbidden', async () => {
const email = { email: 'forbidden#gmail.com' }
const response = await request(app).post('/subscribe').send(email)
if (request.body === 'forbidden#gmail.com') {
expect(response.status).toBe(422)
}
})
it('should give 200 status code when email is valid', async () => {
const email = { email: 'gigi#gmail.com' }
const response = await request(app).post('/subscribe').send(email)
expect(response.error).toBe(false)
expect(response.status).toBe(200)
expect(response.body.body).not.toBeNull()
})
})
I try to fetch an API and save the data to MongoDB, but i think i have some problem with my POST method..
I would like to store the data what i fetch from the API and then if i change data in the front i would like if it changed in the database
Here is my code snippet:
App.js file
class App extends React.Component {
state = {
name: "",
movie: [],
};
componentDidMount = () => {
this.getMovie();
this.displayMovie();
};
getMovie = () => {
axios.get("http://api.tvmaze.com/shows?page=1").then((response) => {
const data = response.data;
this.setState({
movie: data,
});
console.log("Data has been received");
console.log(this.state.movie);
});
};
displayMovie = () => {
axios({
url: "/",
method: "POST",
data: this.state.movie,
})
.then(() => {
console.log("Data has been sent to the server");
this.getMovie();
})
.catch((err) => {
console.log(err);
console.log("Internal server error");
});
};
render() {
return (
<div>
<form onSubmit={this.displayMovie}>
<button type="submit">Send</button>
<h2>Movie List</h2>
<div>
{this.state.movie.map((t) => {
return <p>{t.name}</p>;
})}
</div>
</form>
</div>
);
}
}
export default App;
server.js file
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const app = express();
const movieRouter = require("./routes/movie.routes");
const connect = () => {
return mongoose
.connect("mongodb://localhost:27017/movie", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log("db connected"))
.catch((err) => console.log(err));
};
connect();
app.use(express.json());
app.use("/", movieRouter);
app.use(bodyParser.json({ limit: "50mb", extended: true }));
app.use(bodyParser.urlencoded({ limit: "50mb", extended: true }));
app.listen(5000, () => {
console.log("app is running on 5000");
});
model Schema
const mongoose = require("mongoose");
const movieSchema = new mongoose.Schema({
name: {
type: String,
},
});
const MovieSchema = mongoose.model("movieSchema", movieSchema);
module.exports = MovieSchema;
service js file
const MovieSchema = require("../models/movie.models");
const getMovie = async () => {
const movie = await MovieSchema.find();
return movie;
};
module.exports = {
getMovie,
};
controller file
const getMovie = async (req, res, next) => {
try {
const movie = await movieService.getMovie();
res.status(200).json({ result: movie });
} catch (err) {
next(err);
}
};
module.exports = {
getMovie,
};
router file
const express = require("express");
const router = express.Router();
const movieController = require("../controllers/movie.controllers");
router.get("/", movieController.getMovie);
module.exports = router;
What am i doing wrong ? I have POST http://localhost:3000/ 404 (Not Found) error message.
Thank you for help!
First of all your POST route handler is missing (ref: Andrew Nolan).
Secondly your react code App.js componentDidMount method is calling getMovie and straight after calling displayMovie. It has to wait till getMovie method returns the results. So i placed promises to solve it.
class App extends React.Component {
state = {
name: "",
movie: [],
};
async componentDidMount() {
try {
await this.getMovie();
this.displayMovie();
} catch (e) {
console.log(e);
}
};
getMovie = async () => {
return new Promise((resolve, reject) => {
axios.get("http://api.tvmaze.com/shows?page=1").then(({ data }) => {
this.setState({
movie: data,
});
resolve();
}).catch(e => reject(e));
});
};
displayMovie = async () => {
axios({
url: "/",
method: "POST",
data: this.state.movie,
})
.then(() => this.fetchMovie())
.catch((err) => console.log(err));
};
render() {
return (
<div>
<form onSubmit={this.displayMovie}>
<button type="submit">Send</button>
<h2>Movie List</h2>
<div>
{this.state.movie.map((t) => {
return <p>{t.name}</p>;
})}
</div>
</form>
</div>
);
}
}
export default App;
You don't need to send status code 200. json method will stringify the json object and attach 200 status code as default.
res.status(200).json({ result: movie }); // too long
res.json({ result: movie }); // better practice
res.json({ movie }); // even better
When you are creating mongoose schema, you don't need to explicitly declare type of the data in object literals unless you want to add other configurations.
const movieSchema = new mongoose.Schema({
name: {
type: String,
},
});
const movieSchema = new mongoose.Schema({
name: String // better
});
In your code, you have only specified a GET route for /. You also need to specify a POST route too.
const express = require("express");
const router = express.Router();
const movieController = require("../controllers/movie.controllers");
router.get("/", movieController.getMovie);
router.post("/", movieController.saveMovie);
module.exports = router;
I put in movieController.saveMovie as a placeholder for whatever gets invoked at this route. That part is up to you. I didn't see a method for it in your code snippets to save it.
You are also missing post controller
just like get, add post controller in controller file too.
const postMovie = async (req, res, next) => {
const {name} = req.body;
try {
const newMovie = new Movie({
name,
});
const movie = await newMovie.save();
res.status(200).json({ result: movie });
} catch (err) {
next(err);
}
};
module.exports = {
postMovie,
};
My /chat route works well through Post method with validation with Joi schema but when I send request through Get method, it show Sending Request and continue loading...
My index.js file:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const chat = require('./db/ChatModel');
const app = express();
app.use(bodyParser.json());
app.get('/chat', (req, res) => {
chat.getAllMessages().then( (messages) => {
res.json(messages);
});
});
app.post('/chat', (req, res) => {
console.log(req.dody);
chat.createMessages(req.body).then((message) => {
res.json(message);
}).catch( (error) => {
res.status(500);
res.json(error);
});
});
const port = process.env.PORT || 8888;
app.listen(port, () => {
console.log(`Listening on port ${port}...`);
});
In connection.js I coded this
const monk = require('monk');
const connectionString = 'localhost/chatboard';
const db = monk(connectionString);
module.exports = db;
And ChatModal.js has the following code
const Joi = require('joi');
const db = require('./connection');
const schema = Joi.object().keys({
username: Joi.string().alphanum().min(4).max(16).required(),
subject: Joi.string().required(),
message:Joi.string().max(300).required(),
imgUrl: Joi.string().uri({
scheme: [ // https://github.com/hapijs/joi/blob/v14.3.1/API.md#stringurioptions
/https?/
]
})
});
const chat = db.get('chat');
function getAllMessages() {
return chat.find();
};
function createMessages(message) {
const result = Joi.validate(message, schema);
if (result.error == null) {
message.created = new Date();
return chat.insert(message);
} else {
return Promise.reject(result.error);
}
}
module.exports = {
createMessages,
getAllMessages
};
I can't understand why getAllMessages() doesn't work and postman continue loading when Get request applied like this http://prntscr.com/s0d9c5
ChatModal.js
function getAllMessages() {
try {
return chat.find();
} catch (err) {
return next(err);
}
index.js
app.get('/chat', (req, res, next) => {
try{
data = chat.getAllMessages()
} catch (err) {
return next(error);
}
res.json(data);
});
User try-catch in the ChatModal.js and also index.js then you can understand what is actual error, like bellow:
ChatModal.js
function getAllMessages() {
try {
chat.find();
} catch (err) {
return next(err);
}
I think, may be your data, i mean message list data so weight, in this case you get all message,res.json(messages); json method have long time to parse messages data