Rejected promise with SyntaxError: Unexpected token i in JSON at position 0 when fetching raw file from Gitlab - javascript

I'm trying to fetch a raw file from Gitlab repository, following official documentation.
The functions are the following:
methods: {
async getProjects(url, method, payload) {
const token = this.$store.getters.token
const headers = {
'Content-Type': 'application/json',
'Private-Token': token
}
const response = await fetch(url, {
method: method,
headers: headers,
body: JSON.stringify(payload)
})
return response.json()
},
[...]
async addNewProject() {
const payload = {
"name": "newProjectName",
"namespace_id": 12,
"description": "description"
}
this.getProjects("https://gitlab.example.com/api/v4/projects/", "POST", payload)
.then(data => {
console.log(data.id)
})
.catch((e) => {
console.error(e)
})
let rawFile = null
try {
rawFile = await JSON.parse(this.getProjects("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master", "GET"))
} catch (e) {
rawFile = this.getProjects("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master", "GET")
}
console.log(rawFile)
}
}
Logging the rawFile shows a pending Promise object with rejected state and the SyntaxError as in the title.
Is it possible that the raw format of the file is causing this error? If so, how to prevent it?

There are a few things going on.
gitlab.example.com doesn't exist, so it's difficult to know what results you actually get
You are passing the promise to JSON.parse. You need to use await or then to use the response:
The end of your getProjects calls response.json() which will parse the json. You don't need to parse it again.
How about using:
let rawFile = await this.getProjects("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master", "GET")
If the response is json, that would give you an object, but if the response is not json, you want to use response.text() instead. You'd need a different function:
async getTextFile(url) {
const token = this.$store.getters.token
const headers = {
'Private-Token': token
}
const response = await fetch(url, {
method: "GET",
headers: headers
})
return response.text()
}
then call it like this:
let rawFile = await this.getTextFile("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master")

Related

Cloudflare Worker: TypeError: Cannot reconstruct a Request with a used body

I have the following two functions described in my Cloudflare worker code. However, when I run the code and these functions get called I get a TypeError: Cannot reconstruct a Request with a used body -
async function getApiV2Token(env: Env): Promise<string> {
let API_URL = await getApiUrl(env)
let API_CLIENT_ID = await getApiClientId(env)
let API_CLIENT_SECRET = await getApiClientSecret(env)
let apiUrl = `${API_URL}/auth`
let data = `client_id=${API_CLIENT_ID}&client_secret=${API_CLIENT_SECRET}`
let response, responseJSON
try {
response = await postData(apiUrl, data)
responseJSON = await response.json()
} catch (e) {
console.log(`Error: ${e}.`)
return ""
}
return responseJSON.auth_token
}
async function postData(url: string, data: string): Promise<Response> {
let request = new Request(url, {
method : 'POST',
body : data,
headers: {
'User-Agent': 'cloudflare-worker',
'Content-Type': 'application/x-www-form-urlencoded'
},
redirect : 'manual'
})
let cache = caches.default
let cachedResponse = await cache.match(request.url)
if (cachedResponse) {
return cachedResponse
}
console.log("checked cache")
let response = await fetch(request)
console.log("Requested new token")
let newResponseForExpiresIn = response.clone()
let newResponseForExpiresInJSON = await newResponseForExpiresIn.json()
let expires_in = newResponseForExpiresInJSON.expires_in
let newResponseForHeader = response.clone()
let newResponseToCache = new Response(newResponseForHeader.body, newResponseForHeader)
newResponseToCache.headers.set('Cache-Control', `max-age=${expires_in - API_TOKEN_GRACE_PERIOD}`)
cache.put(request.url, newResponseToCache)
return response
}
The line it fails at is let response = await fetch(request) (found this because "checked cache" is logged but not "Requested new token".
Here's what I've tried -
newRequest = request.clone() and then fetching that instead.
creating an identical request with new Request() and then fetching that.
same as above but using slice() to copy data
I also looked at Cloudflare Worker TypeError: One-time-use body but because I'm using the Cloudflare Worker Modules I don't have access to event. Any suggestions?
EDIT: I logged request.bodyUsed before the offending line and it logs false
I was able to fix this by constructing the request within fetch, not sure why.
const response = await fetch(url, {
method : 'POST',
body : data,
headers: {
'User-Agent': 'cloudflare-worker',
'Content-Type': 'application/x-www-form-urlencoded'
}
})

Response of fetching binary data in Postman is successful, but in console returns undefined

I am trying to fetch binary from API using POST request.
const generateReportData = async (params: ReportParams) => {
const response = await fetchReportService(
{ path: "GenerateReport", method: "POST", body: params }).catch();
console.log(response, 'RESP'); // returns undefined for some reason;
const blob = new Blob([response], { type: "application/octet-stream" });
const fileName = "report.doc";
saveAs(blob, fileName);
};
export const fetchReportService = ({ path, method, query, body, options = {} }: FetchParams) => {
return fetch(Config.api.report, path, {
headers,
method,
body: body ? JSON.stringify(body) : undefined,
...options,
}, query);
};
In this method is response undefined for some reason. However if I check my network tab, the request has 200 code and the response looks like this:
I need this response to be accessible from my code. Any ideas why is the response in my method undefined?
Thank you in advance.
Update:
It is weird that you got undefined. From the code you post you should get at least something non-null.
I'd advice you to do the following to debug:
remove the tailing empty .catch()
const response = fetchReportService(...) without await keyword, then console.log it to make ensure it's an instance of Response builtin class.
If it's still undefined, then you should verify that fetch === window.fetch inside fetchReportService. You might mistakenly import fetch from 'somewhere' and using a wrong fetch function.
Might as well verify that window.fetch.toString() == "function fetch() { [native code] }", to ensure your runtime is not tampered.
Original answer:
This line is problematic.
const blob = new Blob([response], { type: "application/octet-stream" });
If you don't care about the MIME type and simply wanna obtain the binary blob from the response, you can do:
const blob = await response.blob();
Or, you can construct the blob manually from arrayBuffer.
const blob = new Blob([await response.arrayBuffer()], { type: "application/octet-stream" });
You need remove '.catch()'
const generateReportData = async (params: ReportParams) => {
try {
const response = await fetchReportService(
{ path: "GenerateReport", method: "POST", body: params });
console.log(response, 'RESP'); // returns undefined for some reason;
const blob = new Blob([response], { type: "application/octet-stream" });
const fileName = "report.doc";
saveAs(blob, fileName);
} catch (e) {
console.log(e)
}
};
export const fetchReportService = ({ path, method, query, body, options = {} }: FetchParams) => {
return fetch(Config.api.report, path, {
headers,
method,
body: body ? JSON.stringify(body) : undefined,
...options,
}, query);
};
The Fetch API call is asynchronous.
Example with async/await:
export const fetchReportService = async ({ path, method, query, body, options = {} }: FetchParams) => {
try {
const res = await fetch(Config.api.report, path, {
headers,
method,
body: body ? JSON.stringify(body) : undefined,
...options,
}, query);
} catch (error) {
console.error(error);
}
return res;
};

Getting undefined in API call via Fetch - Express Nuxt

I am extremely stuck on a task using Express, API, Fetch.
I am using Nuxt + Shopify API endpoints to grab data such as orders like below
This is my express API Endpoint.
The results should return an array of objects ( orders )
const bodyParser = require('body-parser')
const app = require('express')()
const axios = require('axios')
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.post('/getJSON', async (req, res) => {
const { customerID } = req.body
const id = Buffer.from(customerID, 'base64')
.toString('binary')
.split('gid://shopify/Customer/')
.pop()
console.log('id is', id)
const endpoint = `https://test.myshopify.com/admin/api/2020-07/customers/${id}/orders.json`
try {
const response = await axios.get(endpoint, {
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': '*****************'
}
})
res.status(200).json(response.data.orders)
} catch (error) {
res.status(500).send(error)
}
})
module.exports = app
Now, in my Nuxt store.js, I am using fetch to make a post requests to that endpoint above.
async function apiPost(endpoint, { data }) {
await fetch(`/api${endpoint}/getJSON`, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
}).then(async (res) => {
const contentType = res.headers.get('content-type')
if (contentType.startsWith('text/html')) {
return res.text()
}
if (contentType.startsWith('application/json')) {
await res.json()
}
})
}
When I console.log res.json(), it is a promise
Since it is a promise, I wanted to see what the results were anyways.
res.json().then((resp) => {
console.log('resp is', resp)
})
Turns out the rest is an array of objects.
However, I do not know how to return this object correctly, as the "ordersResponse" below is always undefined !
Below is a function calling the apiPost passing in the 'endpoint'. However, the orderResponse is undefined. I have tried solutions, but all of them end up as orderResponse being undefined.
async fetchOrders({ state, dispatch, commit }, payload) {
try {
const ordersResponse = await apiPost('/customer-orders', {
data: { customerID: state.customer.id }
})
console.log('ordersResponse', ordersResponse) // **undefined**
} catch (error) {
console.error(error)
throw error
}
},
Any help is appreciated
It looks like the apiPost function needs to make a return from within the 'application/json' if block.
if (contentType.startsWith('application/json')) {
return res.json()
}
You should then receive data back when calling it for the ordersResponse variable.

Fetch request not returning any data in console, or on front end

When I submit my form, I am not returning any data, not even in my console. I am trying to return details from WHOIS regarding the URL that is searched, and am getting nothing back.
Can anyone provide any advice as to why this might be the case?
Here is my front end script tag, after my form:
document.getElementById('search').addEventListener('submit', function(e) { e.preventDefault(); getDetails(); })
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {}) {
return new Promise(function (resolve, reject) {
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'text/html'
},
body: JSON.stringify(data)
}).then(async response => {
if (response.ok) {
response.json().then(json => resolve(json))
console.log(data);
} else {
response.json().then(json => reject(json))
}
}).catch(async error => {
reject(error)
})
})
}
On my express backend I am using req.params.url if that helps provide any context at all...
My Status Code is 200, and all appears to be normal in the Headers tab...
You have a mix of promise and async syntax, which is confusing, let's translate it first by unpicking the promise and then into await (if you can use async then do, it's easer than Promise/then):
document.getElementById('search').addEventListener(
'submit',
function(e) {
e.preventDefault();
getDetails();
});
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {})
{
// Fetch will throw an exception if it can't connect to the service
const response = await fetch(url, {
method: 'GET',
headers: { 'Content-Type': 'text/html' },
body: JSON.stringify(data)
});
if (response.ok)
return await response.json();
// We could connect but got an error back from the service
// There may not be a response body, so response.json() or .body() might crash
throw new Error(`Error from server ${response.status}: ${response.statusText}`;
// We don't need catch, as any exception in an await will cascade up anyway
}
This makes it much more readable, and it's apparent that getDetails doesn't make any changes itself, it just returns the JSON from the service. The fix needs to be in the event listener - it needs to do something with that result:
document.getElementById('search').addEventListener(
'submit',
async e => {
e.preventDefault();
const searchResult = await getDetails();
// Do something to show the results, populate #results
const resultsElement = document.getElementById('results');
resultsElement.innerText = JSON.stringify(searchResult);
});
You are mis-using async/await. Try this:
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {}) {
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'text/html'
},
body: JSON.stringify(data)
});
const json = await response.json();
if (response.ok) {
console.log(json);
return json;
} else {
throw new Error(json);
}
}
In essence await is a replacement for then (but you can only use it in functions marked with async).

javascript undefined value using fetch in function

If I call this function:
async function delete_get_note() {
let query = {
'command': 'delete_get_content',
'note_id': input_note_id.value
}
let response = await fetch(
'../api/api.php',
{
method: 'POST',
headers: {'Content-Type': 'application/json;charset=utf-8'},
body: JSON.stringify(query)
}
);
let result = await response.json();
log.innerHTML = result.log;
}
log displays the content of the json returned by my server. However, if I try to encapsulate the call to fetch in a function:
async function json_exchange(object) {
let response = await fetch(
'../api/api.php',
{
method: 'POST',
headers: {'Content-Type': 'application/json;charset=utf-8'},
body: JSON.stringify(object)
});
return await response.json;
}
And call:
async function delete_get_note() {
let query = {
'command': 'delete_get_content',
'note_id': input_note_id.value
}
let result = await json_exchange(query);
log.innerHTML = result.log;
}
Then log displays undefined.
What did I get wrong when I tried to put the call to fetch in a function ?
You forgot to add the parentheses to the json call:
return await response.json;
-->
return await response.json()
Tip: You can use TypeScript checking to easily detect such errors:

Categories

Resources