How to connect Express JS to NotionAPI with POST and GET - node.js

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
...
})

Related

how can I get form data from react to node.js?

I am trying to send the form data of react component to the server through using axios or fetch.
All of the code I wrote failed (in onSubmit).
My server's port is 5000.
I have already set bodyParser in server.js as well.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// React Component
const Voting = ({ history }) => {
const [hasQuery, setQuery] = useState('');
const [formData, setFormData] = useState({
cat: false,
dog: false,
});
const { cat, dog } = formData;
const onChange = e => {
let obj = {};
obj[e.target.name] = e.target.checked;
setFormData(obj);
};
const onSubmit = async e => {
e.preventDefault();
1.
// axios.post('http://localhost:5000/main', { formData }).then(result => {
// console.log(result, 'result');
// });
2.
fetch('http://localhost:5000/main', {
method:'post',
body: formData
})
.then(res => res.json())
.then(data => alert(data.msg));
3.
// fetch('http://localhost:5000/main', {
// method: 'post',
// headers: {
// 'Content-Type': 'application/json; charset=utf-8'
// },
// body: JSON.stringify(formData),
// })
// .then(res => res.json())
// .then(obj => {
// if (obj.result === 'succ') {
// alert('Move on to the next.');
// }
// });
history.push(`/success`);
};
return (
<div>
<p>{hasQuery}</p>
<form
className='mode-container'
onSubmit={onSubmit}
mathod='POST'
action='http://localhost:5000/main'
>
<h3>Choose what you like more</h3>
<div>
<label>
cat
<input
type='radio'
name='cat'
value={cat}
onChange={onChange}
/>
</label>
<label>
dog
<input
type='radio'
name='dog'
value={dog}
onChange={onChange}
/>
</label>
</div>
<button type='submit'></button>
</form>
</div>
);
};
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated,
});
export default connect(mapStateToProps)(Voting);
And below is the code I wrote in node.js. req.body is an empty object.
// Node.js
mainRouter.post(
'/',
(req, _res, _next) => console.log(req, 'req.body'),
);
You are targeting /main in all of your client calls, but on server you only have / route.
Try the following:
axios.post('http://localhost:5000/', { formData }).then(result => {
console.log(result, 'result');
});
Or - change the server route:
mainRouter.post(
'/main',
(req, _res, _next) => console.log(req, '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 send search result back to client side?

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);
});
}

ReactNative : variable for FormData() being logged as undefined in ReactNative application when i make an http request with a file

i have been trying to make a post request from my react native app on an android emulator but the object that i am sending in the body of the request is being vvalued as undefined,been trying alot to solve this issue but no success. Please help me
here is the code to my form in the app known as "API.js" its named so just for testing the API endpoints
import React, { Component } from "react";
import {
Text,
TextInput,
View,
TouchableHighlight,
StyleSheet,
Button,
FormData
} from "react-native";
import Permissions from "react-native-permissions";
import ImagePicker from "react-native-image-crop-picker";
import axios from "axios";
var styles = StyleSheet.create({});
var msg = "Select Image";
var data;
export default class API extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
price: "",
size: "",
description: "",
image: "",
popularity: ""
};
}
FormDataFunc() {
let form = new FormData();
form.append("name", this.state.name);
form.append("price", this.state.price);
form.append("size", this.state.size);
form.append("description", this.state.description);
form.append("image", this.state.image);
form.append("popularity", this.state.popularity);
return form;
return form;
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
Submit() {
axios({
method: "post",
url: "http://192.168.0.102:3000/products",
data: this.FormDataFunc,
headers: {
Accept: "application/json",
"Content-Type": "application/x-www-form-urlencoded"
},
body: this.FormDataFunc
})
.then(() => {
console.log("DONE!");
this.props.navigation.navigate("offersScreen");
})
.catch(err => {
console.log(err);
console.log(this.FormDataFunc);
});
}
componentDidMount() {
Permissions.check("photo").then(response => {
// Response is one of: 'authorized', 'denied', 'restricted', or 'undetermined'
console.log(response);
});
}
render() {
const image = () => {
ImagePicker.openPicker({
multiple: false,
includeBase64: true
}).then(images => {
console.log(images);
this.setState({
images: { data: `\`${images.mime}\`;base64,\`${images.data}\`` }
});
console.log(this.state.images);
msg = "Selected";
});
};
return (
<View>
<TextInput placeholder="Name" name="name" />
<TextInput placeholder="Price" name="price" />
<TextInput placeholder="Size" name="size" />
<TextInput placeholder="Description" name="description" />
<TouchableHighlight onPress={image} name="image">
<Text>{msg}</Text>
</TouchableHighlight>
<TextInput placeholder="Popularity" name="popularity" />
<TouchableHighlight title="Submit" onPress={this.Submit}>
<Text>Send</Text>
</TouchableHighlight>
</View>
);
}
}
here is the code to my backend route "product.js" which recieves the request(node.js and express)
const express = require("express");
const router = express.Router();
const bodyParser = require("body-parser");
const path = require("path");
const cloudinary = require("cloudinary");
const mongoose = require("mongoose");
//Product Model
const product = require("../models/product").product;
router.use(bodyParser.json());
//GET route
router.get("/", (req, res) => {
product.find().then(product => res.json(product));
});
// POST route
cloudinary.config({
cloud_name: "cloud222",
api_key: "783935981748815",
api_secret: "uY47vBS1rI2x5jvFtnXQIjMMqU0"
});
router.post("/", (req, res) => {
//MISC//
let imageUrl;
console.log("starting");
//MISC//
//CLOUDINARY UPLOAD
cloudinary.v2.uploader.upload(req.body.image, (error, result) => {
if (error) {
console.log(error);
console.log(`${req.body.image}`);
} else {
imageUrl = result.secure_url;
//MONGO UPLOAD
var productv = {
name: req.body.name,
price: req.body.price,
size: req.body.size,
description: req.body.description,
image: imageUrl,
popularity: req.body.popularity
};
const productvar = new product(productv);
productvar
.save()
.then(product => console.log(product))
.then(product => res.json(product))
.catch(err => console.log(err));
}
});
//END//
});
module.exports = router;
the data is being logged as undefined and therefore the request doesnt get a response
I think the problem is that you have created a FormData and wanted to post it, but the headers you added is "application/json". You should change it:
headers: {
'content-type': 'multipart/form-data',
'Accept':'multipart/form-data',
},
Hope it works for you.
Problem solved
Had to use onChangeText = instead of onChange as onChange does not return a string

Resources