How to append a JSON descriptor to res.sendFile() in Express? - node.js

After passport authenticating success i want to pass the req.user data to the component
using res.sendFile().
I found this answer Here
But i don't know how to read the options param on the component in componentDidMount()
Can any one help me ?

There is no standard way to send mixed JSON + file content by using just the sendFile() method. As a workaround, you can send your JSON data as a custom response header:
const options = {
headers: {
'Access-Control-Expose-Headers': 'User',
'User': JSON.stringify(req.user),
}
};
res.sendFile(path.join(__dirname, '../assets', 'index.html'), options);
Note that the Access-Control-Expose-Headers header is required, in order for your client to always be able to access the extra header.
Then, assuming you are using axios on the frontend:
axios.get(YOUR_URL, response => {
const user = JSON.parse(response.headers['User']);
console.log('User Object', user);
});
With fetch:
response.headers.get('User');

Related

Node Axios Create Global Token Variable for Use in Separate Variable Header

Using Node & Axios
What I Want to Do
In my server.js file, I want to call an api for a token that always changes, use axios (or other solution) to create a global token variable, and provide the global token to an app.get request header within an object, again, all within my server.js file.
What I'm Trying
I get the data using...
var data = '<tsRequest>\r\n\t<credentials name="name" password="pword">\r\n\t\t<site contentUrl="" />\r\n\t</credentials>\r\n</tsRequest>';
var config = {
method: 'post',
url: 'https://url.uni.edu/api/3.13/auth/signin',
headers: {
'Content-Type': 'text/plain'
},
data : data
};
I try to create the global token variable (this is where I'm struggling)...
const token= axios(config)
.then((response) => {
console.log(response.data.credentials.token);
}).catch((err) => {
console.log(err);
});
console.log(token)
I have a functioning app.get request where instead of manually providing the token, I want to use the const token...
app.get('/gql', async (req, res) => {
var someObject = {
'method': 'POST',
'url': 'https://diffurl.uni.edu/api/metadata/graphql',
'headers': {
'X-Some-Auth': token,
'Content-Type': 'application/json'
},
The Current Results
What I have for the var data = and the var config = and the axios(config) all work to return the token via console.log, but I have 2 issues using axios.
The Axios Issues
In my hopes of creating a global token variable, I only understand how to get a console.log result instead of returning a 'useful data object'.
In just about every piece of documentation or help found, the example uses console.log, which is not a practical example for learners to move beyond just returning the data in their console.
What do I need to provide in axios to create a global token object in addition to or instead of console.log?
I realize 1. is my current blocker, but if I run my app, I get the following:
Promise { <pending> }
Express server running on port 1234
abc123 [the console.logged token via axios]
I'm not sure what the Promise { <pending> } means, how do I fix that?
Beyond The Axios Issues
If the axios problem is fixed, am I passing the const token correctly into my app.get... var someObject... headers?
Thank you for any help you can provide.
This is what Axios interceptors are for.
You can create an Axios instance with an interceptor that waits for the token request to complete before adding the token to the outgoing request.
A response interceptor can be added to handle 401 statuses and trigger a token renewal.
const data = "<tsRequest>...</tsRequest>";
let renew = true;
let getTokenPromise;
const renewToken = () => {
if (renew) {
renew = false; // prevent multiple renewal requests
getTokenPromise = axios
.post("https://url.uni.edu/api/3.13/auth/signin", data, {
headers: {
"content-type": "text/plain", // are you sure it's not text/xml?
},
})
.then((res) => res.data.credentials.token);
}
return getTokenPromise;
};
const client = axios.create();
// Request interceptor to add token
client.interceptors.request.use(async (config) => ({
...config,
headers: {
"X-Some-Auth": await renewToken(),
...config.headers,
},
}));
// Response interceptor to handle expiry
client.interceptors.response.use(
(res) => res,
(error) => {
if (error.response?.status === 401) {
// Auth expired, renew and try again
renew = true;
return client(error.config);
}
return Promise.reject(error);
}
);
// if putting this in a module...
// export default client;
The first time you try to make a request, the token will be retrieved. After that, it will continue to use the last value until it expires.
if you want to create a to send the token with every request in axios you should create a custom axios instance or change the global axios default
you will find the way to do it here, about promise problem you need to resolve it using .then
this how i think you should do it
// first create axios instance
// you can set config defaults while creating by passing config object see the docs
const instance = axios.create();
// then get the token from API
axios(config).then(response=>{
instance.defaults.headers.common["header you want to set"]=response.data.credentials.token
});
// then use the instance to make any request you want that should have the token

How can I use Axios to access the JSON data within a response sent using Expressjs?

I'm creating a web application that generates a pdf on a server then sends it to the client for display within the browser.
The client is using Vuejs / Axios to send a POST request. Afterwards, The server is receiving it with Expressjs, generating a unique PDF, converting the file to a base64 value then sending it back as a response.
I cannot seem to get the response correct. When I attempt to display response.data.pdfData within the client I get undefined in the console. I can see that there is indeed a response with the key and value pair using inspection tools within the Network tab under the Preview section but cannot seem to access it.
// FILE: ./Client/src/App.vue
submit(personalInfo) {
this.cardInfo.personalInfo = personalInfo;
console.log('Sending POST preview_card...');
axios({
url: 'http://localhost:5000/api/preview_card',
method: 'POST',
responseType: 'blob',
data: {
cardInfo: this.cardInfo,
},
}).then((response) => {
console.log(response.data.pdfData);
});
},
// FILE: ./Server/app.js
app.post('/api/preview_card', (req, res) => {
// Generate pdf
const doc = new jsPDF('p');
doc.setFontSize(40);
doc.text(req.body.cardInfo.templateInfo, 100, 100);
doc.save('response.pdf');
// Convert pdf to base64
var tempFile = path.resolve(__dirname, './response.pdf');
var pdfBase64 = fs.readFileSync(tempFile).toString('base64');
res.setHeader('Content-Type', 'application/json');
return res.send(JSON.stringify({ pdfData: pdfBase64 }));
});
I find it necessary to serve the pdf this way due to my client's compnents as well as to enforce a level of data coherency between concurrent users.

Why i can not retrieve data from req.body

What i want to do is to read the property name of the request i send to my express.js server.Here is how i pass the json data to a post request.
document.querySelector('#checkout').onsubmit= async e =>{
const form = new FormData(document.querySelector('#checkout'))
let user = createUserInfo(form),
order = {
name: "Test_Name"
}
fetch("/checkout/create-order", {
method: "POST",
mode: "same-origin",
redirect: 'manual',
headers:{
"Content-Type": "application/json"
},
body: JSON.stringify(
{
order: order,
user: {
name:"test_Name"
}
}
)
}).then(res=>{
res.ok
}).then(data=>{
console.log(data)
})
}
And this is how i read it using express.js:
app.use(express.json());
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({ extended: false }));
const YOUR_DOMAIN = 'http://localhost:4242/checkout.html';
app.post('/checkout/create-order', async (req, res) => {
console.log(req.body.order.name)
}
When i try to read the name property i get an error.
C:\xampp\htdocs\server.js:9
console.log(req.body.order.name)
^
TypeError: Cannot read properties of undefined (reading 'name')
Add e.preventDefault() to the beginning of the onsubmit handler.
By default, when the user clicks a form submit button, the browser will send a URL encoded POST request to the URL defined in the form's action attribute (or if there is no action, the current page).
<form action="/somewhere_else"></form>
Your express code is seeing the request sent from the browser, which doesn't have any of the values you defined in the fetch request.
This also causes the page to reload and interrupt your fetch request code before its sent. By adding event.preventDefault() you suppress this behavior and your code should run as expected.
P.S.
You don't need to use both express.json() and bodyparser.json(). Body-parser is included with Express, so both of those middlewares are doing the same thing. You can also use express.urlencoded() instead of bodyparser.urlencoded().
Your server-side code is correct in my opinion, but a small tip, you don't need to use bodyparser.json() and express.json() both do the same thing.
just chose one and stick to it.

Axios - Prevent sending JWT token on external api calls

I'm building a fullstack app with nuxt + express and I have finally managed to include an authentication between my frontend/backend with passport and jwt.
I want to make additional api requests to my own github repo for fetching the latest releases (so a user gets a information that an update exists). This requets failed with a "Bad credentials" messages. I think this happens because my jwt token is sent with it (I can see my token in the request header).
My question is, is it possible to prevent axios from sending my JWT token in only this call? First, to make my request work and second, I don't want the token to be sent in external requests.
Example:
const url = 'https://api.github.com/repos/xxx/releases/latest'
this.$axios.get(url)
.then((res) => {
this.latestRelease = res.data
}).catch((error) => {
console.error(error)
})
transformRequest
You can override the Authorization for a specific call by passing an options object to your get request and transforming your request headers:
const url = 'https://api.github.com/repos/xxx/releases/latest';
this.$axios.get(url, {
transformRequest: (data, headers) => {
delete headers.common['Authorization'];
return data;
}
})
.then((res) => {
this.latestRelease = res.data;
}).catch((error) => {
console.error(error);
})
As explained in their GitHub readme:
transformRequest allows changes to the request data before it is sent to the server.
This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE'.
The last function in the array must return a string or an instance of Buffer, ArrayBuffer,
FormData or Stream.
You may modify the headers object.
Creating a specific instance
You can create an instance of axios for different scenarios. This allows you to separate your axios calls that require an authorization header and those who don't. Each instance has its own 'global' options:
const axiosGitHub = axios.create({
baseURL: 'https://api.github.com/',
timeout: 1000,
headers: {}
});
// won't include the authorization header!
const data = await axiosGithub.get('repos/xxx/releases/latest');
You could use this answer to have several instances of axios: https://stackoverflow.com/a/67720641/8816585
Or you could also import a brand new axios and use it locally like this
<script>
import axios from 'axios'
export default {
methods: {
async callFakeApi() {
const result = await axios.get('https://jsonplaceholder.typicode.com/todos/1')
console.log('result', result)
},
}
}
</script>
Axios interceptors as mentionned by Thatkookooguy are another solution!

How can I sent some data (may be a json data) from Express backend to React client through response header?

I'm using the github oauth to authenticate user,so during the setup I used redirect route as my backend url (eg :http://localhost:4000/home).
After getting the token to that route,I perform a http request to url
(https://github.com/login/oauth/access_token?client_id=${clientID}&client_secret=${clientSecret}&code=${requestToken})
In order to get the access token of user.
My question is after I receive this access token,I pass this to front-end just as a parameter in url(which is visible to user).
Eg: res.redirect(http://localhost:3000/home/${accessToken})
So I just want to make it invisible to user by passing it through response header. How can I do this????
// Declare the redirect route
app.get('/home', (req, res) => {
// The req.query object has the query params that
// were sent to this route. We want the `code` param
const requestToken = req.query.code
axios({
// make a POST request
method: 'post',
// To the Github authentication API, with the client ID, client secret and request token
url: `https://github.com/login/oauth/access_token?client_id=${clientID}&client_secret=${clientSecret}&code=${requestToken}`,
// Set the content type header, so that we get the response in JSON
headers: {
accept: 'application/json'
}
}).then((response) => {
// Once we get the response, extract the access token from the response body
const accessToken = response.data.access_token
// redirect the user to the welcome page, along with the access token
res.redirect(`http://localhost:3000/home/${accessToken}`)
})
})

Resources