Stream Response into HTTP Response - node.js

I have an API that is trying to make an HTTP request to an API that streams and image back to the me, then either stream that image back to the client making the request to me or wait until the image has been streamed to me and send it all at once.
I am using Express and request-promise.
Here's a shortened version of my code.
const express = require('express');
const router = express.Router();
const request = require('request-promise');
const imgFunc = async () => {
try {
const response = await request.get({
method: 'GET',
uri: `http://localhost:8080`,
});
return response;
} catch(err) {
console.log(err);
}
};
router.get('/', async function(req, res, next) {
try {
const response = await imgFunc();
return res.send(response);
} catch (err) {
console.log(err);
}
});
module.exports = router;
The image that I get back is just what I assume is the binary data and I don't know if I need to do something at the request-promise level to make that right or when I send it back to the client.
The server that I have running at localhost:8080 mimics the actual server that I will be hitting when this is all said and done.

You could pipe the streams directly rather than using request-promise.
const express = require('express');
const router = express.Router();
const https = require('https');
router.get('/', function(req, res) {
const url = 'https://www.gravatar.com/avatar/2ea70f0c2a432ffbb9e5875039645b39?s=32&d=identicon&r=PG&f=1';
const request = https.get(url, function(response) {
const contentType = response.headers['content-type'];
console.log(contentType);
res.setHeader('Content-Type', contentType);
response.pipe(res);
});
request.on('error', function(e){
console.error(e);
});
});
module.exports = router;
Or using the request library on which request-promise is based:
const express = require('express');
const router = express.Router();
const request = require('request');
router.get('/', function(req, res) {
const url = 'https://www.gravatar.com/avatar/2ea70f0c2a432ffbb9e5875039645b39?s=32&d=identicon&r=PG&f=1';
request.get(url).pipe(res);
});
module.exports = router;

you could pipe the streams directly through axios
const express = require('express');
const router = express.Router();
const axios = require("axios");
router.get('/', function(req, res) {
const link = 'https://app.idesk360.com/media/logos/iDeskLoginTxt.png';
const arrayBuffer = await axios.get(link, {responseType: 'stream'});
const contentType = arrayBuffer.headers["content-type"];
res.setHeader('content-type', contentType);
arrayBuffer.data.pipe(res);
});
module.exports = router;

Related

How do I grab json from an external api (serp api) from my backend and then make that same data available for my front end application?

Right now I have a front end react application using axios and and a backend server using node.js and express. I cannot for the life of me get my serp api data to post so that my front end can get it through axios and display the json data. I know how to get data to the front end but I am not a backend developer so this is proving to be incredibly difficult at the moment. I'm able to get the data from the the external api, I just don't know how to post it once I get it. Also I would not like to have all these request running on server.js so I created a controller but I think that is where it is messing up. Any help is appreciated
//pictures controller
const SerpApi = require('google-search-results-nodejs');
const {json} = require("express");
const search = new SerpApi.GoogleSearch("674d023b72e91fcdf3da14c730387dcbdb611f548e094bfeab2fff5bd86493fe");
const handlePictures = async (req, res) => {
const params = {
q: "Coffee",
location: "Austin, Texas, United States",
hl: "en",
gl: "us",
google_domain: "google.com"
};
const callback = function(data) {
console.log(data);
return res.send(data);
};
// Show result as JSON
search.json(params, callback);
//res.end();
}
// the above code works. how do i then post it to the server so that i can retrieve it to the backend?
module.exports = {handlePictures};
//server.js
const express = require('express');
const app = express();
const path = require('path');
const cors = require('cors');
const corsOptions = require('./config/corsOptions');
const { logger } = require('./middleware/logEvents');
const errorHandler = require('./middleware/errorHandler');
const cookieParser = require('cookie-parser');
const credentials = require('./middleware/credentials');
const PORT = process.env.PORT || 3500;
// custom middleware logger
app.use(logger);
// Handle options credentials check - before CORS!
// and fetch cookies credentials requirement
app.use(credentials);
// Cross Origin Resource Sharing
app.use(cors(corsOptions));
// built-in middleware to handle urlencoded form data
app.use(express.urlencoded({ extended: false }));
// built-in middleware for json
app.use(express.json());
//middleware for cookies
app.use(cookieParser());
//serve static files
app.use('/', express.static(path.join(__dirname, '/public')));
// routes
app.use('/', require('./routes/root'));
app.use('/pictures', require('./routes/api/pictures'));
app.all('*', (req, res) => {
res.status(404);
if (req.accepts('html')) {
res.sendFile(path.join(__dirname, 'views', '404.html'));
} else if (req.accepts('json')) {
res.json({ "error": "404 Not Found" });
} else {
res.type('txt').send("404 Not Found");
}
});
app.use(errorHandler);
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
//api/pictures.js
const picturesController= require('../../controllers/picturesController');
const express = require('express')
const router = express.Router();
// for POST request use app.post
router.route('/')
.post( async (req, res) => {
// use the controller to request external API
const response = await picturesController.handlePictures()
// send the response back to client
res.json(response)
})
module.exports = router;
You just need to return the result from SerpApi in your handlePictures function. To do this make a new Promise and when search.json runs callback do what you need with the results and pass it in resolve.
Your picturesController.js with an example of returning all results.
//pictures controller
const SerpApi = require("google-search-results-nodejs");
const { json } = require("express");
const search = new SerpApi.GoogleSearch(process.env.API_KEY); //your API key from serpapi.com
const handlePictures = async (req, res) => {
return new Promise((resolve) => {
const params = {
q: "Coffee",
location: "Austin, Texas, United States",
hl: "en",
gl: "us",
google_domain: "google.com",
};
const callback = function(data) {
resolve(data);
};
search.json(params, callback);
});
};
module.exports = { handlePictures };
Output:
And I advise you to change your API key to SerpApi to prevent it from being used by outsiders.
Since I don't have the full context of your App I can just assume the context. But given the fact that you already have wrapped the logic of calling the external API into a dedicated controller you can use it in the following way in an express app (used the hello world example from express):
// import your controller here
const express = require('express')
const app = express()
const port = 3000
// for POST request use app.post
app.get('/', async (req, res) => {
// use the controller to request external API
const response = await yourController.method()
// send the response back to client
res.json(response)
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Here's an example how to execute the http request from the frontend:
const response = await fetch('http://localhost:3000') // result from res.json(response)

getting filename of formdata after axios request

I have a quick question. I would like to extract the file name of a wav file I am passing to my backend through axios using formData. I am not sure how to do this is nodejs. I would appreciate any help
Frontend:
//file is a Blob
var new_file = new File([file], bucket_string);
var formData = new FormData();
formData.append("wavfile", new_file, bucket_string);
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
return await axios.post(`/files/upload-file`, formData, config);
//return await axios.post(`http://localhost:2000/files/upload-file`, data);
backend:
controller.js
async function uploadFile(req, res, next) {
fileService.uploadFile().then(function(val) {
res.json(val)
});
} 
service.js
function uploadFile({ data }) {
const file = data;
var file_name = ?
//i would like to get the file name here
}
const express = require('express');
const multer = require('multer');
const app = express();
const upload = multer();
function uploadFile(req, res, next) {
console.log(req.file.originalname);
res.json({ok: true}).end();
}
app.post('/', upload.single('wavfile'), uploadFile);
app.listen(3001);

Why is the request body empty and the responce status is 404?

I am trying to build a web application using MVC pattern, but I have a problem with POST-request to "http://localhost:5000/api/device": postman POST request attempt
The problem only occurs on POST request, GET request is OK: get request
Code:
index.js
const express = require('express')
const sequelize = require('./db')
require('dotenv').config();
const path = require('path')
const models = require('./models/models')
const cors = require("cors")
const app = express();
const router = require('./routes/index')
const fileUpload = require('express-fileupload')
const errorHandler = require('./milddleware/ErrorHandlingMiddleware')
app.use(cors())
app.use(express.json())
app.use(express.static(path.resolve(__dirname, 'static')))
app.use(fileUpload({}))
app.use('/api', router)
app.use(errorHandler)
const PORT = process.env.PORT || 5000;
const db_start = async ()=>
{
try
{
await sequelize.authenticate();
await sequelize.sync();
app.listen(PORT, ()=>{console.log(`Server started on port ${PORT}`)})
}
catch (e)
{
console.log(e);
}
}
db_start()
routes/index.js
const Router = require('express')
const router = new Router();
const deviceRouter = require('./deviceRouter')
const typeRouter = require('./typeRouter')
const brandRouter = require('./brandRouter')
const userRouter = require('./userRouter')
router.use('/user', userRouter)
router.use('/device', deviceRouter)
router.use('/brand', brandRouter)
router.use('/type', typeRouter)
module.exports = router
routes/deviceRouter.js
const Router = require('express')
const DeviceController = require('../controllers/deviceController')
const router = new Router();
router.post('/', DeviceController.create)
router.get('/', DeviceController.getAll)
router.get('/:id', DeviceController.getOne)
module.exports = router
controllers\deviceController.js
const uuid = require('uuid')
const path = require('path')
const {Device} = require('../models/models')
const ApiError = require("../errors/ApiError")
class DeviceController
{
async create(req, res, next)
{
console.log(req);
try{
const {name, price, brandId, typeId, info} = req.body;
const {img} = req.files;
let fileName = uuid.v4() + ".jpg";
img.mv(path.resolve(__dirname, '..', 'static', fileName));
const device = await Device.create({name, price, brandId, typeId, img: fileName});
return res.json(device);
}
catch(e)
{
next(ApiError.badRequest(e.message));
}
}
async getAll(req, res)
{
const {brandId, typeId} = req.query
let devices;
if(!brandId && !typeId)
{
devices = await Device.findAll()
}
if(brandId && !typeId)
{
devices = await Device.findAll({where: {brandId}})
}
if(!brandId && typeId)
{
devices = await Device.findAll({where: {typeId}})
}
if(brandId && typeId)
{
devices = await Device.findAll({where: {brandId,typeId}})
}
return res.json(devices)
}
async getOne(req,res)
{
}
}
module.exports = new DeviceController()
I logged the request and saw that the request body came up empty and req.files is undefined.
I compared these router and controller with others and found no differences in structure.
What am I doing wrong?
You cannot retrieve files of form-data directly from req.files.
Use formidable (it's an npm package) to parse form-data and work with it easily. Here are the steps:
Run npm install formidable.
Import formidable and fs package in your controller script with:
const formidable = require('formidable')
const fs = require('fs');
Change your create function with this:
async create(req, res, next) {
let form = new formidable.IncomingForm()
form.keepExtensions = true
form.parse(req, async (err, fields, files) => {
// console.log('err', err)
// console.log('fields', fields)
if (err) {
next(ApiError.badRequest(err));
return;
}
const { name, price, brandId, typeId, info } = fields; // <-- fields contain all your text data. it's like req.body
const { img } = files; // <-- it should work now!
let fileName = uuid.v4() + ".jpg";
fs.writeFileSync(path.resolve(__dirname, '..', 'static', fileName), img);
const device = await Device.create({ name, price, brandId, typeId, img: fileName });
return res.json(device);
}
}

Emit socket on post request

I`m trying to create emit for the socket on post request from postman but got some troubles. I found an issue here but it seems not working for me. I have this code in my app.js
App.js
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const http = require('http').createServer(app)
const eventsRoute = require('./routes/eventsRoute')
const io = require('socket.io')(http, {
cors: {origin: "*"},
path: '/api/events'
})
io.of('/api/events/').on('connection', socket => console.log('connected'))
...
app.use('/api/events', eventsRoute(io))
module.exports = app
And here I got eventsRoute.js code. Here, I think, is the main problem
eventsRoute.js
const express = require('express')
const errorHandler = require('../utils/errorHandler')
const router = express.Router()
const returnRouter = io => {
router.post('/', async (req, res) => {
try {
io.sockets.emit('create', req.body)
res.status(200).json({message: 'successful'})
} catch (e) {
errorHandler(e)
}
})
router.get("/", function (req, res) {
try {
res.send({})
} catch (e) {
errorHandler(e)
}
})
return router
}
module.exports = returnRouter
And on my client side, I have some code in the script tag. Here it is
<script src="https://cdn.socket.io/3.1.3/socket.io.min.js"></script>
<script>
const socket = io('ws://localhost:5000', {path: '/api/events'})
socket.on('create', data => {
console.log(data)
})
</script>
So, the main reason was in ... all files. Let's begin with index.js. I'm exporting the app. Instead of that, I have to export http. Then in app.js, I will have to change line with sockets to this
const io = require('socket.io')(http, options)
io.of('/api/events')
.on('connection', socket => {
app.use('/api/events', eventsRoute(socket))
})

How to upload (pdf) file from FileReader to node js Express app

I have a (React) js app that reads a PDF file using FileReader then uses fetch to send it to a node js server running Express. When the request is received on the server side, however, the request body is undefined. What is missing from my code for this to work?
Client side:
function readFile() {
let file = fileInputRef.current.files[0];
const reader = new FileReader();
return new Promise((resolve) => {
reader.onload = function (e) {
resolve(e.target.result);
};
reader.readAsDataURL(file);//readAsDataURL, readAsArrayBuffer, or readAsBinaryString?
});
}
function handleSubmit(event) {
event.preventDefault();
readFile().then((value) => {
fetch('/gen/file', {
method: 'POST',
body: value
})
});
Server side:
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser')
const app = express();
const jsonParser = bodyParser.json()
const port = 3000;
app.post("/gen/file", function (req, res, next) {
console.log(req.body);//undefined - Why????
});
app.listen(port, function (err) {
if (err) console.log(err);
});
Client Side:
function getBase64(file,callback){
const reader = new FileReader();
reader.addEventListener('load',()=> callback(reader.result));
reader.readAsDataURL(file);
}
function handleSubmit(event) {
event.preventDefault();
let body = {};
getBase64(file,fileUrl=>{
body.file = fileUrl;
fetch('/gen/file', {
method: 'POST',
body
})
})
Server Side:
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser')
const app = express();
const jsonParser = bodyParser.json()
const port = 3000;
const fs = require('fs');
app.post("/gen/file", function (req, res, next) {
console.log(req.body);//undefined - Why????
let file = req.body.file;
let base64 = file.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
var buffer = new Buffer.from(base64[2],'base64');
fs.writeFile(__dirname+"/out.jpeg", buffer, 'base64', function (err) {
console.log(err);
});
});

Resources