How do I send search result back to client side? - node.js

I'm making a web app that uses Spotify API now. I'd like to get a search keyword from client side and send its search result back to the client side. I'm making a post request with the search keyword to "http://localhost:4000/search_result" and send the result back to "http://localhost:4000/api" and then fetch the data. I get a 404 for the fetch call when I look into Network on the Chrome dev tool.
And I'd also like to know if there's way to make use of the search keyword from the post request for a get request in server.js.
Main.js
import React, { Component } from "react";
import SingerCard from "./SingerCard";
import axios from "axios";
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
keyword: "",
artists: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({ keyword: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
axios
.post(
"http://localhost:4000/search_result",
{
keyword: this.state.keyword,
},
{
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
// Accept: "application/json",
},
}
)
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.log(err);
});
}
componentDidMount() {
fetch("http://localhost:4000/api")
.then((res) => res.json())
.then((artists) => {
this.setState({ artists });
})
.catch((err) => console.log);
}
render() {
return (
<div className="main">
<form onSubmit={this.handleSubmit}>
<label htmlFor="search">Search an artist: </label>
<span>
<input
type="search"
value={this.state.keyword}
onChange={this.handleChange}
name="keyword"
/>
<button type="submit" value="Submit">
Search
</button>
</span>
</form>
<br />
<div className="container">
{this.state.artists.map((elem) => (
<SingerCard
images={elem.images}
name={elem.name}
artists={this.state.artists}
/>
))}
{console.log("Arists are: " + this.state.artists)}
</div>
<br />
</div>
);
}
}
export default Main;
server.js
const express = require("express");
const SpotifyWebApi = require("spotify-web-api-node");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const port = 4000 || process.env.PORT;
require("dotenv").config();
app.use(express.json());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
// Create the api object with the credentials
var spotifyApi = new SpotifyWebApi({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
});
// Retrieve an access token.
spotifyApi.clientCredentialsGrant().then(
function (data) {
console.log("The access token expires in " + data.body["expires_in"]);
console.log("The access token is " + data.body["access_token"]);
// Save the access token so that it's used in future calls
spotifyApi.setAccessToken(data.body["access_token"]);
},
function (err) {
console.log("Something went wrong when retrieving an access token", err);
}
);
app.post("/search_result", (req, res) => {
console.log(req.body.keyword);
spotifyApi.searchArtists(req.body.keyword).then(function (data) {
console.log(data.body);
var search_res = data.body.artists.items;
app.get("/api", (req, res) => {
res.json(search_res);
res.end();
});
res.end();
}),
function (err) {
console.log(err);
};
});
app.listen(port, () => console.log(`It's running on port ${port}`));

Your server side routes should be configured once, not on the fly. When you call app.post and app.get you are configuring a route and providing a callback that will be used every time a request comes in that matches that route. In your code, you create a post route, and then inside the handler you create a get route. Don't do that.
HTTP Servers are meant to be stateless. This means each request (post, get) know nothing about one another. If you want to share information between, you do it via a database (in-memory, redis, nosql, sql, etc...), where each route is using only the parameters passed to it.
For example, the POST route creates an id for the search results. The POST route returns this id. The GET route when called, should receive this same id and look up the results. Of course, this is only necessary for really long processes, typically job queues. For your specific case, you only need one route.
app.post("search_result", (req, res) => {
console.log(req.body.keyword);
spotifyApi
.searchArtists(req.body.keyword)
.then(
function(data) {
console.log(data.body);
var search_res = data.body.artists.items;
res.json(search_res);
res.end();
},
function (err) {
console.log(err);
res.status(500).send(err);
}
);
});
And don't forget to remove your componentDidMount code since you only need to call the POST route.
handleSubmit(e) {
e.preventDefault();
axios
.post(
"http://localhost:4000/search_result",
{
keyword: this.state.keyword,
},
{
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
// Accept: "application/json",
},
}
)
.then(function (res) {
console.log(res);
this.setState({ artists });
})
.catch(function (err) {
console.log(err);
});
}

Related

How to connect Express JS to NotionAPI with POST and GET

I am very new to backend so I am trying to implement a simple Notion form on a Typescript website. I was following this tutorial: https://commotion.page/blog/how-to-create-a-form-app-with-the-notion-api to learn how to post from localhost:3000 to the Notion backend database which looks exactly like the one in the tutorial. However, every time I reload the page I get this error:
Here is the code for my express server, app.js (exactly the same as the one in the tutorial):
const express = require('express')
const axios = require('axios')
const cors = require('cors')
const app = express()
const port = 3002
const secretKey = 'secret_[KEY REPLACED]'
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${secretKey}`,
'Notion-Version': '2022-02-22',
}
app.post('/704f58825cf841759f76733c5794e8aa', async (req, res) => {
const { name, email } = req.body
await axios({
method: 'POST',
url: 'https://api.notion.com/v1/pages',
headers,
data: {
parent: { dat704f58825cf841759f76733c5794e8aaabase_id },
properties: {
"Name": { "title": [{ text: { content: name } }] },
"Email": { "email": email }
}
}
})
})
app.get('/:database_id', async (req, res) => {
const { database_id } = req.params;
const resp = await axios({
method: 'GET',
url: 'https://api.notion.com/v1/databases/' + database_id,
headers
});
return res.json(resp.data)
})
app.use(cors())
app.use(express.json())
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
I am using ChakraUI for my front-end as well. This is the code for my main app, which is located outside of the server folder in the main app folder:
const database_id = '[KEY REPLACED]';
export default function ContactFormWithSocialButtons() {
const { hasCopied, onCopy } = useClipboard('intellimindsBR#gmail.com');
const [db, setDB] = useState({});
const [email, setEmail] = useState('');
const [name, setName] = useState('');
const onSubmit = (e) => {
e.preventDefault();
console.log(name);
console.log(email);
fetch('http://localhost:3002/' + database_id, {
method: 'POST',
body: JSON.stringify({ email: email, name: name }),
headers: { "Content-Type": 'application/json' }
});
}
useEffect(() => {
fetch('http://localhost:3002/' + database_id).then(async (resp) => {
setDB(await resp.json());
});
}, []);
return (
<Box
bg={useColorModeValue('white', 'gray.700')}
borderRadius="lg"
p={8}
color={useColorModeValue('gray.700', 'whiteAlpha.900')}
shadow="base">
<VStack spacing={5}>
<form onSubmit={onSubmit}>
<FormControl isRequired>
<FormLabel>Name</FormLabel>
<InputGroup>
<InputLeftElement children={<BsPerson />} />
<Input type="text" name="name" placeholder="Your Name"
onChange={event => setName(event.currentTarget.value)} />
</InputGroup>
</FormControl>
<FormControl isRequired>
<FormLabel>Email</FormLabel>
<InputGroup>
<InputLeftElement children={<MdOutlineEmail />} />
<Input
type="email"
name="email"
placeholder="Your Email"
onChange={event => setEmail(event.currentTarget.value)}
/>
</InputGroup>
</FormControl>
<Button
type="submit"
colorScheme="blue"
bg="blue.400"
color="white"
_hover={{
bg: 'blue.500',
}}>
Send Message
</Button>
</form>
</VStack>
</Box>
);
}
When I load the server with node app.js, everything works fine and the title of the database successfully loads onto the production page but as soon as I enter data to submit in the form I get this response:
I also get this error in console for my app.js page when I submit the form:
const { name, email } = req.body
^
TypeError: Cannot destructure property 'name' of 'req.body' as it is undefined.
I will also mention that when I load the server with node app.js, the error Cannot GET / but all the data loads fine when I go to the API URL with the database ID in the URL (http://localhost:3002/[database ID]).
I have properly configured the Notion API integration and I have tried enabling Access-Control-Allow-Origin for Chrome, but I'm still facing these errors.
Before you can access req.body, you must use a body-parsing middleware like express.json:
app.post('/704f58825cf841759f76733c5794e8aa',
express.json(),
async (req, res) => {
const { name, email } = req.body
...
})

Network error from using localhost when web app is launched with Netlify

I just deployed a Spotify web app with Netlify. It works fine when it's running on my computer but it doesn't work on others. It shows Network Error, POST http://localhost:4000/search_result net::ERR_CONNECTION_REFUSED. I changed the url of the axios request to "/search_result" but it still didn't work. How should I fix it?
Here's my code.
Main.js
import React, { Component } from "react";
import SingerBox from "./SingerBox";
import axios from "axios";
import "../../App.css";
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
keyword: "",
artists: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({ keyword: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
if (this.state.keyword === "") {
alert("Enter Search Keyword");
}
axios
.post(
"/search_result",
{
keyword: this.state.keyword,
},
{
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
}
)
.then((res) => {
this.setState({ artists: res.data });
})
.catch((err) => {
console.log(err);
});
}
render() {
return (
<div className="container">
<div className="main">
<form onSubmit={this.handleSubmit}>
<label className="header" htmlFor="search">
Explore New Artists
</label>
<span>
<input
className="search-box"
type="search"
value={this.state.keyword}
onChange={this.handleChange}
name="keyword"
placeholder="Search artists..."
/>
<button className="submit-btn" type="submit" value="Submit">
Search
</button>
</span>
</form>
<br />
{this.state.artists.map((elem) => (
<SingerBox images={elem.images} name={elem.name} id={elem.id} />
))}
<br />
</div>
</div>
);
}
}
export default Main;
server.js
const express = require("express");
const SpotifyWebApi = require("spotify-web-api-node");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const port = 4000 || process.env.PORT;
require("dotenv").config();
app.use(express.json());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
// Create the api object with the credentials
var spotifyApi = new SpotifyWebApi({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
});
// Retrieve an access token.
function newToken() {
spotifyApi.clientCredentialsGrant().then(
function (data) {
console.log("The access token expires in " + data.body["expires_in"]);
// Save the access token so that it's used in future calls
spotifyApi.setAccessToken(data.body["access_token"]);
},
function (err) {
console.log("Something went wrong when retrieving an access token", err);
}
);
}
newToken();
tokenRefreshInterval = setInterval(newToken, 1000 * 60 * 60);
app.post("/search_result", (req, res) => {
spotifyApi
.searchArtists(req.body.keyword)
.then(function (data) {
let search_res = data.body.artists.items;
res.json(search_res);
res.end();
})
.catch((err) => {
console.log(err);
res.status(500).send(err);
});
});
app.get("/albums/:id", (req, res) => {
console.log(req.params.id);
spotifyApi
.getArtistAlbums(req.params.id, { limit: 40 })
.then(function (data) {
res.json(data.body.items);
res.end();
});
});
app.get("/albums/tracks/:albumID", (req, res) => {
console.log(req.params.albumID);
spotifyApi
.getAlbumTracks(req.params.albumID, { limit: 20 })
.then(function (data) {
console.log(data.body);
res.json(data.body.items);
res.end();
});
});
app.listen(port, () => console.log(`It's running on port ${port}`));
It is likely that the server code isn't running as you expect it to.
Think about what commands you run to get that localhost server running on your machine, compared to the build command you provided o Netlify to build your site.
Netlify is primarily for hosting static sites, so will not automatically provide a backend server for you to use.
You can either host the server part of your app on something like Heroku (and then have to hard-code the URL of that server instance into your app), or you can use Netlify Functions to handle your basic post request, which will let you use the relative URLs as in your question.

Why is my website running fine on my computer but not on other's?

I made a Spotify web app and launched it with Netlify. When I run its server file, it works well on my computer but not on my friend's. I thought it was because of the Spotify API at first but another web app I made, which doesn't use any API, only works on my computer as well. I think it's because of the server port or something but I'm not sure how to fix it.
Here's the website url and the server side code.
https://xenodochial-kepler-118793.netlify.app
server.js
const express = require("express");
const SpotifyWebApi = require("spotify-web-api-node");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const port = 4000 || process.env.PORT;
require("dotenv").config();
app.use(express.json());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
// Create the api object with the credentials
var spotifyApi = new SpotifyWebApi({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
});
// Retrieve an access token.
function newToken() {
spotifyApi.clientCredentialsGrant().then(
function (data) {
console.log("The access token expires in " + data.body["expires_in"]);
// Save the access token so that it's used in future calls
spotifyApi.setAccessToken(data.body["access_token"]);
},
function (err) {
console.log("Something went wrong when retrieving an access token", err);
}
);
}
newToken();
tokenRefreshInterval = setInterval(newToken, 1000 * 60 * 60);
app.post("/search_result", (req, res) => {
spotifyApi
.searchArtists(req.body.keyword)
.then(function (data) {
let search_res = data.body.artists.items;
res.json(search_res);
res.end();
})
.catch((err) => {
console.log(err);
res.status(500).send(err);
});
});
app.get("/albums/:id", (req, res) => {
console.log(req.params.id);
spotifyApi
.getArtistAlbums(req.params.id, { limit: 40 })
.then(function (data) {
res.json(data.body.items);
res.end();
});
});
app.get("/albums/tracks/:albumID", (req, res) => {
console.log(req.params.albumID);
spotifyApi
.getAlbumTracks(req.params.albumID, { limit: 20 })
.then(function (data) {
console.log(data.body);
res.json(data.body.items);
res.end();
});
});
app.listen(port, () => console.log(`It's running on port ${port}`));
Main.js
import React, { Component } from "react";
import SingerBox from "./SingerBox";
import axios from "axios";
import "../../App.css";
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
keyword: "",
artists: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({ keyword: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
if (this.state.keyword === "") {
alert("Enter Search Keyword");
}
axios
.post(
"/search_result",
{
keyword: this.state.keyword,
},
{
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
}
)
.then((res) => {
this.setState({ artists: res.data });
})
.catch((err) => {
console.log(err);
});
}
render() {
return (
<div className="container">
<div className="main">
<form onSubmit={this.handleSubmit}>
<label className="header" htmlFor="search">
Explore New Artists
</label>
<span>
<input
className="search-box"
type="search"
value={this.state.keyword}
onChange={this.handleChange}
name="keyword"
placeholder="Search artists..."
/>
<button className="submit-btn" type="submit" value="Submit">
Search
</button>
</span>
</form>
<br />
{this.state.artists.map((elem) => (
<SingerBox images={elem.images} name={elem.name} id={elem.id} />
))}
<br />
</div>
</div>
);
}
}
export default Main;
You have hardcoded localhost in your code somewhere. The apis are hitting your local server when someone is searching for the artist.
Remove localhost from code and every thing should work fine.

How do I use data from POST request for the next GET request

I'm trying to build a web app that uses Spotify API now. I want it to send a search keyword that an user submits to the server and send back its search result to the front end. The problem is I get a 404 status code for the fetch call. The POST request works fine.
Main.js
import React, { Component } from "react";
import SingerCard from "./SingerCard";
import axios from "axios";
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
keyword: "",
artists: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({ keyword: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
axios
.post(
"http://localhost:4000/search_result",
{
keyword: this.state.keyword,
},
{
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
}
)
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.log(err);
});
}
componentDidMount() {
fetch("http://localhost:4000/api")
.then((res) => res.json)
.then((artists) => {
this.setState({ artists });
});
}
render() {
return (
<div className="main">
<form onSubmit={this.handleSubmit}>
<label htmlFor="search">Search an artist: </label>
<span>
<input
type="search"
value={this.state.keyword}
onChange={this.handleChange}
name="keyword"
/>
<button type="submit" value="Submit">
Search
</button>
</span>
</form>
<br />
<div className="container">
{this.state.artists.map((elem) => (
<SingerCard
images={elem.images}
name={elem.name}
artists={this.state.artists}
/>
))}
{console.log(this.state.artists)}
</div>
<br />
</div>
);
}
}
export default Main;
server.js
const express = require("express");
const SpotifyWebApi = require("spotify-web-api-node");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const port = 4000 || process.env.PORT;
require("dotenv").config();
app.use(express.json());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
// Create the api object with the credentials
var spotifyApi = new SpotifyWebApi({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
});
// Retrieve an access token.
spotifyApi.clientCredentialsGrant().then(
function (data) {
console.log("The access token expires in " + data.body["expires_in"]);
console.log("The access token is " + data.body["access_token"]);
// Save the access token so that it's used in future calls
spotifyApi.setAccessToken(data.body["access_token"]);
},
function (err) {
console.log("Something went wrong when retrieving an access token", err);
}
);
app.post("/search_result", (req, res) => {
console.log(req.body.keyword);
spotifyApi.searchArtists(req.body.keyword).then(function (data) {
var search_res = data.body.artists.items;
res.json(search_res);
app.get("http://localhost:/api", (req, res) => {
res.json(search_res);
res.end();
});
res.end();
}),
function (err) {
console.log(err);
};
});
app.listen(port, () => console.log(`It's running on port ${port}`));
I think the app.get() in the app.post() causes the error but I can't figure out another way to send the search result back.
You're getting a 404 because the get method is not correctly defined.
Update your server code to define the get method to just keep the pathname, like this:
app.get("/api", (req, res) => {
// ...
}
Currently, you are defining this route inside the app.post. The get route definition should be outside of the post route.
Use Axios.get
import React, { Component } from "react";
// import SingerCard from "./SingerCard";
import axios from "axios";
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
keyword: "",
artists: []
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({ keyword: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
const headers = {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
};
axios.post(
"https://jsonplaceholder.typicode.com/users",
{ keyword: this.state.keyword },
{ headers: headers }
)
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});
}
componentDidMount() {
axios.get("https://jsonplaceholder.typicode.com/users").then(res => {
this.setState({
artists: res.data
});
});
}
render() {
return (
<div className="main">
<form onSubmit={this.handleSubmit}>
<label htmlFor="search">Search an artist: </label>
<span>
<input
type="search"
value={this.state.keyword}
onChange={this.handleChange}
name="keyword"
/>
<button type="submit" value="Submit">
Search
</button>
</span>
</form>
<br />
<div className="container">
{this.state.artists.map(elem => (
<div key={elem.id}>
<ul>
<li>{elem.name}</li>
</ul>
</div>
))}
</div>
</div>
);
}
}
export default Main;

How to send data from React front-end to Nodejs Express back-end on button click

Disclaimer: I am new to React and Nodejs and have of course looked at all the current similar posts but none of them were able to help me fully so please don't mark as duplicate.
In my Nodejs index.js I have:
app.get('/list-certs', function (req, res) {
fs.readFile("certs.json", 'utf8', function (err, data) {
console.log( data );
res.send( data );
});
})
app.post('/list-certs', function (req, res) {
res.send('POST request: ' + req.body.domain-input);
console.log('post is working!')
});
And in my React App.js I have:
componentDidMount() {
this.callApi()
.then(res => {
this.setState({
json: res.map(x => x),
})
})
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/list-certs');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
handleChange(event) {
this.setState({domainInputValue: event.target.value});
}
click() {
}
render() {
return (
<div className="App">
<form action="http://127.0.0.1:8000/list-certs" className="add-cert-form" method="post">
<h1>Add new domain</h1>
<h5 className="domain-label">Name:</h5>
<Input className="domain-input" value={this.state.domainInputValue} onChange={(e) => {this.handleChange(e)}}></Input>
<Button onClick={this.click} className="domain-button" type='primary'>Add</Button>
</form>
.
.
.
}
How do I send the data in my input field domain-input to my back-end when the button domain-button is clicked? I know something needs to go in the click() but I'm not sure what.
Any help is appreciated!
Your click function will look like it:
async click() {
const { domainInputValue } = this.state;
const request = await fetch('/echo/json', {
headers: {
'Content-type': 'application/json'
},
method: 'POST',
body: { domainInputValue }
});
}

Resources