Send parameter not in URL to API - reactjs

I send a request to my API with advancedFetch. This works, however, I want to know if it is possible to send a parameter without defining it in the URL. Are there any smart ways of doing this?
I tried researching it but I'm not sure I'm using the right keywords for what I'm trying to do.
This is where I set off my request (the value is from a modal input field):
setNewUserName(userName) {
this.setState({newUserName: userName});
advancedFetch('/api/users', {
method: 'POST',
body: JSON.stringify({}),
credentials: 'include',
// I've tried sending the param here
userName: userName,
headers: {
'Content-Type': 'application/json'
}
})
.then(() => {
this.loadUsers();
})
.catch(err => {
//error handling
});
}
In my controller I defined the route and implemented function like this:
index.create = (req, res, next) => {
let userName = req.params.userName;
console.log(userName);
user
.create(userName)
.then((response) => {
res.send(response);
})
.catch((err) => {
next(err);
});
};
router.post('/users', index.create);
And then in my service.js I write the data to my database:
create: function(userName){
userName = userName
return query(`INSERT INTO ${tableName} (user) VALUES (?, ?)`, [1, userName])
.catch(err => {
app.logger.error('[Users] failed to create a user', err.message);
return Promise.reject(new Error('failed to create user'));
});
},
I always get an undefined userName, do I have to create a route with the value at the end?

You're receiving userName as undefined, because you're sending the request with JSON-encoded data, rather than URL parameters and the same logic doesn't apply for that case.
Luckily there's an easy way to solve your problem, using expressjs body-parser package. It's very easy to use.
This is how you initialize it:
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// parse application/json
app.use(bodyParser.json())
And this is how you would read "userName" in your router function:
index.create = (req, res, next) => {
let userName = req.body.userName;
console.log(userName);
user
.create(userName)
.then((response) => {
res.send(response);
})
.catch((err) => {
next(err);
});
};
And btw, when you're calling advancedFetch, you should actually be doing this:
body: JSON.stringify({newUserName: userName}),

Yes, you can actually do it this way:
In the frontend call you can send the parameter through the body like so
body: JSON.stringify({userName: userName})
And then in your controller what you want to do is to directly access the paramter from the body:
let userName = req.body.userName;
And now it's not undefined anymore :)

Related

Parsing fetched XML with react-xml-parser

I am first fetching xml data from an external api in my backend and then trying to send that to the frontend.
Backend (server):
app.get("/api", (req, res) => {
var request = require('request');
var options = {
'method': 'GET',
'url': 'http://link',
'headers': {
}
};
request(options, function (error, response) {
console.log("TEST1")
console.log(response.body)
if (error){
console.log("TEST2")
res.status(404).write("NO LUCK");
}
console.log("TEST3")
res.status(200).write(response.body);
});
});
The xml appears in my terminal correctly. TEST1 and TEST2 do too.
Frontend (client):
import XMLParser from 'react-xml-parser';
useEffect(() => {
fetch("/api")
.then(res => res.text())
.then(data => {
var xml = new XMLParser().parseFromString(data);
console.log(xml)
})
.catch(err => console.log(err));
}, []);
Nothing appears on the console. I got the frontend code from this stack overflow post.
fetch("/api")
.then((res) => res.body)
.then((data) => console.log(data));
If I log the response body like this I get a ReadableStream object. This is the only functionality I've been implementing so far so nothing should be interfering with it. When I try different approaches I keep needing to restart React or it will load endlessly when I refresh.
When I open http://localhost:3001/api I can see the xml data I'm trying to transfer.
Still not sure why it didn't work before but it works with this.
Frontend (client):
import axios from "axios";
axios.get('/api')
.then(xml => {
console.log(xml.data);
})
Backend (server):
app.get('/api', (req, res) => {
var axios = require('axios');
var config = {
method: 'get',
url: 'http://link',
headers: {'Content-Type': 'application/xml'}
};
axios(config)
.then(function (response) {
res.json(response.data);
})
.catch(function (error) {
console.log(error);
})
})

Axios Post from react to express proxy server

I have created an express server in which I am implementing a graphQL request. The following block of code was taken from postman snippets of a successful request
const express = require("express"),
app = express(),
port = process.env.PORT || 4000,
cors = require("cors");
var axios = require('axios');
var data = JSON.stringify({
query: `mutation claimTask ($taskId: String!, $assignee: String) {
claimTask (taskId: $taskId, assignee: $assignee) {
id
name
taskDefinitionId
processName
creationTime
completionTime
assignee
variables {
id
name
value
previewValue
isValueTruncated
}
taskState
sortValues
isFirst
formKey
processDefinitionId
candidateGroups
}
}`,
variables: {"taskId":"22","assignee":"demo"}
});
var config = {
method: 'post',
url: 'http://[my_ip]/graphql',
headers: {
'Authorization': 'Bearer ey....',
'Content-Type': 'application/json',
'Cookie': 'TASKLIST-SESSION=..'
},
data : data
};
app.use(cors());
app.listen(port, () => console.log("Backend server live on " + port));
app.post("/api", (req, res) => {
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
res.send({ message: JSON.stringify(response.data) });
})
.catch(function (error) {
console.log(error);
res.send({ message: error });
});
})
Currently I am calling this request from a react app with a button like this:
Axios({
method: "POST",
url: "http://localhost:4000/api",
headers: {
"Content-Type": "application/json"
}
}).then(res => {
console.log(res.data.message);
});
For the next step I want to pass the variables from my react app instead of writing them directly as string to express. What is the right approach to achieve this?
I am using the express server to avoid cors related issues.
Any suggestions can be useful, thank you!
So you want to send an Axios POST from react. Something along those lines should work:
const handleSubmit = (e) => {
e.preventDefault();
const variables = {
taskId: ”22”,
userId: “demo”
};
axios.post("http://localhost:4000/api", variables).then((response) => {
console.log(response.status);
});
};
Inspired by https://blog.logrocket.com/understanding-axios-post-requests/

Body of request received on Express backend is different than how i sent it from React frontend

I am trying to send a post request from my React front end to my Express front end, for some reason, the object I want to recieve, is being displayed so that the object is the key of another object and the value is and empty string.
Here is my onSubmit React function
handleSubmit = event => {
event.preventDefault()
const backend = '/api/login'
fetch(backend, {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body: JSON.stringify(this.state)
})
.then(res => {
res.json()
})
.then(user => {
console.log(user)
})
.catch(err => {
console.log(err)
})
}`
And my post function on the express server
app.post("/login", (req, res) => {
console.log(req.body)
})
For example, if the object I want to send is {username: "user1", password: "password"}, when I console.log(req.body), I get { '{"username":"user1","password":"password"}': '' } in the console.
How do i fix this so i get the object that I requested?
Because it is JSON format. To parse it you can use:
JSON.parse('{"username":"user1","password":"password"}')
or JSON.parse(req.body)
The approach is fine with JSON.stringify() because it should be posted just like a string to the server. But if you want it to be an object at the backend then you have to parse it back with:
const userObj = JSON.parse(req.body.Data); // it will parse it back as an object

JWT Secure Routes in React

I am adding JWT authentication to a blog app I'm working on. On the server side (built with Nodejs) I am creating the token and sending it back with a successful login. On the client side I am saving the token in LocalStorage. When I log in and check the application tab in dev tools I can see the token. On the server route where blogs are posted to I check authentication. If the token is authenticated the blog posts to the database, but if I delete the token or change it and then make the post request the request fails, as expected.
So far so good.
What I'm confused about is how to restrict access to the page where the blog editor resides on the client. If people aren't authenticated they should not be able to access this page at all, even though if not authenticated they can't post anyway.
Login route on server:
router.post('/login', async (req, res, next) => {
const cursor = User.collection.find({username: req.body.username}, {username: 1, _id: 1, password: 1});
if(!(await cursor.hasNext())) {
return res.status(401).json({ message: 'Cannot find user with that username' });
}
const user = await cursor.next();
try {
if(await bcrypt.compare(req.body.password, user.password)) {
const token = jwt.sign({
email: user.email,
userId: user._id
}, process.env.JWT_SECRET, { expiresIn: "1h" })
return res.status(201).json({
message: 'User Authenticated',
token: token
});
} else {
return res.status(400).json({
authenticated: false,
username: req.body.username,
password: req.body.password
})
}
} catch (err) {
return res.status(500).json({ message: err })
}
});
How I'm checking the token authentication on the server:
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization;
console.log(token);
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.userData = decoded;
next();
} catch (error) {
return res.status(401).json({ message: 'Auth Failed' })
}
}
My client side login route fetch:
handleSubmit(event) {
event.preventDefault();
const formData = {
username: event.target.username.value,
password: event.target.password.value
}
fetch('http://localhost:4000/user/login', {
method: "POST",
mode: "cors",
body: JSON.stringify(formData),
headers: {
"Content-Type": "application/json"
}
})
.then(res => res.json())
.then(res => {
localStorage.setItem('authorization', res.token);
console.log(res);
})
.catch(err => console.error(err))
}
And here is my fetch call from the client on the blog posting route where the editor resides:
handleSubmit = (event) => {
event.preventDefault();
const data = new FormData(event.target);
const body = event.target.postBody.value;
const postTitle = event.target.title.value;
console.log(event.target);
console.log(data);
console.log(event.target.postBody.value);
fetch('http://localhost:4000/blog', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
"Authorization": localStorage.getItem('authorization')
},
mode: 'cors',
body: JSON.stringify({
title: postTitle,
postBody: body
})
})
.then(res => res.json())
.then(err => console.error(err))
}
So, like I said, everything is working as expected but I don't want people to be able to access the editor page if they are not authenticated. I guess I would check to see if the token exists in localstorage and then redirect? But wouldn't I also need to check to see if the token on the client can be authenticated on the server as well? So would I essentially need to post to the server to do the check whenever someone navigates to that page, or any other page I want to restrict access to? Come to think of it, if a user is already authenticated I don't want them to be able to access the login page either.
I have heard that people use Redux to manage state across components, but I really don't want to go down that road, at least not yet because this project is for learning purposes and I don't really want to start with Redux or anything else like that until I have a better grasp of React on it's own. I don't know if I need Redux or not and from what I understand, that's enough to know that I probably don't need it.
This is just such a different flow than I'm used to from PHP sessions and I'm having some trouble wrapping my head around it.
I realize that you folks may not really need to see all this code, but I also would like some more experienced eyes to see it and point out anywhere I might be making mistakes or where I could improve here.
So this is what I have come up with for now, if anyone knows a better way, I'm definitely open to suggestions.
I created a class called CheckAuth which essentially just makes a GET request to the server and sends the jwt along with it.
checkAuth.js:
class CheckAuth {
constructor() {
this.auth = false;
}
async checkLogin() {
console.log(localStorage.getItem("authorization"));
let data = await fetch('http://localhost:4000/auth', {
method: "GET",
mode: "cors",
headers: {
"Content-Type": "application/json",
"authorization": localStorage.getItem("authorization")
}
})
return data.json();
}
logout(cb) {
localStorage.removeItem('authenticated')
this.auth = false;
cb();
}
async isAuthenticated() {
const data = await this.checkLogin()
return data;
}
}
export default new CheckAuth();
Then on pages that only logged in users should see I am doing a simple check to see if they have the token and if it's valid inside of componentDidMount().
componentDidMount() {
const check = checkAuth.isAuthenticated();
console.log(check);
check.then(res => {
console.log(res);
if(res.authenticated !== true) {
this.props.history.push("/login");
}
})
.catch(err => { console.error(err) })
}

Partial observable handling in Angular 2

I have a component which a login form: loginForm which calls my api:
onLogin(): void {
let username = this.loginForm.value.username;
let password = this.loginForm.value.password;
this.api.login(username, password);
.map(res => res.json())
.subscribe(
this._processData,
);
}
There is a service which calls an api: loginService.
login(username: string, password: string): Subscription<any> {
let headers = new Headers();
headers.append("Content-Type", "application/json");
return this.http.put("https://myapi.com", JSON.stringify({ username: username, password: password}), {
headers: headers
})
.map(res => res.json())
.subscribe(
data => console.log(data),
err => console.log(err),
() => console.log("Done")
);
}
What I want is the error to be handled inside the loginService. But the result back to the onLogin method.
I tried a variety of mapping handlers (as shown) in the onLogin but could not get it to work. Any thoughts about what I might do wrong?
In fact, you define a callback within the subscribe method to handle error in the login method (the second parameter). If you want to let errors to be propagated into your onLogin method you need to remove this callback (and even the call of subscribe).
return this.http.put("https://myapi.com",
JSON.stringify({ username: username, password: password}), {
headers: headers
})
.map(res => res.json());
If you want to log the error and then propagate it you can leverage the Observable.throw method:
return this.http.put("https://myapi.com",
JSON.stringify({ username: username, password: password}), {
headers: headers
})
.map(res => res.json())
.catch(err => {
console.log(err);
return Observable.throw(err);
});
In this case you need to add a callback to handle the error in the onLogin method:
onLogin(): void {
let username = this.loginForm.value.username;
let password = this.loginForm.value.password;
this.api.login(username, password)
.subscribe(
(data) => {
// Success
this._processData(data)
},
(err) => {
// Error
}
);
}
You can notice that there is a catch operator on Observable to catch error and in the case of errors, the map operator isn't called.
These answers could give you more details:
Angular2 http observables - how to work with undefined type
Observable Map function not running (Angular2, http)
Hope it helps you,
Thierry
You can define your error handler in your service, and just pass the error from component where you subscribe.
// component
onLogin(): void {
let username = this.loginForm.value.username;
let password = this.loginForm.value.password;
this.api
.login(username, password)
.subscribe(
this._processData, // success
this.api.onError // error
// if above doesn't work this should:
// error => this.api.onError(error)
);
}
_processData(...data) { console.log(data); }
// service
login(username: string, password: string): Subscription<any> {
let headers = new Headers();
headers.append("Content-Type", "application/json");
return this.http
.put("https://myapi.com", JSON.stringify({ username: username, password: password}), { headers: headers })
.map(res => res.json())
}
onError(...args) { console.log(args); }

Resources