I'm successfully authenticating with the server, sending my graphql request and receiving a response. However the response isn't quite right.
This is just a simple nodejs, axios, graphql situation. I can get a correct respose via postman or curl but not with node. I've tried fetch as well to no avail. Am I doing something wrong? This is for the producthunt api, the api explorer is located at here
require('dotenv').config()
const axios = require("axios");
const PH_KEY = process.env.PH_KEY;
const PH_SECRET = process.env.PH_SECRET;
const BASE_URL = process.env.BASE_URL;
const AUTH_URL = process.env.AUTH_URL;
const GRAPHQL_URL = process.env.GRAPHQL_URL;
const PH = axios.create({
baseURL: BASE_URL,
responseType: 'json',
headers: {
// 'Accept': 'application/json, text/plain, gzip, deflate, */*',
'Content-Type': 'application/json'
},
})
let TOKEN = 'Bearer '
const getAuth = async () => {
return await PH({
url: AUTH_URL,
method: "post",
data: {
client_id: `${PH_KEY}`,
client_secret: `${PH_SECRET}`,
grant_type: "client_credentials"
}
})
.then((res) => {
console.log('auth status ', res.status)
return res.data.access_token
})
.then((res) => {
TOKEN += res
console.log(TOKEN)
})
.catch((err) => {
console.log(err)
})
}
const getHunt = async () => {
await getAuth()
PH.defaults.headers.common['Authorization'] = TOKEN
return await PH({
url: GRAPHQL_URL,
method: 'post',
data:
{
query: `
query posts {
posts(order: RANKING, first: 1) {
edges {
node {
name
votesCount
user {
name
}
createdAt
}
}
}
}
`
}
})
.then((res) => {return res})
.then((res) => {
console.log(res.data)
return res.data
})
.catch((err) => {
console.log(err)
})
}
const Main = async () => {
await getHunt()
}
Main()
This is the output I receive in node:
[Running] node "/Users/fireinjun/Work/YAC/yac-ph-tophuntfunction/app.js"
auth status 200
Bearer #################################################
{ data: { posts: { edges: [Array] } } }
Here's what I'm expecting:
{
"data": {
"posts": {
"edges": [
{
"node": {
"name": "Trends by The Hustle",
"votesCount": 125,
"user": {
"name": "Jack Smith"
},
"createdAt": "2019-06-04T07:01:00Z"
}
}
]
}
}
}
I was accessing the data incorrectly! Apparently I needed to see the res.data.data.posts.edges[0]
Related
I've got a client-side app that uses apollo/client and I'have a few server-side function where also use the client to do some light queries and mutations.
I've recently implemented jwt integration and now my server-side calls fail because of what I presume is missing headers.
here is my client setup:
const wsLink = new GraphQLWsLink(
createClient({
url: process.env.API_URL_WS,
webSocketImpl: isNode ? ws : null,
connectionParams: async () => {
const { id_token } = await auth.getSession();
return {
headers: {
Authorization: `Bearer ${id_token}`,
},
};
},
})
);
const authLink = setContext(async (_, { headers }) => {
const { id_token } = await auth.getSession();
return {
headers: {
...headers,
Authorization: `Bearer ${id_token}`,
},
};
});
const httpLink = new HttpLink({
uri: process.env.API_URL_HTTP,
});
const splitLink = split(
({ query, ...rest }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
authLink.concat(httpLink)
);
export const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
and here is how I am using it in the server-side function to make a mutation
await client
.mutate({
mutation: gql`
mutation InsertSubscriptionId(
$id: uuid!
$stripe_subscription_id: String!
) {
update_user_by_pk(
pk_columns: { id: $id }
_set: { stripe_subscription_id: $stripe_subscription_id }
) {
id
}
}
`,
variables: {
id: session.metadata.hasura_user_id,
stripe_subscription_id: session.subscription,
},
context: {
headers: {
Authorization: req.cookies.access_token,
},
},
})
.then((result) => console.log(result))
.catch(console.log);
this call fails even if I use the users access token or apply an admin token for access to the DB.
As a workaround I've resorted to using fetch for this mutation (which works). Any idea why headers are not respected?
My workaround with admin access secret:
fetch(process.env.API_URL_HTTP, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-hasura-admin-secret": process.env.GATSBY_HASURA_ADMIN_SECRET,
},
body: JSON.stringify({
query: `
mutation InsertSubscriptionId(
$id: uuid!
$stripe_subscription_id: String!
) {
update_user_by_pk(
pk_columns: { id: $id }
_set: { stripe_subscription_id: $stripe_subscription_id }
) {
id
}
}
`,
variables: {
id: session.metadata.hasura_user_id,
stripe_subscription_id: session.subscription,
},
}),
})
.then((res) => res.json())
.then((result) => console.log(result));
In this case the session was lost after a few jumps between my app and payment provider. As user can't be authenticated when session is lost, I've resorted to authenticate the admin user for this ssr function
I'm trying to send different request in interceptor
I want to send accessToken in request header authorization for every request except one case
so I write this code in interceptor request.use
config.headers = {authorization: `Bearer ${accessToken}`};
but if this error occurred
error.response.data.code === 'expired'
I want to send refreshtoken in header authorization not accesstoken
so I write this code in interceptor.response.use.error
const { data } = await axios.post(
`${Config.API_URL}/user/refreshToken`,
{},
{ headers: { authorization: `Bearer ${refreshToken}` } }
);
this is my code
useEffect(() => {
axios.interceptors.request.use(async (config: any) => {
const accessToken = await EncryptedStorage.getItem("accessToken");
config.headers = { authorization: `Bearer ${accessToken}` };
return config;
});
axios.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const {
config,
response: { status },
} = error;
if (status === 419) {
if (error.response.data.code === "expired") {
const originalRequest = config;
const refreshToken = await EncryptedStorage.getItem("refreshToken");
const { data } = await axios.post(
`${Config.API_URL}/user/refreshToken`,
{},
{ headers: { authorization: `Bearer ${refreshToken}` } }
);
return axios(originalRequest);
}
}
return Promise.reject(error);
}
);
}, [dispatch]);
how can i fix my code?
if i use my code if error.response.data.code === 'expired'
the headers.authorization accesstoken is still being requested.
Make it so your request interceptor only sets a default authorization header without overriding anything already present
axios.interceptors.request.use(async (config) => {
const accessToken = await EncryptedStorage.getItem("accessToken");
return {
...config,
headers: {
authorization: `Bearer ${accessToken}`,
...config.headers
}
}
});
You could also avoid making the getItem() request entirely which might save a little time
axios.interceptors.request.use(async (config) => {
if (!config.headers.authorization) {
config.headers.authorization = `Bearer ${await EncryptedStorage.getItem("accessToken")}`
}
return config;
});
the code below is the first request for get list data
mounted() {
this.getList()
},
methods: {
handleClick(row) {
console.log(row.id)
console.log(row.url)
this.$router.push({ name: 'Main', query: { id: row.id } })
},
getList() {
axios
.get('/api/v1/requests/all', {
params: {
userId: this.$store.state.userInfo.id,
},
})
.then(response => {
let moment = require('moment')
for (var item of response.data.data) {
item.createdAt = moment(item.createdAt).format(
'YYYY-MM-DD HH:mm:ss',
)
}
this.items = response.data.data
})
.catch(error => {
console.log(error)
})
}
my interceptor
axios.interceptors.response.use(
function (response) {
return response
},
async function (error) {
const originalRequest = error.config
if (error.response.status === 401 && !originalRequest._retry) {
error.response.config._retry = true
sessionStorage.removeItem('access-token')
let headers = {
grant_type: 'refresh_token',
Authorization: sessionStorage.getItem('refresh-token'),
}
axios
.post('/api/v1/users/refresh_token', {}, { headers: headers })
.then(response => {
let token = response.data.data
sessionStorage.setItem('access-token', token)
originalRequest.headers['Authorization'] = token
originalRequest.headers['grant_type'] = 'grant_type'
return axios.request(originalRequest)
})
.catch(error => {
console.log(error)
alert('blablabla.')
})
}
return Promise.reject(error)
},
)
the flow is i understand
1.token expired
2.move to list page
3.mounted hook is request data
4.getList -> axios get('~~request/all')
5.interceptor->axios post('~~~refresh_token')
6.re request with new token(request/all)
7.re request is 200, but not update list page
i'd really appreciate your help :)
Seems like you need to return the second request (await for result and return). Right now the result of second request seems to be ignored
axios.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
error.response.config._retry = true;
sessionStorage.removeItem("access-token");
let headers = {
grant_type: "refresh_token",
Authorization: sessionStorage.getItem("refresh-token"),
};
const [secondError, res] = await axios // await here
.post("/api/v1/users/refresh_token", {}, { headers: headers })
.then(async (response) => {
let token = response.data.data;
sessionStorage.setItem("access-token", token);
originalRequest.headers["Authorization"] = token;
originalRequest.headers["grant_type"] = "grant_type";
return [null, await axios.request(originalRequest)];
})
.catch((err) => {
console.log(err);
alert("blablabla.");
return [err, null];
});
// Handle here
if(secondError) {
return Promise.reject(secondError);
}
return Promise.resolve(res)
}
return Promise.reject(error);
}
);
The above solution worked for me perfectly. here's the modified code that I used for my requirement.
export default function axiosInterceptor() {
//Add a response interceptor
axios.interceptors.response.use(
(res) => {
// Add configurations here
return res;
},
async function (error) {
const originalRequest = error.config;
let secondError, res
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
[secondError, res] = await axios({
method: "POST",
url: `${baseURL}/account/token/refreshtoken`,
withCredentials: true,
headers: { "Content-Type": "application/json" },
})
.then(async (response) => {
//handle success
return [null, await axios.request(originalRequest)];
}).catch(function (error) {
console.error(error)
});
}
if (secondError) {
return Promise.reject(secondError);
}
return Promise.resolve(res)
}
);
}
I am using third party API. The way it works is:
I send post request then Token is returned in response.
Then i use that Token to check status. Afterwards, report is returned in response
In postman, i make both calls separately and it is working, but in Axios I have 1 async function and 2 await Promises.
Postman(NodeJs - Axios) looks like this:
For getting Token:
var data = JSON.stringify({
"security": {
"pLogin": "a",
"pPassword": "io"
},
"data": {
"pHead": "005",
"pCode": "00433",
"pLegal": 1,
"pClaimId": "z4LpXRWZKecSnL-FQtgD",
"pReportId": 8,
"pReportFormat": 1
}
});
var config = {
method: 'post',
url: 'http://10.22.50.10/report/',
headers: {
'Content-Type': 'application/json'
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
For getting Report with the token:
var data = JSON.stringify({
"data": {
"pHead": "005",
"pCode": "00433",
"pToken": "kgqjismxdrpjnjaqnlnbmovcsvnkarfd",
"pClaimId": "z4LpXRWZKecSnL-FQtgD",
"pReportFormat": 1
}
});
var config = {
method: 'post',
url: 'http://10.22.50.10/report/status',
headers: {
'Content-Type': 'application/json'
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
My async function with Axios:
/* 1. Searching for client in database (Full name is used, but can be changed)*/
const client = await client.findOne({
name: body.name,
family_name: body.family_name,
patronymic: body.patronymic
});
if (!client) {
return res.status(401).json({ message: "Client is not registered" });
}
/* 2. If client was found in database, make an API call */
let credit_report;
try{
credit_report = await axios.post(
'http://10.22.50.10/report',
{
security: {
pLogin: 'a',
pPassword: 'io',
},
data: {
pHead: "005",
pCode: "00433",
pLegal: 1,
pClaimId: client.claim_id,
pReportId: 8,
pReportFormat: 1
}
},
{
headers: {
'content-type': 'application/json'
}
}
);
}catch(err){
return res.status(400).json({errorMessage: err.message})
}
// await new Promise(resolve => setTimeout(resolve, 3000));
if(!credit_report.data.data.token) return res.status(400).json({message: credit_report.data});
const credit_report_status = await axios.post(
'http://10.22.50.10/report/status',
{
data: {
pHead: "005",
pCode: "00433",
pToken: credit_report.data.data.token,
pClaimId: client.claim_id,
pReportFormat: 1
}
},
{
headers: {
'content-type': 'application/json'
}
}
);
console.log(credit_report_status)
if(credit_report_status.data.data.result == '05000') return res.status(200).json({ message: 'Client fetched.', clientData64: credit_report_status.data.data.reportBase64});
else return res.status(400).json({message: credit_report_status.data})
When I am using Postman to check my module, it is saying Error 400 Bad Request
I want to fetch a details of the one student I wrote the method for that . It working with postman but not working when i connect it with frontend
frontend controller for fetching student details
export const getStudent = (studentId, token) => {
return fetch(`${API}/student/${studentId}`, {
method: "GET",
headers: {
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
setting the values
const [student, setStudent] = useState({
fullName: "",
admissionNumber: "",
rollNumber: "",
age: "",
gender: "",
faculty: "",
email: "",
});
const preload = (studentId) => {
getStudent(user._id, token, studentId).then((student) => {
if (student?.error) {
setStudent(console.log(student.error));
} else {
setStudent(student);
}
});
};
useEffect(() => {
preload();
}, []);
backend
exports.getStudent = (req, res) => {
return res.json(req.student);
};
fetching status is ok but response is null
Updated Code
const preload = (studentId) => {
getStudent(studentId, token).then((data) => {
if (data.error) {
setStudent(console.log(data.error));
} else {
setStudent(student);
}
});
};
preload needs only two parameters userid and token
In useEffect it needs to pass the studentId to get the details
useEffect(() => {
preload(match.params.studentId);
}, []);