New to Axios and Node as a backend- have inherited some code in React for a login page and am trying to figure out why I can't make a POST to the backend.
This is my .env file:
REACT_APP_BACKEND_URL=http://localhost:3000/admin
And there is a file called API.js
import axios from "axios";
// Set config defaults when creating the instance
export const CompanyAPI = () => {
let api = axios.create({
baseURL: process.env.REACT_APP_BACKEND_URL,
timeout: 10000,
headers: {
"Content-Type": "application/json",
},
});
return api;
};
And then there is a LoginPage.js:
export const LoginPage = () => {
const API = CompanyAPI();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const getAuth = (e) => {
e.preventDefault();
API.post("/auth/login", {
email: email,
password: password,
})
.then(async (response) => {
if (response.status === 200) {
await localStorage.setItem("jwt_key_admin", response.data.token);
setTokenKey(response.data.token);
setIsAuth(true);
navigate("/");
}
})
.catch((error) =>
alert(
"Login Failed"
)
);
};
My question is, is there an example on how I could use express to handle the /auth/login endpoint and complement the existing API.js file?
Basically what I see from this code is:
An axios instance was created and the baseURL was set to being http://localhost:3000/admin
From first glance I can tell that all Api calls that you make a resulting to 404 reason being React locally will always run on port 3000 unless that port is in use.
So now your axios baseURL being to set to port 3000 definitely axios should return a 404 because you surely do not have the endpoint that you are trying to hit
Solution:
Here you are to change the baseURL's port number to the port where Nodejs server is listening on
Then once that is said and done then make sure that even the endpoints that you are trying to hit do exist then your axios calls should work now
Advise:
If the Axios instance created is confusing drop it for a bit and import raw axios not the instance then use axios in it's basic form then once you have that working you surely will have established the correct port number and everything then you can edit the axios instance created with baseURL you have established.
Add
"proxy":"http://localhost:3000/admin"
in your package.json file and restart your React App.
Related
I am pretty new to Programming and have some questions regarding the MERN stack.
I am building an app and trying to realize the log in via google. I was successful with integrating the google auth to my frontend and now I want to store the user after a successful login in the backend.
The first question I have is do I need a database to store the user, or is it common to just store them on the express backend?
In the auth process I do get the JWT from google and try sending it to the backend, but it does not work.
I do get the following error: "SyntaxError: Unexpected token o in JSON at position 1".
How can I send the JWT to the backend and check, if the user already exists in MongoDB and if he does not exist, make a new entry for the user. And when he is logged in, make a session so he does not have to log in again after every refresh.
At the moment I got the following code for the frontend:
import './App.css';
import { useEffect, useState } from 'react'
import jwt_decode from 'jwt-decode';
import Survey from './components/survey';
function App() {
const [ user, setUser] = useState({});
const [backendData, setBackendData] = useState([{}]);
// fetch backend API, we can define relative route, as proxy is defined in package.json
useEffect(() => {
fetch("http://localhost:5000/api").then(
response => response.json()
).then(
data => {
setBackendData(data)
}
)
}, [])
// store the JWT and decode it
function handleCallBackResponse(response){
console.log("Encoded JWT ID token: " + response.credential);
var userObject = jwt_decode(response.credential);
console.log(userObject);
setUser(userObject);
document.getElementById("signInDiv").hidden = true;
fetch("http://localhost:5000/user", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: userObject,
})
}
// logout the user and show sign in button, google.accounts.id.disableAutoSelect is recording the status in cookies. This prevents a UX dead loop.
function handleSignOut(event){
setUser({});
document.getElementById("signInDiv").hidden = false;
google.accounts.id.disableAutoSelect();
}
useEffect(() => {
/* global google */
// The google.accounts.id.initialize method initializes the Sign In With Google client based on the configuration object.
google.accounts.id.initialize({
client_id: "CLIENT_ID",
callback: handleCallBackResponse
});
// The google.accounts.id.renderButton method renders a Sign In With Google button in your web pages
google.accounts.id.renderButton(
document.getElementById("signInDiv"),
// if only want to display icon
//{theme: "outline", size: "medium", type: "icon"}
{theme: "outline", size: "medium", text: "signin", shape: "square"}
);
// The google.accounts.id.prompt method displays the One Tap prompt or the browser native credential manager after the initialize() method is invoked.
google.accounts.id.prompt();
}, [])
// If we have no user: show sign in button
// if we have a user: show the log out button
return (
<div className="App">
<div id = "signInDiv"/>
{ Object.keys(user).length !== 0 &&
<button className ='signout' onClick={ (e) => handleSignOut(e)}>
{ user &&
<img className='logout' alt="googleprofile" src={user.picture} width='30px' height='30px'></img>
}
</button>
}
<Survey></Survey>
{(typeof backendData.users === 'undefined') ? (
<p>Loading</p>
) : (
backendData.users.map((user, i) => (
<p key={i}>{user}</p>
))
)}
</div>
);
}
export default App;
Backend:
const express = require('express')
const morgan = require('morgan')
const cors = require("cors")
const mongoose = require("mongoose")
const { OAuth2Client } = require("google-auth-library");
const jwt = require("jsonwebtoken");
const app = express()
const uri = "MONGODBURL"
// connect to mongoDB
async function connect() {
try {
await mongoose.connect(uri)
console.log("Connected to MongoDB")
} catch (error) {
console.error(error)
}
}
connect()
// setup view engine, file in "views" folder needs to have ending .ejs
app.set('view engine', 'ejs')
// log requests in the terminal for troubleshooting
app.use(morgan('combined'))
// allow cors URLs
app.use(cors({
origin: ['http://localhost:3000', 'https://play.google.com', 'https://accounts.google.com'],
methods: ["GET", "POST", "PUT", "DELETE"],
credentials: true
}))
app.use(express.json());
// start app on port 5000 and give an log message
app.listen(5000, () => {console.log("Server started on port 5000") })
Hope this make clear what I want to achieve. I really appreciate any help.
Kind Regards
My frontend code:
const user = useSelector(selectUser)
function getWatchLater(name){
axios.get('http://localhost:5000/watchlater', {user:name})
.then((response)=>{
// console.log(response.data)
setWatchLater(response.data)
})
}
The user variable holds the username and the function sends that username to the backend to get the data. Don't worry, the user variable does hold the username, i have checked it thoroughly.
My backend code:
const mysql = require('mysql2')
const express = require('express')
const cors = require('cors');
const app = express()
app.use(cors());
app.use(express.json())
app.get("/watchlater", (request, response)=>{
const user = request.body.user;
//console.log(user);
});
So basically, it will get the username and run the query. The problem is it does not get the username at all from the frontend. I tried console logging the variable user but to no avail. It returns empty.
The second argument in the axios.get() function is expecting a config object. Checkout the axios documentations on instance method and request config.
In short, in the frontend part, pass your payload into the data field of the config object, as shown in the example below.
const config = {
headers: { Authorization: token },
data: { user:name }
}
const response = await axios.get(`${url}`, config)
You need to send parameter using params object of config in case of get request. Your frontend request should change to this,
const user = useSelector(selectUser)
function getWatchLater(name){
axios.get('http://localhost:5000/watchlater', { params: { user: name }
}).then((response)=>{
// console.log(response.data)
setWatchLater(response.data)
})
}
In your express endpoint you should receive it as,
app.get("/watchlater", (request, response)=>{
const user = request.params.user;
//console.log(user);
});
I am attempting to follow this tutorial (https://levelup.gitconnected.com/introduction-to-express-js-a-node-js-framework-fa3dcbba3a98) to connect Express with React Native via . I have a server.js script running which connects to the client (App.tsx) on my ip, port 3000. The server and app are run simultaneously on the same device in different terminals. The server is able to recieve GET requests just fine, as when the app launches, a useEffect function calls a GET request, and the server sends a message. However my POST requests, which contain a content body set to JSON.stringify("hello world") are not working. and the server script returns the following error anytime I press the button:
SyntaxError: Unexpected token h in JSON at position 0
at JSON.parse (<anonymous>)
...
I'm assuming I am sending in badly formatted json, or haven't set the content type properly, but I haven't been able to figure out the exact problem.
App.tsx (where myip is my ip address):
import { StatusBar } from 'expo-status-bar';
import React, { useEffect, useState } from 'react';
import { StyleSheet, Text, View, ScrollView, TouchableOpacity, TextInput } from 'react-native';
export default function App() {
const [response, setResponse] = useState();
useEffect(() => {
fetch("http://myip:3000/get")
.then(res => res.json())
.then(res => console.log(res.theServer))
}, []);
async function handleSubmit() {
console.log('button press');
const response = await fetch("http://myip:3000/wow/post", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify("hello world")
});
const body = await response.text();
setResponse({ responseToPost: body });
}
return (
<View style={styles.container}>
<TouchableOpacity onPress={handleSubmit}>
<Text>Submit</Text>
</TouchableOpacity>
</View>
}
...
});
server.js
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const port = process.env.PORT || 3000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.get("/get", (req, res) => {
res.send({ theServer: "hElLo FrOm YoUr ExPrEsS sErVeR" });
});
app.post("/wow/post", (req, res) => {
console.log(req.body);
res.send(`Here is what you sent me: ${req.body.post}`);
});
app.listen(port, () => console.log(`listening on port ${port}`));
First, stop using body-parser. Express has its own request body parsing middleware.
app.use(express.json())
app.use(express.urlencoded()) // extended = true is the default
The JSON parsing middleware is configured to handle objects by default. While a string literal like "hello world" is valid JSON, it's not what the framework expects, hence your error.
Since you appear to be trying to access req.body.post, you should send your data with such a structure
fetch("http://myip:3000/wow/post", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ post: "hello world" })
})
Alternatively, if you did want to post a JSON string literal, you would need to configure your JSON middleware like so
app.use(express.json({ strict: false }))
strict
Enables or disables only accepting arrays and objects; when disabled will accept anything JSON.parse accepts.
in which case your "hello world" string would appear in req.body
for me, adding
res.header('Access-Control-Allow-Origin', '*');
solved the problem
I am trying to setup a cache according to this guide for one expensive query that has a result that changes only once a day. The query takes 7-8 seconds and most it is after the DB query, because the response returned from the resolver must be heavily processed.
I am using apollo-server-express library and the change pluging is apollo-server-plugin-response-cache.
This is what I have done:
server.js
const { ApolloServer } = require('apollo-server-express')
const responseCachePlugin = require('apollo-server-plugin-response-cache')
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
// ...
},
plugins: [responseCachePlugin()]
resolvers.js
personDetails: async (root, args, ctx, info) => {
info.cacheControl.setCacheHint({ maxAge: 600 })
const persons = await Person.find({}).populate('details')
// processing the data
return processedData
}
I expect the resolver to run once and then after that the response should be returned from the cache almost instantly. This doesn't work. I am doing something wrong or I haven't understood how this should work.
I tried to put cache hints also in the schema, but didn't get any better results.
It should work. Here is a working example:
server.ts:
import { ApolloServer, gql } from 'apollo-server-express';
import express from 'express';
import responseCachePlugin from 'apollo-server-plugin-response-cache';
const typeDefs = gql`
type Query {
personDetails: String
}
`;
const resolvers = {
Query: {
personDetails: async (root, args, ctx, info) => {
console.log(`[${new Date().toLocaleTimeString()}] Query.personDetails`);
info.cacheControl.setCacheHint({ maxAge: 10 });
return 'This is person details';
},
},
};
const app = express();
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
plugins: [responseCachePlugin()],
});
apolloServer.applyMiddleware({ app });
const server = app.listen({ port: 4000 }, () => {
console.log(`The server is running in http://localhost:4000${apolloServer.graphqlPath}`);
});
The request logs:
The server is running in http://localhost:4000/graphql
[1:51:27 PM] Query.personDetails
[1:51:52 PM] Query.personDetails
The response header:
cache-control: max-age=10, public
The first graphql request send at [1:51:27 PM]. In the next 10 seconds, all requests sent will hit the cache(defaults to an in-memory LRU cache), which means the graphql resolver personDetails will not execute. The graphql response will be read from the cache and sent to client-side.
After 10 seconds, I send another graphql request at [1:51:52 PM]. The cache is expired. So this request will not hit the in-memory cache. The graphql resolver will execute and generate a new value.
source code: https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/stackoverflow/57243105
I understand that this is an old question but it might help someone solve server side caching issue in Apollo.
So as per my understanding the apollo-server-plugin-response-cache plugin only works if the response from the underlying REST API have control-cache header. In case there is no cache-control header in your REST API it can be overriden in restDataSouce GET calls as below.
return this.get(`uri_path`,"", {cacheOptions: { ttl: 1}});
After that continue using info.cacheControl at the resolvers as suggested in the example from the question snippets. maxAge there will override the time mentioned in the cacheOptions.
I need to access my backend API to send info from a contact form for an email, I deployed my app in a webhost called Kinghost and it gave me two urls the first is generically mywebaddr.com:port-number and the second is mywebaddr.com/site.
I have tried to use both addresses with the function route in the end just like I did in localhost, that in order to work I used http://localhost:4000/contact for example, but it didn't work...
this is my request:
const baseUrl = 'http://mywebsiteurl.com/contact'
const initialState = {
message: {
name: '',
email: '',
subject: '',
main: ''
},
}
export default class ContactUs extends Component {
state = { ...initialState }
reset = () =>{
this.setState({message: initialState.message})
}
send = () => {
const message = this.state.message
const url = baseUrl
console.log(message)
axios.post(url, message)
.then(this.reset())
.then(alert('Message successfully sent!'))
}
this is my index.js (backend)
const express = require('express')
const app = express()
const consign = require('consign')
const port = 4005
consign()
.then('./config/middlewares.js')
.then('./api')
.then('./config/routes.js')
.into(app)
app.listen(port, () => {
console.log(port)
})
my middlewares.js contains cors
const bodyParser = require('body-parser')
const cors = require('cors')
module.exports = app => {
app.use(bodyParser.json())
app.use(cors())
}
Actually, I don't think it's because of my code itself once I can execute everything perfectly in localhost, but somehow I can't get through with the correct URL
I'm new to node and I can't guess what am I doing wrongly, so if someone can help me I'd be really thankful :)
This is not a question for stack-overflow as your issue is not with the application but is with your network.
Anyhow, your API application is running on port 4005. Make sure the port is open with your hosting provider. while your at it make sure your port 4000 is open as well.
after you confirm your firewall settings ill update my answer if your still facing issues.