Fetch API - How Do I Send Query From Front End to Backend? - node.js

I need to pass a search query from my front end (in React) to my back end (Express) so that my Twitter API route will grab the correct data. Here is where I'm hitting the Twitter API. I was just playing around with a req.query to check out JSON so I know that part needs to be removed.
tweets.js
var express = require('express');
var router = express.Router();
const bodyParser = require('body-parser');
const Twit = require('twit');
const config = require('./config');
var T = new Twit(config);
/* GET users listing. */
router.get('/', function(req, res, next) {
let ticker = req.query.ticker;
T.get('search/tweets', { q: ticker })
.then(function(result) {
var tweets = result.data;
console.log(tweets);
res.send({tweets});
})
.catch(function(err) {
console.log('caught error', err.stack)
res.send({ error: err })
})
})
module.exports = router;
Also note route is set up like this in express
app.use('/tweets', tweets);
And here is my front end in React (ignoring the actual search component for now). Just confused as to how I would send a search query
import React, { Component } from 'react';
import '../App.css';
const filterData = (tweet) => {
return ((!tweet.retweeted) && !(tweet.text.includes('RT #') && ((tweet.in_reply_to_status_id) === null)));
};
class Twitter extends Component {
constructor(props) {
super(props);
this.state = {
tweets:[],
}
}
componentDidMount() {
this.getData("GOOG");
}
getData = (query) => {
fetch('/tweets?ticker='+query)
.then(res => res.json())
.then(data => data.statuses)
.then(statuses => statuses.filter(filterData))
.then(results => this.setState({tweets:results}))
}
render() {
return (
<div className="App">
<h1>Tweets About </h1>
{this.state.tweets.map(tweet =>
<div key={tweet.id}>{tweet.text}</div>
)}
</div>
);
}
}
export default Twitter;

The issue is here in this line.
Wrong one
fetch('/tweets?ticker={query)') <-- this is wrong
Correct one
const filterData = (tweet) => {
return ((!tweet.retweeted) && !(tweet.text.includes('RT #') && ((tweet.in_reply_to_status_id) === null)));
};
class Twitter extends Component {
constructor(props) {
super(props);
this.state = {
tweets:[],
}
}
componentDidMount() {
this.getData("GOOG");
}
getData = (query) => {
fetch(`http://localhost:3001/tweets?ticker=${query}`, {method: 'GET', headers: {"Content-Type": "application/json", "Access-Control-Allow-Origin": "*"}})
.then(res => res.json())
.then(data => data.statuses)
.then(statuses => statuses.filter(filterData))
.then(results => this.setState({tweets:results}))
}
renderTweets(){
if(this.state.tweets){
this.state.tweets.map(tweet =>
<div key={tweet.id}>{tweet.text}</div>
)
}
}
render() {
return (
<div className="App">
<h1>Tweets About </h1>
{this.state.tweets ? this.renderTweets(): ''}
</div>
);
}
}
export default Twitter;
Use template literals
fetch(`/tweets?ticker=${query}`)
OR
use normal string
fetch('/tweets?ticker='+query)
Check here for more details on how template literals works. Your code should be fine now.

Related

Delete request not working in React+Express+MongoDB app

im working through fullstackopen course along TOP, every excercise went well so I drifted off the course to build simple todo app to solidify the knowledge i gained so far. So i developed front end with react, then the back end with node express connected to mongoDB. All seemed fine but then the delete request stopped working - every other request works fine, only the delete causes errors. After requesting a delete the page crashes, BUT in the database the request is fulfilled and the note is removed. So when I reconnect to the node server and refresh the page, the content is up to date and everything seems to work again.
RESTclient is saying that delete request works fine. But in the browser, when i click delete button, after like a second the app crashes and this is shown in the console:
Notes.js:20 Uncaught TypeError: Cannot read properties of null (reading 'id')
at Notes.js:20:27
at Array.map (<anonymous>)
at b (Notes.js:19:16)
at xo (react-dom.production.min.js:167:137)
at Pi (react-dom.production.min.js:197:258)
at Eu (react-dom.production.min.js:292:88)
at bs (react-dom.production.min.js:280:389)
at gs (react-dom.production.min.js:280:320)
at vs (react-dom.production.min.js:280:180)
at ls (react-dom.production.min.js:271:88)
server.js:
require("dotenv").config();
const express = require("express");
const morgan = require("morgan");
const cors = require("cors");
const mongoose = require("mongoose");
const Note = require("./models/note");
const app = express();
app.use(express.static("build"));
app.use(express.json());
app.set("json spaces", 2);
app.use(cors());
app.use(morgan("tiny"));
/// DEFINE DEFAULT PORT //
const PORT = process.env.PORT;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
/// DEFINE BASIC ROUTES
app.get("/notes", (request, response, next) => {
Note.find({}).then((notes) => response.json(notes));
});
app.get("/notes/:id", (request, response, next) => {
Note.findById(request.params.id)
.then((note) => {
if (note) {
response.json(note);
} else {
response.status(404).end();
}
})
.catch((error) => next(error));
});
/// DELETE ///
app.delete("/notes/:id", (request, response, next) => {
Note.findByIdAndRemove(request.params.id)
.then((response) => response.status(204).end())
.catch((error) => next(error));
});
/// UPDATE ///
app.put("/notes/:id", (request, response, next) => {
const {content, done} = request.body
Note.findByIdAndUpdate(
request.params.id,
{content, done},
{new: true, runValidators: true, context: "query"},
)
.then(updatedNote => response.json(updatedNote))
.catch(error => next(error))
})
/// ADD ///
app.post("/notes", (request, response, next) => {
const body = request.body;
if (!body.content) {
return response.status(400).json({
error: "content missing",
});
}
const note = new Note({
content: body.content,
done: false,
});
note
.save()
.then((saved) => response.json(saved))
.catch((error) => next(error));
});
/// HANDLE UNDEFINED ROUTES ///
const unknownEndpoint = (request, response) => {
response.status(404).send({ error: "unknown endpoint" });
};
app.use(unknownEndpoint);
/// HANDLE ERRORS ///
const errorHandler = (error, request, response, next) => {
console.error(error.message);
if (error.name === "CastError") {
return response.status(400).send({ error: "malformatted id" });
} else if (error.name === "ValidationError") {
return response.status(400).json({ error: error.message });
}
next(error);
};
app.use(errorHandler);
front-end,
app.js:
import { useState, useEffect } from "react";
import css from "./App.css"
import Button from "./Button";
import Input from "./Input";
import noteService from "./services/NoteService";
import Notes from "./Notes";
function App() {
const [notes, setNotes] = useState([]);
const [newNote, setNewNote] = useState("");
useEffect(() => {
noteService.getAll().then((response) => {
setNotes(response);
});
}, []);
const handleInput = (event) => {
const content = event.target.value;
setNewNote(content);
};
const handleSubmit = (event) => {
event.preventDefault();
const note = { content: newNote, done: false };
noteService
.create(note)
.then((response) => setNotes(notes.concat(response)));
setNewNote("");
};
const handleDelete = (id) => {
noteService
.trash(id)
.then(setNotes(notes.filter((note) => note.id !== id)));
};
const toggleStatus = (id) => {
const note = notes.find((item) => item.id === id);
const updated = { ...note, done: !note.done };
noteService.update(id, updated).then((response) => {
setNotes(notes.map((note) => (note.id !== id ? note : response)));
});
};
const showDone = () => {
noteService.getAll().then((response) => {
setNotes(response.filter((note) => note.done));
});
};
const showUndone = () => {
noteService.getAll().then((response) => {
setNotes(response.filter((note) => !note.done));
});
};
const showAll = () => {
noteService.getAll().then((response) => {
setNotes(response);
});
};
return (
<div className="container">
<h1>TO_DO NOTES</h1>
<div className="header">
<Input action={handleInput} value={newNote} />
<Button text={"Add"} action={handleSubmit} />
</div>
<div>
<Button text="Show All" action={showAll} />
<Button text="Show Done" action={showDone} />
<Button text="Show Undone" action={showUndone} />
</div>
<Notes notes={notes} action={handleDelete} toggle={toggleStatus}/>
</div>
);
}
export default App;
Notes.js:
import Button from "./Button";
import css from "./Notes.css";
const Note = ({ item, action, toggle }) => {
return (
<li
onClick={() => toggle(item.id)}
className={item.done ? "done" : "undone"}
>
{item.content} <Button text="x" action={() => action(item.id)} />
</li>
);
};
const Notes = ({ notes, action, toggle }) => {
return (
<>
<ul>
{notes.map((item) => (
<Note key={item.id} item={item} action={action} toggle={toggle} />
))}
</ul>
</>
);
};
export default Notes;
NoteService.js:
import axios from "axios";
const baseUrl = "/notes";
const getAll = () => {
const request = axios.get(baseUrl);
return request.then((response) => response.data);
};
const create = (newObject) => {
const request = axios.post(baseUrl, newObject);
return request.then((response) => response.data);
};
const update = (id, newObject) => {
const request = axios.put(`${baseUrl}/${id}`, newObject);
return request.then((response) => response.data);
};
const trash = id => {
const request = axios.delete(`${baseUrl}/${id}`)
return request.then(result => result.data)
}
export default {
getAll,
create,
update,
trash,
};
I would really appreciate some help. I compared this project with the other one i have thats structured the same, the other one is working but here cannot figure out what is wrong.
In the Notes.js file within the Notes component, where you are iterating using notes.map, change it to notes?.map and see if that works.

CURRENT_USER null, TypeError: Cannot read properties of null

I am facing an issue my user is always showing null in the backend, I don't know why.
I am following an Udemy course my tutor is saying middleware is responsible for user identification. but the problem is I typed the same code as my tutor is typing but his codes are working fine mine not.
I am using Axios from the front end to make request.
This my controller ==>
export const currentUser = async (req, res) => {
try {
const user = await User.findById(req._id).select("-password").exec();
console.log("CURRENT_USER", user); return res.json({ ok: true });
} catch (err) {
console.log(err);
}
};
This is my middleware ==>
import { expressjwt } from "express-jwt";
export const requireSignIn = expressjwt({ getToken: (req, res) => req.cookies.token, secret: process.env.JWT_SECRET, algorithms: ["HS256"], }) ;
This my front end code where I have been making request ===>
import { useEffect, useState, useContext } from "react";
import axios from "axios";
import { useRouter } from "next/router";
import { SyncOutlined } from "#ant-design/icons";
import UserNav from "../nav/userNav";
import { Context } from "../../context";
const UserRoutes = ({ children }) => {
const { state: { user } } = useContext(Context);
// state
const [ok, setOk] = useState(false);
// router
const router = useRouter();
useEffect(() => {
fetchUser();
}, []);
const fetchUser = async () => {
try {
const { data } = await axios.get("/api/current-user");
console.log(data);
if (data.ok) setOk(true);
} catch (err) {
console.log(err);
setOk(false);
router.push("/login");
}
};
return (
<>
{!ok ? (
<SyncOutlined spin className="d-flex justify-content-center display-1 text-primary p-5" />
) : (
<div className="UserNavSec">
<div className="UserNavCol1">
<div className="UserNavCont"><UserNav/></div
</div>
<div className="UserNavCol2"> {children} </div
</div>
)}
</>
);
};
export default UserRoutes;

I have a problem with the state.map in react.js not showing data

I'm having a problem with my code. I'm fetching data from the server-side (node.js) and save it in using setState(data), when I do the console.log(this.state.data) it Shows all the data, but When I try to do the map it returns nothing.
Although, when I create an object on the CardList.js it shows the data correctly.
CardList.js
import React from 'react';
import Card from './card';
import {getAllData} from '../store/store';
import '../sass/cardList.scss';
class CardsList extends React.Component {
constructor(props){
super(props);
this.state = {
data: [],
};
}
async updateState() {
var datas = await getAllData();
this.setState({data: datas});
}
componentDidMount(){
this.updateState();
}
render() {
console.log(this.state.data);
const cards = this.state.data.map(([key, element]) => <Card key={key} title={element.title} url={element.url} target={element.target} category={element.category} date={element.date}/>)
return(
<div className="cardList">
{cards}
</div>
);
}
}
export default CardsList;
Card.js
import React from 'react';
import '../sass/card.scss';
class Card extends React.Component {
render() {
return(
<div className="card-container">
<div>
{this.props.category}</span> <span className="font">{this.props.date}</span>
</div>
<div>
<h3 className="title">{this.props.title}</h3>
</div>
<div>
<p className="small">{this.props.url}</p>
</div>
</div>
)
}
}
export default Card;
store.js
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:3001/'
})
export function getAllData(){
var data = [];
api.get('/')
.then((response) =>{
response.data.forEach((item) => {
data.push(item);
});
})
.catch((error) => {
return console.log(error);
});
return data;
}
export function getDataByCategory(category){
var data = [];
api.get('/'+category).then(response => {
response.data.forEach((item) => {
data.push(item);
});
}).catch((err) => {
return console.log(err);
});
return data;
}
export function getMenu(){
var data = [];
api.get('/menu')
.then((response) =>{
response.data.forEach((item) => {
data.push(item);
});
})
.catch((error) => {
return console.log(error);
});
return data;
}
Server-Side:
routes.js
const appRouter = (app, fs) => {
//variables
const data = './data/globalSearch.json';
app.get('/', (req, res) =>{
fs.readFile(data, 'utf8',(err, data) => {
if(err){
throw err;
}
res.send(200,JSON.parse(data).posts);
})
}),
app.get('/menu', (req, res) => {
fs.readFile(data,'utf8', (err, data) => {
res.send(JSON.parse(data).menu);
})
}),
app.get("/:category", (req, res) => {
const category = req.params.category;
var postsFound = [];
console.log("category: ", category);
fs.readFile(data,'utf8', (err, data) => {
const posts = JSON.parse(data).posts
postsFound = posts.map( post => {if(post.category == category) return post}).filter(value => value != null);
res.send(postsFound);
})
})
};
module.exports = appRouter;
This is the output
[1]: https://i.stack.imgur.com/pmhLw.png
If I create the default data in the CardsList, the cards appear witht the info
[2]: https://i.stack.imgur.com/emse9.png
see if this works:
render() {
return(
<div className="cardList">
{this.state.data.length!==0?this.state.data.map((key, element) => return (<Card key={key} title={element.title} url={element.url} target={element.target} category={element.category} date={element.date}/>);):' ' }
</div>
);
}
Sorry if any typos.

Cannot add to cart on frontend react.js, although the end points are working correctly and it is adding items to cart in post man app

so I am trying to add to cart. My node.js endpoints are working correctly and I am able to add items to cart when viewed in postman app but it does not display items on the front end, and when inspecting through the chrome developers tools, the items array is empty when on the postman while testing it is successfully storing items.
Here is my server.js
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
let Book = require("./models/bookModel");
const port = 4000;
app.use(cors());
app.use(bodyParser.json());
mongoose.connect("mongodb://127.0.0.1:27017/books", { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once("open", function() {
console.log("MongoDB database connection established successfully..");
});
const bookRoutes = express.Router();
app.use("/books", bookRoutes);
const cartRoutes = express.Router();
app.use("/cart", cartRoutes);
bookRoutes.route("/").get(function(req, res) {
Book.find(function(err, books) {
if (err) {
console.log(err);
} else {
res.json(books);
}
});
});
bookRoutes.route("/:id").get(function(req, res) {
let id = req.params.id;
Book.findById(id, function(err, book) {
res.json(book);
});
});
cartRoutes.route("/").get(function(req, res) {
var cart = req.session.cart;
var displayCart = { items: [], total: 0 };
var total = 0;
for (var item in cart) {
displayCart.items.push(cart[item]);
total += cart[item].qty * cart[item].price;
}
displayCart.total = total;
return res.json(displayCart);
});
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
Book.findById(id, function(err, book) {
if (err) {
console.log(err);
}
if (cart[id]) {
cart[id].qty++;
} else {
cart[id] = {
item: book._id,
title: book.title,
price: book.price,
qty: 1
};
}
res.redirect("/cart");
});
});
app.listen(port, function() {
console.log("Server is running on Port: " + port);
});
the server response:
{
"items": [
{
"item": "5dd7668f33c21d811b74f403",
"title": "Modern PHP",
"price": 25.65,
"qty": 1
},
{
"item": "5dd6bb36725bbba1ca482eea",
"title": "Professional Node.js",
"price": 20.56,
"qty": 2
}
],
"total": 66.77
}
cart.js
import React, { Component } from "react";
import axios from "axios";
import CartItem from "./cart1-item.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import { throws } from "assert";
export default class Cart extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({
items: response.data.items
});
console.log(response.data.items);
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
Calculate = item => {
return item.qty * item.price;
};
render() {
return (
<div className="container">
<div className="row">{this.checkItems()}</div>
</div>
);
}
}
cartitem.js
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
const CartItem = props => {
return (
<div className="container">
<h2>{props.book.title}</h2>
</div>
);
};
export default CartItem;
here is the app.js code for cart route
<Route path="/cart" exact component={Cart}></Route>
Edited code book-details.component.js
import React, { Component } from "react";
import "../css/styles.css";
import axios from "axios";
export default class BookDetails extends Component {
constructor(props) {
super(props);
this.state = {
book: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/books/" + this.props.match.params.id)
.then(response => {
this.setState({ book: response.data });
})
.catch(function(err) {
console.log(err);
});
}
AddToCart = e => {
let id = e.currentTarget.getAttribute("id");
axios.post(`http://localhost:4000/cart/${id}`).then(() => {
window.location.href = "http://localhost:3000/cart/";
});
};
render() {
const { book, quantity } = this.state;
return (
<div className="container">
<div className="row">
<div className="col sm-4">
<img src={`./images/${book.cover}`}></img>
</div>
<div className="col sm-8">
<h2>{book.title}</h2>
<ul>
<li>Category: {book.category}</li>
<li>Author: {book.author}</li>
</ul>
<p className="button blue">${book.price}</p>
<p>{book.description}</p>
<button id={book._id} onClick={this.AddToCart}>
Add To Cart
</button>
</div>
</div>
</div>
);
}
}
App.js
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import BooksList from "./components/book-list.component.js";
import BookDetails from "./components/book-details.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import Navigation from "./components/navigation.component";
import Cart from "./components/cart1.component";
class App extends Component {
render() {
return (
<Router>
<Navigation></Navigation>
<Route
path="/"
exact
render={() => (
<div className="container">
<BooksList></BooksList>
</div>
)}
></Route>
<Route path="/books/:id" exact component={BookDetails}></Route>
<Route path="/cart/" exact component={Cart}></Route>
</Router>
);
}
}
export default App;
Any help would be appreciated.
i think i should point out that what you are passing to CartItem is "books" while in the CartItem component you are trying to get "items" from props (this.props.items). that part should be this.props.books.title.
UPDATE:
After you updated your question, i noticed this addition:
and on clicking the add to cart button it navigates to the link
href={"/cart"} className="button"> Add to Cart
this might be where your problem is coming from. on the API, to add books to cart, you did something like this:
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
so you are basically making a post request (even though from the code you are not really posting any data since you are just extracting the id from the url parameter. maybe you should consider making this a get request).
the key part here is the post http method and the id that is expected as the url parameter.
to make things simple on yourself, you can change your "add to cart" to something like:
<button className="button" id={book.id} onClick={this.addToCart}>
Add to Cart
</button>
for addToCart, you can do something like this:
addToCart=(e)=>{
let id = e.currentTarget.getAttribute("id");
axios.post(`http://localhost:4000/cart/${id}`)
.then(()=>{window.location.href = "http://localhost:3000/cart"})
}
note that like i said, you can replace the post request above to a get request since you are not actually posting any form data. if you wish to do this, you should also change the corresponding post request in your api to a get request.
Also, note that you can't get cart items posted through postman from the browser. you are using node sessions for cart items storage. you have to create a different react component (if you have not already created it) from where you can send the post request above to your express api

Next JS Custom Server app.render do not pass query to a component

I'm trying to pass userData with app.render, but while Server side rendering router.query is empty, although i have passed userData! Is it NextJS's bug, or am i doing something wrong?
app.js:
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
if (pathname === '/index') {
app.render(req, res, '/index', {
userData: {
id: 1,
name: 'admin'
}
})
} else {
handle(req, res, parsedUrl)
}
}).listen(3333, err => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
pages/index.js:
import { useRouter } from 'next/router'
export default () => {
const router = useRouter();
const { query } = router;
return (
<div>
Query: {JSON.stringify(query)}
</div>
);
};
If getInitialProps is absent, Next.js will statically optimize your
page automatically by prerendering it to static HTML. During
prerendering, the router's query object will be empty since we do not
have query information to provide during this phase. Any query values
will be populated client side after hydration.
You can access your query using getInitialProps.
with useRouter:
import { useRouter } from 'next/router'
const Index = () => {
const router = useRouter();
const { query } = router;
return (
<div>
Query: {JSON.stringify(query)}
</div>
);
};
Index.getInitialProps = async () => {
return {};
};
export default Index
with a class component:
import React from 'react'
class Index extends React.Component {
static async getInitialProps (context) {
let query = context.query;
return {query}
}
render() {
let {query} = this.props
return (
<div>
Query: {JSON.stringify(query)}
</div>
);
}
}
export default Index
Or if you prefer a functional component :
const Index = (props) => (
<div>
Query: {JSON.stringify(props.query)}
</div>
)
Index.getInitialProps = async ( context ) => {
let query = context.query;
return {query}
}
export default Index
Please note that obviously this works with /index but not with /
I think it's as simple as adding a return statement before app.render to prevent the rest of the code from executing.
if (pathname === '/index') {
return app.render(req, res, '/index', {
userData: {
id: 1,
name: 'admin'
}
})
} else {
handle(req, res, parsedUrl)
}

Resources