I am creating a Recipe Search Engine/Culinary blog.
I want user to choose food category (e.g pork), send it to the server, pass that information to the API call link, then call the API and send the results back to front-end. I am up to sending category name to the server from client and got stuck on how i can handle that.
CLIENT-SIDE
function getRecipes(category){
const categorySearch = category.alt;
let data = {
categoryChoice: categorySearch
}
let options = {
method: 'POST',
headers: {
"Content-type": "application/json; charset=UTF-8"
},
body: JSON.stringify(data)
}
const promise = fetch('/data', options);
promise.then(response => {
if(!response.ok){
console.error(response)
} else {
return response.json();
}
}).then(result => {
console.log(result);
})
}
SERVER-SIDE
const express = require('express');
const app = express();
const fetch = require('node-fetch');
require('dotenv').config();
const API_KEY = process.env.API_KEY;
const port = process.env.PORT || 3100;
app.listen(port, () => console.log('listening at 3100'));
app.use(express.static('public'));
app.use(express.json({ limit: "1mb"}));
app.post('/data', async (request, response) => {
const data = await request.body;
const gotData = data.categoryChoice;
const category = gotData;
console.log(category);
response.json(category);
return category
})
app.get('/data', async (request, response, category) => {
const cat = await category;
const url = `https://edamam-recipe-search.p.rapidapi.com/search?q=${cat}&from=0&to=100`
const fetch_response = await fetch(url);
const json = await fetch_response.json();
response.json(json);
})
I am correctly getting the category depending on what i click in the front-end and that category is being send to server. How i can pass that variable to the API call ? And then how i can send it back to the front-end ?
Many Thanks
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 am trying to use express to create an endpoint so I can retrieve data from an api and return the json data in the body.
We are getting data from a rest api that returns an array of json data. What i would like is to use express router.get to display the json formatted on the front-end so i can then access the endpoint and get the data. Here is what i have so far:
"use strict";
const express = require("express");
const path = require("path");
const serverless = require("serverless-http");
const app = express();
const bodyParser = require("body-parser");
const fetch = require("node-fetch");
var async = require("express-async-await");
const router = express.Router();
router.get("/", async function(req, res, next) {
var requestOptions = {
method: "POST",
headers: {
Accept: "application/json",
Authorization:
"Basic *********",
"Access-Control-Allow-Origin": "*"
},
redirect: "follow"
};
async function getApi() {
try {
const response = fetch(
"http://www.reed.co.uk/api/1.0/search?employerId=*******",
requestOptions
)
.then(res => {
return res.json();
})
.then(json => {
return json;
});
return response;
} catch (error) {
console.log(error);
}
}
const ooIprocessData = async () => {
const data = await getApi();
const ooiResponseData = await data;
return ooiResponseData;
};
ooIprocessData();
res.end;
});
The below code returns the data in the node response when we access the router but it shows and I need to resolve it on the front-end.
Can anyone point me in the right place for this to work?
Thanks,
You have some unnessecary steps here. Also you could move your function outside of your router function.
"use strict";
const express = require("express");
const path = require("path");
const serverless = require("serverless-http");
const app = express();
const bodyParser = require("body-parser");
const fetch = require("node-fetch");
var async = require("express-async-await");
const router = express.Router();
router.get("/", async function(req, res) {
var requestOptions = {
method: "POST",
headers: {
Accept: "application/json",
Authorization: "Basic *********",
"Access-Control-Allow-Origin": "*"
},
redirect: "follow"
};
try {
let result = await getApi(requestOptions);
res.status(200).json(result);
} catch (err) {
return res.status(500).json(err);
}
});
function getApi(requestOptions) {
return fetch(
"http://www.reed.co.uk/api/1.0/search?employerId=*******",
requestOptions
).then(res => {
return res.json();
});
}
Your problem was also that getApi returns an promise that resolves to undefined because you try to return outside of the promise chain
Use async / await where its useful. Not where it could maybe work
Response has a function called json to send back JSON data. You can also optionally use Response.prototype.status to provide metadata about whether it worked or not. For example,
res.status(200).json({status: 1, msg: 'Fetched successfully'});
I am trying to get the username of the profile on the url of that users page. Just to figure out the basic of getting Instagram data
The code below is what I have tried to do and miserably failed(also I am a beginner sorry for bad code)
const express = require('express');
const bodyParser = require('body-parser');
const request = require('request-promise');
const path = require('path');
var cheerio = require('cheerio');
const app = express()
var followers = [];
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.set('view engine', 'ejs')
app.get('/', function (req, res) {
request({
method: 'GET',
url: 'https://www.instagram.com/unrivaledhype/'
}, function(err, response, body, callback) {
if(err) return console.error(err);
$ = cheerio.load(url);
var post = $("*");
var follwerCount = post.find('h1').text();
console.log(follwerCount);
followers.push({follwerCount: follwerCount})
});
res.send(JSON.stringify(followers, null, 4));
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
It just displays follwerCount:"" but(even though I named it misspelled followerCount I went for the username) although if someone can show me how to get the follower count that would be greatly appreciated.
Unfortunately, Instagram pages are rendered on the client side, so it is not possible to get information like the number of followers through this way.
A solution to this problem would be to use puppeteer.
With puppeteer you can start a headless version of chrome, which also interprets javascript, so that Instagram pages get rendered completely.
Example code:
const puppeteer = require('puppeteer')
class InstagramClient {
async start() {
this.browser = await puppeteer.launch({
headless: true //When set to true, a new browser window will ge opened
})
}
async stop() {
await this.browser.close()
}
async getFollowers(username) {
if (!this.browser) throw new Error('Browser not started')
const page = await this.browser.newPage()
await page.goto(`https://instagram.com/${username}/`)
const pageExists = await page.evaluate(_ => {
return document.querySelector('.error-container') === null
})
if (!pageExists) {
throw new Error(`Page of ${username} doesn't exist`)
}
//Wait until the page got completly renderer
await page.waitForSelector('h1')
const followers = await page.evaluate(username => {
//This code will get executed on the instagram page
//Get the number of followers
const followers = document.querySelector(`a[href="/accounts/login/?next=%2F${username}%2Ffollowers%2F&source=followed_by_list"]`).querySelector('span').innerText
//Return the number of followers back to the node process
return followers
}, username)
page.close()
return followers
}
}
module.exports = InstagramClient
const InstagramClient = require('./utils/instagram-client')
async function start() {
const client = new InstagramClient()
await client.start()
console.log('#instagram:', await client.getFollowers('instagram'))
console.log('#unrivaledhype:', await client.getFollowers('unrivaledhype'))
console.log('#teslamotors:', await client.getFollowers('teslamotors'))
await client.stop()
}
start()
Output:
#instagram: 309m
#unrivaledhype: 3,230
#teslamotors: 6m
If you want a more detailed explanation, check out this video:
A Guide to Web Scraping with NodeJS
Express example:
const express = require('express')
const InstagramClient = require('./utils/instagram-client')
const app = express()
const client = new InstagramClient()
app.get('/:instagramName/followers', async (request, response) => {
const instagramName = request.params.instagramName
try {
const followers = await client.getFollowers(instagramName)
response.json({
success: true,
followers: followers
})
} catch (e) {
response.json({
success: false,
error: e.toString()
})
return
}
})
async function start() {
await client.start()
app.listen(3000, _ => console.log('Server is listening on port 3000'))
}
start()
I have an API endpoint in my Node/Express app. The endpoint is responsible to upload files. There are several stages involved in the upload process. like image conversion, sending images to another third party API, etc. I am using socket.io to tell the client about the current stage of upload.
The problem is, The socket connection works fine in the first call, but in my second call to the endpoint, the socket connection runs twice and retains the data which I sent in the previous call.
Here's my code:
server.js
import ClientsRouter from './api/routes/clients';
import express from 'express';
import http from 'http';
import io from 'socket.io';
const port = process.env.PORT || 3000;
const app = express();
const server = http.Server(app);
const socket = io(server);
app.set('socket', socket);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.set('view engine', 'ejs');
app.use(express.static('dist'))
app.use('/uploads', express.static('uploads'));
app.use('/api/clients', ClientsRouter);
server.listen(port, () => console.log(`Server Listening on ${process.env.URL}`));
api/routes/clients.js
import express from 'express';
import ClientsController from '../controllers/clients';
ClientsRouter.post('/uploadClientData/', clientDataUpload.array('client_data'), ClientsController.uploadClientData);
controllers/clients.js
const uploadClientData = async (req, res) => {
try {
const files = req.files
const clientFolder = req.body.client_folder
const { dbxUser } = req;
const team_member_id = req.body.team_member_id;
const data = req.files.map( i => ({team_member_id, destination: i.destination.substring(1), filename: i.filename, status: 1 } ))
const io = req.app.get("socket");
console.log("Outside Socket", data); //This contains my currently passed data
io.on('connection', async socket => {
console.log("Socket Connection established");
console.log("Inside Socket", data); //But This contains my current data aling with the data that I passed in previous call
await uploadQueue.collection.insertMany(data)
socket.emit('upload stage', { upload_stage: 2, progress: 33 })
await helpers.convertImagesToWebResolution(team_member_id, req.body.dpi, req.body.resolution);
socket.emit('upload stage', { upload_stage: 3, progress: 66 })
await helpers.uploadImagesToDropbox(team_member_id, dbxUser, clientFolder)
socket.emit('upload stage', { upload_stage: 4, progress: 100 })
})
res.status(200).json({message: "Uploaded"});
} catch (error) {
console.log(error)
res.status(500).json({
error
});
}
}
And in my front-end react component
componentDidMount(){
const { currentFolder } = this.props;
this.setState({ client_folder: currentFolder }, () => this.afterFileSelect())
}
componentDidUpdate(prevProps){
const { selectedFiles } = this.props;
if(prevProps.selectedFiles !== selectedFiles){
this.afterFileSelect()
}
}
afterFileSelect = async () => {
const { selectedFiles, setSelectedFiles, currentFolder, user, uploadSettings} = this.props;
let formData = new FormData()
formData.append('client_folder', currentFolder)
formData.append('team_member_id', user.team_member_id)
formData.append('resolution', uploadSettings.resolution.split("x")[0])
formData.append('dpi', uploadSettings.dpi)
for(let selectedFile of selectedFiles){
formData.append('client_data', selectedFile)
}
let uploadResp = uploadSettings.convert_web_res ? await uploadClientData(formData) : await dropboxDirectUpload(formData)
const endpoint = uploadResp.config.url;
const host = endpoint.substring(0, endpoint.indexOf("api"));
const socket = socketIOClient(host);
socket.on("upload stage", data => {
this.setState({upload_stage: data.upload_stage, progress: data.progress})
data.upload_stage === 4 && this.setState({client_folder: ""})
})
}
Also I want to know if this is the correct way to to track upload progress?