I am modelling the auth layer for a simple react/redux app. On the server side I have an API based on the devise_token_auth gem.
I am using fetch to post a sign in request:
const JSON_HEADERS = new Headers({
'Content-Type': 'application/json'
});
export const postLogin = ({ email, password }) => fetch(
`${API_ROOT}/v1/auth/sign_in`, {
method: 'POST',
headers: JSON_HEADERS,
body: JSON.stringify({ email, password })
});
// postLogin({ email: 'test#test.it', password: 'whatever' });
This works, and I get a 200 response and all the data I need. My problem is, information is divided between the response body and headers.
Body: user info
Headers: access-token, expiration, etc.
I could parse the JSON body this way:
postLogin({ 'test#test.it', password: 'whatever' })
.then(res => res.json())
.then(resJson => dispatch(myAction(resJson))
But then myAction would not get any data from the headers (lost while parsing JSON).
Is there a way to get both headers and body from a fetch Request?
Thanks!
I thought I'd share the way we finally solved this problem: by just adding a step in the .then chain (before parsing the JSON) to parse the auth headers and dispatch the proper action:
fetch('/some/url')
.then(res => {
const authHeaders = ['access-token', 'client', 'uid']
.reduce((result, key) => {
let val = res.headers.get(key);
if (val) {
result[key] = val;
}
}, {});
store.dispatch(doSomethingWith(authHeaders)); // or localStorage
return res;
})
.then(res => res.json())
.then(jsonResponse => doSomethingElseWith(jsonResponse))
One more approach, inspired by the mighty Dan Abramov (http://stackoverflow.com/a/37099629/1463770)
fetch('/some/url')
.then(res => res.json().then(json => ({
headers: res.headers,
status: res.status,
json
}))
.then({ headers, status, json } => goCrazyWith(headers, status, json));
HTH
Using async/await:
const res = await fetch('/url')
const json = await res.json()
doSomething(headers, json)
Without async/await:
fetch('/url')
.then( res => {
const headers = res.headers.raw())
return new Promise((resolve, reject) => {
res.json().then( json => resolve({headers, json}) )
})
})
.then( ({headers, json}) => doSomething(headers, json) )
This approach with Promise is more general. It is working in all cases, even when it is inconvenient to create a closure that captures res variable (as in the other answer here). For example when handlers is more complex and extracted (refactored) to separated functions.
My solution for the WP json API
fetch(getWPContent(searchTerm, page))
.then(response => response.json().then(json => ({
totalPages: response.headers.get("x-wp-totalpages"),
totalHits: response.headers.get("x-wp-total"),
json
})))
.then(result => {
console.log(result)
})
If you want to parse all headers into an object (rather than keeping the Iterator) you can do as follows (based on Dan Abramov's approach above):
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => (res.headers.get('content-type').includes('json') ? res.json() : res.text())
.then(data => ({
headers: [...res.headers].reduce((acc, header) => {
return {...acc, [header[0]]: header[1]};
}, {}),
status: res.status,
data: data,
}))
.then((headers, status, data) => console.log(headers, status, data)));
or within an async context/function:
let response = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await (
response.headers.get('content-type').includes('json')
? response.json()
: response.text()
);
response = {
headers: [...response.headers].reduce((acc, header) => {
return {...acc, [header[0]]: header[1]};
}, {}),
status: response.status,
data: data,
};
will result in:
{
data: [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}],
headers: {
cache-control: "public, max-age=14400"
content-type: "application/json; charset=utf-8"
expires: "Sun, 23 Jun 2019 22:50:21 GMT"
pragma: "no-cache"
},
status: 200
}
depending on your use case this might be more convenient to use. This solution also takes into account the content-type to call either .json() or .text() on the response.
Related
I am trying to send the param name in the Cloud Function managed by Firebase using POST method, I've read quite a few documentation, and no matter what I try it always returns undefined. Also is it safe to use this for sensitive data?
Client-Side
fetch(`https://<<APP-NAME>>.cloudfunctions.net/addCardForExistingCustomer`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
JSON.stringify(body: {'name': 'Name'})
})
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(err => console.error(err));
Server-side (Firebase Cloud-Functions)
exports.addCardForExistingCustomer = functions.https.onRequest(async (request, response) => {
let name = await request.body.name
response.json({
response: `${name}`
})
})
I'm new to the FetchAPAI. For my first API project, I'm currently using the ClimatIQ API and following the steps in their Quickstart guide. Even if I've already signed up and received an authentication key from them, I keep getting the ff error from them:
POST https://beta2.api.climatiq.io/estimate 400
{error: 'invalid_request', message: 'Error parsing the request body.'}
Take note in the guide, the code is in Curl, and I did my best trying to convert that code into a fetchAPI request on JavaScript.
const fetchData = async (url) => {
await fetch(url, {
method: "POST",
headers: { Authorization: "Bearer MY_API_KEY" },
data: {
emission_factor: "electricity-energy_source_grid_mix",
parameters: {
energy: 4200,
energy_unit: "kWh",
},
},
//body: JSON.stringify(data),
})
.then((response) => response.json())
.then((json) => console.log(json))
.catch((err) => console.log(`Here's the error ${err}`));
};
fetchData("https://beta2.api.climatiq.io/estimate");
From time to time, it also shows a "header is not defined" even if I already put in the authentication key they gave me in the "MY_API_KEY" part of my codebase. Is this an error with their server?
try this:
const data = {
emission_factor: "electricity-energy_source_grid_mix",
parameters: {
energy: 4200,
energy_unit: "kWh",
},
};
const fetchData = async(url) => {
await fetch(url, {
method: "POST",
headers: {
Authorization: "Bearer MY_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then((response) => response.json())
.then((json) => console.log(json))
.catch((err) => console.log(`Here's the error ${err}`));
};
fetchData("https://beta2.api.climatiq.io/estimate");
I`ve been struggeling this a quiet a lot and would appreciate help. I want to be able to print the title value in the console with console.log.
`<script>
const url = "***";
const opts = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: "*[_type == 'post']{ _updatedAt, _createdAt, title}[0]" })
};
fetch(url, opts)
.then(res => res.json())
.then(console.log)
.catch(console.error);
</script>`
Results I get in console now:
{ms: 6, query: "*[_type == 'post']{ _updatedAt, _createdAt, title}[0]", result: {…}}
ms: 6
query: "*[_type == 'post']{ _updatedAt, _createdAt, title}[0]"
result:
title: "Title2"
_createdAt: "2020-07-02T16:28:49Z"
_updatedAt: "2020-07-05T13:16:27Z"
__proto__: Object
__proto__: Object
You want to access the title field of the result object. Try this:
fetch(url, opts)
.then(res => res.json())
.then(console.log)
.catch(console.error);
If result is an object, I'm guessing you need to specify result in the log
fetch(url, opts)
.then(res => res.json())
.then(data => console.log(data.result))
.catch(console.error);
Using redux in React.js I get the most starred repositories in the last 30 days, now I wanna use the pagination that github api provides but to do so I have to use the headers in the response, how can I do that, how can I change my code to get the headers from the response, this is the function that gets the response:
import getDate from './getDate';
export function fetchRepos() {
return function(dispatch) {
dispatch({
type: "FETCH_REPOS_REQUEST",
});
return fetch(
"https://api.github.com/search/repositories?q=created:>" +
getDate() +
"&sort=stars&order=desc",
)
.then(response => response.json().then(body => ({response, body})))
.then(({response, body}) => {
if (!response.ok) {
dispatch({
type: "FETCH_REPOS_FAILURE",
error: body.error,
});
} else {
dispatch({
type: "FETCH_REPOS_SUCCESS",
repos: body.items,
});
}
});
};
}
Please help, thank you!
I like to assemble a response object that includes the headers as an object for fetch calls like so:
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => (res.headers.get('content-type').includes('json') ? res.json() : res.text())
.then(data => ({
headers: [...res.headers].reduce((acc, header) => {
return {...acc, [header[0]]: header[1]};
}, {}),
status: res.status,
data: data,
}))
.then(response => console.log(response)));
in your case you could then simply get the headers with response.headers in the last .then().
but technically you can access the headers with res.headers.get('<header.name>').
I'm putting together a React app that consumes data from a Node/Express REST API which is currently on my local machine. I've got a simple res.json returning a Sequelize object, and I'm accessing it through a service I made. Obviously, I'm going to be putting the object in state eventually, but I'm currently having difficulty accessing the values.
const options = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: JSON.stringify({email: "matthewharp#gmail.com", password: "M1nerals"})
};
fetch('http://localhost:3000/users/sign_in', options)
.then(response => console.log(response.json()));
I'm getting the results in the console, but they're stuck in the [[PromiseValue]].
I must be missing some kind of async step, but I'm not sure what.
The json method returns a promise, which you also need to await. So do:
fetch('http://localhost:3000/users/sign_in', options)
.then(response => response.json())
.then(obj => console.log(obj));
You're having this error because response.json() return a promise.
you need to do
fetch('http://localhost:3000/users/sign_in', options)
.then(response => response.json())
.then(res => console.log(res));
You need to return the promise from the fetch call or else you need to act on it in the then for the json promise.
const options = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: JSON.stringify({email: "matthewharp#gmail.com", password: "M1nerals"})
};
return fetch('http://localhost:3000/users/sign_in', options)
.then(response => {
console.log(response.json())
return response.json()
}
);
or...
const options = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: JSON.stringify({email: "matthewharp#gmail.com", password: "M1nerals"})
};
fetch('http://localhost:3000/users/sign_in', options)
.then(response => {
console.log(response.json())
response.json().then( result => {
// whatever you're doing with the data here.
}
);
Take a look at the fetch api:
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
You need a separate then chained to take the json data once ready, and it will give you the values.
('http://localhost:3000/users/sign_in', options)
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(JSON.stringify(myJson));
});