I'm using Got to make requests to a Strapi API from Node, like so:
res.setHeader('Content-Type', 'application/json')
try {
const request = req.query.request
const decodedRequest = Buffer.from(request, 'base64').toString()
const api = process.env.API_URL ? process.env.API_URL.replace(/\/$/, '') : ''
const url = `${api}${decodedRequest}`
const response = await got.get(url)
const body = await got.get(url).json()
const headers = JSON.parse(JSON.stringify(response.headers))
res.status(200).json({
headers: headers,
data: body
})
} catch (e) {
res.status(500).json({})
}
This works but note that I have the request twice because if I do:
res.setHeader('Content-Type', 'application/json')
try {
const request = req.query.request
const decodedRequest = Buffer.from(request, 'base64').toString()
const api = process.env.API_URL ? process.env.API_URL.replace(/\/$/, '') : ''
const url = `${api}${decodedRequest}`
const response = await got.get(url)
const body = response.json()
const headers = JSON.parse(JSON.stringify(response.headers))
res.status(200).json({
headers: headers,
data: body
})
} catch (e) {
res.status(500).json({
error: e
})
}
it Just crashes and the e from the catch returns an empty error so I have no idea what's going on
I need the headers because the pagination info from Strapi is returned there:
This works because of the value you're awaiting. The conventional example:
const body = await got.get("...").json();
is equivalent to:
const res = got.get("...");
const body = await res.json();
// ^ note
but not:
const res = await got.get("...");
// ^ note
const body = res.json();
From the Promise API docs:
The main Got function returns a
Promise.
Although in order to support cancelation,
PCancelable is used
instead of pure Promise.
The json method is attached to this PCancelable object, not the value it resolves to. If you try to call it on the response, therefore, you get TypeError: res.json is not a function.
What you want is something like:
const res = await got.get("...");
const body = JSON.parse(res.body);
const headers = res.headers;
// ...
That said, if you're doing this for pagination reasons you could also look into their API for that.
Related
export const getCharactersAsync = createAsyncThunk('getCharactersAsync', async (data) => {
const response = await axios.get('users', { params: { limit: data.limit } });
return response.data;
});
this code block allows me to control limit attribute.
export const getCharactersAsync = createAsyncThunk('getCharactersAsync', async (data) => {
const params = new FormData();
// const params = new URLSearchParams();
params.append('limit', data.limit);
const response = await axios.get('users', params);
console.log(response);
return response.data;
});
However I cannot control limit with using params.append. I tried URLSearchParams instead of FormData but still cannot manipulate limit attribute of the response. Why they differ from each other?
EDIT: This question has missleading information. I should have mention that i am using react-native. I found that react native doesn't fully support everything the web supports. So i need to install package called react-native-url-polyfill.Here is a github issues link
https://github.com/facebook/react-native/issues/23922#issuecomment-648096619
docs
params are the URL parameters to be sent with the request. Must be a plain object or a URLSearchParams object
It can't be FormData
Solution
You wanted to use { params }, not params
export const getCharactersAsync = createAsyncThunk('getCharactersAsync', async (data) => {
const params = new URLSearchParams();
params.append('limit', data.limit);
const response = await axios.get('users', { params });
console.log(response);
return response.data;
});
I am retrieving a file from an S3 bucket and then forwarding it to another API. It's causing the following error:
DataCloneError: function httpAdapter(config) {
return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise)...<omitted>...
} could not be cloned.
at MessagePort.<anonymous> (file:///D:/Dev/beep/node_modules/serverless-offline/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js:24:10)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
The code used is here:
module.exports.fileUpload = async (event) => {
const bodyForm = JSON.parse(event.body);
const s3 = getS3Client();
const getObjectCommand = new GetObjectCommand({
Bucket: 'bucket-name',
Key: path.parse(bodyForm.name).name
});
const signedUrl = await getSignedUrl(s3, getObjectCommand);
const response = await axios.get(signedUrl, { responseType: 'stream' });
const form = new FormData();
form.append('file', response.data, bodyForm.edf_name);
await axios.post('https://api-url', form).then(res => {
console.log(res)
})
}
This issue happens (most probably) because your form object has methods in it (like your append method), and methods cannot be serialised.
You have two options here:
The hacky way, forcing a serializable form:
const serializableForm = JSON.parse(JSON.stringyfy(form));
await axios.post('https://api-url', serializableForm).then(res => {
console.log(res)
})
The better way, in which you map what form fields you need in your request
const serializableForm = {
foo: form.foo,
bar: form.bar
// ...
};
await axios.post('https://api-url', serializableForm).then(res => {
console.log(res)
})
So i'm making a fetch request from react to express and the api url requires params but I want the last bit of the url to be dynamic. How would I send a param from react to the api url in express? Heres the code
react
const [dynamic, setDynamic] = useState("somevalue")
useEffect(() => {
async function fetchData() {
const response = await fetch("/web")
const data = await response.json()
console.log(data)
}
fetchData()
}, [dynamic])
express.js
app.get("/web", async (req, res) => {
const response = await fetch("https://alexa.com/api?Action=urlInfo&Url=")
const data = await response.json()
res.json(data)
})
Basically, I would like the dynamic state value to be injected into the end of the URL in express.
Something like
react
const [dynamic, setDynamic] = useState("somevalue")
async function fetchData() {
const response = await fetch("/web" + dynamic)
const data = await response.json()
console.log(data)
}
Then in express when the API is called the URL changes to
const response = await fetch("https://alexa.com/api?Action=urlInfo&Url=somevalue")
How can I go about achieving this?
You define the parameter on the route handler side, setting up a route that matches the desired pattern. For example:
app.get("/foo/bar/:myparam", async (req, res) => {
//... here's your handler code
})
Will match /foo/bar/baz, and req.params.myparam === "baz" inside the handler.
http://expressjs.com/en/guide/routing.html#route-parameters
Using query params is similar, but you want to check the values of the req.query object instead, like:
// client side (React code)
await fetch("/foo/bar?myparam=baz")
...
// In your express handler:
req.query.myparam === "baz"
Use Dynamic value in params of api call.
React code would look like this:
const [dynamic, setDynamic] = useState("somevalue")
useEffect(() => {
async function fetchData() {
const response = await fetch(`/web/${dynamic}`)
const data = await response.json()
console.log(data)
}
fetchData()
}, [dynamic])
In Express.Js code
define the parameter on the route handler side, and use that in fetch api call
app.get("/web/:value", async (req, res) => {
const {value} = req.params;
const response = await fetch(`https://alexa.com/api?Action=urlInfo&Url=${value}`)
const data = response.json()
res.json(data)
})
I have the following files:
My routes - where the orders_count route lives:
routes/index.js
const express = require('express');
const router = express.Router();
const transactionsController = require('../controllers/transactionsController');
const ordersController = require('../controllers/ordersController');
const ordersCountController = require('../controllers/ordersCountController');
router.get('/transactions', transactionsController);
router.get('/orders', ordersController);
router.get('/orders_count', ordersCountController);
module.exports = router;
I then have my orders count controller living in the controllers directory:
controllers/ordersCountController.js
const ordersCountService = require('../services/ordersCountService');
const ordersCountController = (req, res) => {
ordersCountService((error, data) => {
if (error) {
return res.send({ error });
}
res.send({ data })
});
};
module.exports = ordersCountController;
My controller then calls my order count service which fetches data from another API.
services/ordersService.js
const fetch = require('node-fetch');
// connect to api and make initial call
const ordersCountService = (req, res) => {
const url = ...;
const settings = { method: 'Get'};
fetch(url, settings)
.then(res => {
if (res.ok) {
res.json().then((data) => {
return data;
});
} else {
throw 'Unable to retrieve data';
}
}).catch(error => {
console.log(error);
});
}
module.exports = ordersCountService;
I'm trying to return the JSON response. I initially had it setup with requests but looking at the NPM site, it appears that it's depreciated so have been digging through how to use node-fetch.
I have tried both 'return data' and res.send({data}), but neither are solving the problem.
I am still new to this so I am likely missing something very obvious, but how come I am not sending the JSON back through so that it displays at the /api/orders_count endpoint?
I keep thinking I messed something up in my controller but have been looking at it for so long and can't seem to figure it out.
Any help would be greatly appreciated and if there is anything I can add for clarity, please don't hesitate to ask.
Best.
please learn promises and await syntax. life will be easier.
never throw a string. always prefer a real error object, like that : throw new Error('xxx'); that way you will always get a stack. its way easier to debug.
avoid the callback hell : http://callbackhell.com/
you need to decide if you want to catch the error in the controller or in the service. no need to do in both.
in the controller you call the service that way :
ordersCountService((error, data) => {
but you declare it like that :
const ordersCountService = (req, res) => {
which is not compatible. it should look like this if you work with callback style :
const ordersCountService = (callback) => {
...
if (error) return callback(error)
...
callback(null, gooddata);
here is an example to flatten your ordersCountService function to await syntax, which allows the "return data" you were trying to do :
const fetch = require('node-fetch');
// connect to api and make initial call
const ordersCountService = async (req, res) => {
const url = ...;
const settings = { method: 'Get'};
try {
const res = await fetch(url, settings);
if (!res.ok) throw new Error('Unable to retrieve data');
return await res.json();
} catch(error) {
console.log(error);
}
}
module.exports = ordersCountService;
in fact i would prefer to error handle in the controller. then this woud be sufficient as a service
const fetch = require('node-fetch');
// connect to api and make initial call
const ordersCountService = async () => {
const url = ...;
const settings = { method: 'Get'};
const res = await fetch(url, settings);
if (!res.ok) throw new Error('Unable to retrieve data');
return await res.json();
}
module.exports = ordersCountService;
then you can call this funtion like this :
try {
const data = await ordersCountService(req, res);
} catch(err) {
console.log(err);
}
//or
ordersCountService(req, res).then((data) => console.log(data)).catch((err) => console.error(err));
I once wrote a very simple code to fetch&display data from certain api. Now that that API doesn't work anymore, I wanted to keep the code intact and make it display an error message that the API doesn't work.
async function getInfected() {
const api_url = //API Address that doesn't work anymore//;
const response = await fetch(api_url);
// console.log(response);
if (response.ok){
const data = await response.json();
const todayInfected = data.data[0][3];
const todayDeath = data.data[0][2];
document.getElementById('todayInfected').textContent = todayInfected;
document.getElementById('todayDeath').textContent = todayDeath;
} else {
const errorMessage = "Error! Can't fetch API!";
document.getElementsByClassName('nowp').textContent = errorMessage;
}
}
getInfected();
This code does nothing and console.log(response) also doesn't work. And unfortunately, I have very little understanding of fetch(). Any Ideas?