I am creating a generic instance of an api instance to keep my code DRY. Running into an issue exporting the function:
TypeError: request is not a function
index.js
var express = require('express'),
app = express();
const axios = require("axios");
const request = require("./request");
app.get("/api", (req, res) => {
request({
method: 'get',
url: 'https://jsonplaceholder.typicode.com/posts/1'
}).then((resp) => {
console.log(resp);
})
});
app.listen(3000);
request.js
const axios = require("axios");
/**
* Create an Axios Client with defaults
*/
const client = axios.create({
// baseURL: constants.api.url
});
/**
* Request Wrapper with default success/error actions
*/
module.exports.request = function(options) {
const onSuccess = function(response) {
console.debug('Request Successful!', response);
return response.data;
}
const onError = function(error) {
console.error('Request Failed:', error.config);
if (error.response) {
// Request was made but server responded with something
// other than 2xx
console.error('Status:', error.response.status);
console.error('Data:', error.response.data);
console.error('Headers:', error.response.headers);
} else {
// Something else happened while setting up the request
// triggered the error
console.error('Error Message:', error.message);
}
return Promise.reject(error.response || error.message);
}
return client(options)
.then(onSuccess)
.catch(onError);
}
The original code was written with es6 but I guess node doesnt work well so I would like to convert the above function so that node can run it.
module.exports.request = ...
You just exported an object with a request function. That object is not a function.
To do this ES6 style in node you'll need to define the methods as consts:
const onSuccess(response) => {
...
}
const onError(error) => {
...
}
Then export {onSuccess, onError}
If ES6 doesn't work in your node project install babel.
Related
I have two functions in functions.js - postData and startProcess. I am calling both functions in a function in server.js called sendData. sendData is then called in the app.post route.
In sendData, getData is called first and returns an id. This id is passed to startProcess and after it is run, a message that says 'success` should be printed. If it fails, a message that says 'failed to complete process should be returned'.
It seems like postData runs successfully, but startProcess is unable to pick or use its response as id.
When I run just the postData function in SendData, I get this error message:
JSON.stringify(value);
TypeError: Converting circular structure to JSON
What am I missing and how can I properly implement this?
functions.js
const axios = require("axios");
const BASE_URL = "http://localhost:1770";
const startProcess = async (id) => {
const headers = {
"Content-type": "application/json",
};
try {
return axios.post(`${BASE_URL}/start/${id}`, { headers });
} catch (error) {
console.error(error);
}
};
const postData = async (body) => {
const headers = {
"Content-type": "application/json",
};
try {
return axios.post(`${BASE_URL}/data`, body, { headers });
} catch (error) {
console.error(error);
}
};
server.js
const express = require("express");
const process = require("./functions.js");
const payload = require("./payload.json");
const res = require("express/lib/response");
// Create Express Server
const app = express();
app.use(cors());
// Configuration-api
const PORT = 5203;
app.use(express.json());
const sendData = async (req, res, next) => {
const body = payload;
try {
const response = await process.postData(body);
if ((response.status = 200)) {
let id = response.data;
const processResult = await process.startProcess(id);
if ((processResult.status = 200)) {
res.send("successfully started process");
}
}
} catch (error) {
console.error(error);
}
};
app.post("/data", sendData);
app.listen(PORT, () => {
console.log(`Running this application on the PORT ${PORT}`);
});
Here is how my API works:
You can find SeaweedFS here on GitHub.
And the code here:
// /drivers/seaweedfs.js Defines how API interacts with SeaweedFS
const { error } = require("console");
const http = require("http");
module.exports = class Weed {
constructor(mserver) {
this.mserver = new URL(mserver);
}
get(fileId) {
return new Promise((resolve, reject) => {
let options = {
hostname: this.mserver.hostname,
port: this.mserver.port,
method: "GET",
path: `/${fileId}`,
timeout: 6000,
};
let data;
const fileReq = http.request(options, (res) => {
console.log(`Statuscode ${res.statusCode}`);
res.on("data", (response) => {
data += response;
});
res.on("end", () => {
resolve(data);
});
});
fileReq.on("error", () => {
console.error(error);
reject();
});
fileReq.end();
});
}
};
// /routes/file.js An Express router
const express = require("express");
const router = express.Router();
const Weed = require("../drivers/seaweedfs");
let weedClient = new Weed("http://localhost:60002");
router.get("/:fileId", (req, res) => {
weedClient.get(req.params.fileId)
.then(data=>{
res.write(data)
res.end()
})
}
)
module.exports = router;
MongoDB driver not yet implemented.
When I try to GET a file(using Firefox, Hoppscotch says Could not send request: Unable to reach the API endpoint. Check your network connection and try again.), I get something whose MIME type is application/octet-stream for some reason. It's bigger than the original file. I know there must be some problems with my code, but I don't know where and how to fix it.
I'm trying to make a simple webpage using React.js to display information retrieved from an API. I say simple because it should be, but I haven't developed anything for a long time and I seem to have an issue that I can't find the cause off.
I made a node.js file to act as a mediator between my webpage and the API because I was having CORS issues.
Situation:
Whenever I push the button I don't retrieve any information. The call keeps displaying "pending" as status within the network tab(see below image).
But within my Node.js script I can see by logging that I do get the correct response and retrieve all the information I need. So I can state that my node script works as it should.
I think the issue is situated between the exchange from my Node.js script and the CallService.js script. I just can't put my finger on what is wrong.
What am I missing here?
I've provided all the code I have below but left out sensitive information.
Node.js:
const express = require('express');
const request = require('request');
const app = express();
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
app.get('/ListAllModels', (req, res) => {
request(
{
url: '/* api url*/',
headers: {
'Content-Type': 'application/xml',
'X-Tenant-ID': '/*tenant id*/',
'Authorization': 'Basic /*authorization*/'
}
},
(error, response, body) => {
if (error || response.statusCode !== 200) {
return res.status(500).json({ type: 'error', message: err.message });
}
console.log(response);
return response;
}
)
});
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => console.log(`listening on ${PORT}`));
I made a Callservice.js file in my react app:
import axios from 'axios';
export const getAllEmailModels = () => {
return axios.get("http://localhost:4000/ListAllModels").then(response => {
if (response.statusCode === 200) {
return response;
}
throw new Error("Network response was not ok.");
}).catch(error => {
return error;
});;
};
In my app I made a button that should call on the API and provide the retrieved information in console.
import React, { Component } from 'react'
//import axios from 'axios';
import * as CallService from "../Services/CallService";
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {emailModels:[]};
this.emailModelTest = this.emailModelTest.bind(this);
}
emailModelTest() {
console.log("button works");
CallService.getAllEmailModels().then(response => this.setState({
emailModels: response.body,
}), console.log(this.state.emailModels) );
}
render() {
return (
<div>
<button onClick={this.emailModelTest}>test call</button>
</div>
)
}
}
Your problem is in here:
app.get('/ListAllModels', (req, res) => {
request(
{
url: '/* api url*/',
headers: {
'Content-Type': 'application/xml',
'X-Tenant-ID': '/*tenant id*/',
'Authorization': 'Basic /*authorization*/'
}
},
(error, response, body) => {
if (error || response.statusCode !== 200) {
return res.status(500).json({ type: 'error', message: err.message });
}
console.log(response);
return response;
}
)
});
It's pending because it there is never a response.
When it goes to your 'ListAllModels' endpoint, it makes a request to another endpoint asynchronously.
When the response comes back it isn't inside the app.get function anymore.
Also, the return response will return the response to the caller of that callback which is inside the request() function. You need to send the response back to your CallService using the res object.
You could wrap it in a promise.
You need to send the response using the res object that is passed in as a parameter. That object represents the response send by the node server. The only way the client can get anything is if you send something through that object using res.send(data). Using return response; doesn't work because it's inside another function. It will be returned to the caller of that function not the parent function app.get()
app.get('/ListAllModels', (req, res) => {
fetch('{{url}}', {
headers: {
'Content-Type': 'application/xml',
'X-Tenant-ID': '{{tenant-ID}}',
'Authorization': 'Basic {{auth}}'
}
}).then(otherRes => {
res.status(200).send(otherRes);
}).error(error => {
res.status(400).end();
});
});
I am trying to do an api call from another api call in this code but I keep getting
Error: Converting circular structure to JSON
const express = require('express');
const router = express.Router();
const config = require('config');
const auth = require('../../middleware/auth');
const axios = require('axios');
const CircularJSON = require('circular-json');
// #router POST api/couriers/couriersPlease
// #desc Test route
// #access Private
router.post('/couriersPlease/', auth, async (req, res) => {
try {
const apiId = config.get('couriersPlease.id')
const apiKey = config.get('couriersPlease.key')
// Encode api id and key to base64 for couriers please request
const encodedString = new Buffer.from(apiId + ':' + apiKey).toString('base64');
const apiConfig = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${encodedString}`
}
};
const body = CircularJSON.stringify(req.body);
console.log('Body:', body);
const response = await axios.post('https://api-test.couriersplease.com.au/v2/domestic/quote', body, apiConfig);
res.json(response);
} catch (err) {
console.error('Error: ', err.message);
return res.status(500).send(err.message);
}
});
module.exports = router;
I checked through answers here but after hours I am still stuck,
I have tried JSON.stringify and even circular-json but it still doesn't work. Any ideas?
res.json(response);
should be
res.json(response.data);
Feel so stupid now haha
At present I'm performing the trick of piping a request req to a destination url, and piping the response back to res, like so:
const request = require('request');
const url = 'http://some.url.com' + req.originalUrl;
const destination = request(url);
// pipe req to destination...
const readableA = req.pipe(destination);
readableA.on('end', function () {
// do optional stuff on end
});
// pipe response to res...
const readableB = readableA.pipe(res);
readableB.on('end', function () {
// do optional stuff on end
});
Since request is now officially deprecated (boo hoo), is this trick at all possible using the gaxios library? I thought that setting responseType: 'stream' on the request would do something similar as above, but it doesn't seem to work.
SImilarly, can gaxios be used in the following context:
request
.get('https://some.data.com')
.on('error', function(err) {
console.log(err);
})
.pipe(unzipper.Parse())
.on('entry', myEntryHandlerFunction);
Install gaxios:
npm install gaxios
And then use request with the Readable type specified and with responseType set to 'stream'.
// script.ts
import { request } from 'gaxios';
(await(request<Readable>({
url: 'https://some.data.com',
responseType: 'stream'
}))
.data)
.on('error', function(err) {
console.log(err);
})
.pipe(unzipper.Parse())
.on('entry', myEntryHandlerFunction);
// first-example.ts
import { request } from 'gaxios';
// pipe req to destination...
const readableA = (await request<Readable>({
url: 'http://some.url.com',
method: 'POST',
data: req, // suppose `req` is a readable stream
responseType: 'stream'
})).data;
readableA.on('end', function () {
// do optional stuff on end
});
// pipe response to res...
const readableB = readableA.pipe(res);
readableB.on('end', function () {
// do optional stuff on end
});
Gaxios is a stable tool and is used in official Google API client libraries. It's based on the stable node-fetch. And it goes with TypeScript definitions out of the box. I switched to it from the deprecated request and from the plain node-fetch library.
I guess if you provide responseType as stream and use res.data, you will get a stream which you could pipe like this
const {request} = require("gaxios");
const fs = require("fs");
const {createGzip} = require("zlib");
const gzip = createGzip();
(async () => {
const res = await request({
"url": "https://www.googleapis.com/discovery/v1/apis/",
"responseType": "stream"
});
const fileStream = fs.createWriteStream("./input.json.gz");
res.data.pipe(gzip).pipe(fileStream);
})();
Looks like you are trying to basically forward requests from your express server to the clients. This worked for me.
import { request } from "gaxios";
const express = require("express");
const app = express();
const port = 3000;
app.get("/", async (req: any, res: any) => {
const readableA = (await request({
url: "https://google.com",
responseType: "stream",
})) as any;
console.log(req, readableA.data);
const readableB = await readableA.data.pipe(res);
console.log(res, readableB);
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
I imagine more complicated responses from A will require more nuiance in how to pipe it. But then you can probably just interact with express's response object directly and set the appropriate fields.