How to make a NodeJS request and pipe it into another response - javascript

So, i'm doing an api, to query a certain map service which require an API key. I want to keep the API key private so in my own api on the server, I will call a http.request to the map service, then immediately pipe it into the response to my own api user.
Here is sample code to illustrate the idea:
import http from "http";
export default function handler(req, res) {
http.request(`http://map.service.example.com/foo-bar`, (mapRes) => mapRes.pipe(res));
}
But so far, the code above doesn't work.
Any other possible way (with fetch maybe?) is welcome.

For an http.request to go through you have to call its end method:
import http from "http";
export default function handler(req, res) {
const mapReq = http.request(`http://map.service.example.com/foo-bar`, (mapRes) => mapRes.pipe(res));
mapReq.end();
}
OR
You can use the get method:
import http from "http";
export default function handler(req, res) {
http.get(`http://map.service.example.com/foo-bar`, (mapRes) => mapRes.pipe(res));
}

Related

Why Doesn't the Fetch API Call My API Endpoint I Created in Express?

I am wondering why the Fetch API in javascript isn't call the endpoint I created in express. When I start my server and go to the '/characters' endpoint, it returns correctly formatted JSON.
Here is my express index.js
const app = express();
const PORT = 3000;
const charactersRoute = require('./routes/characters');
//Characters Route
app.use('/characters', charactersRoute)
app.listen(PORT, function(err) {
if(err) console.log(err);
console.log(`Server is listening on port ${PORT}`)
})
Here is my Characters Route
const express = require('express'); // Web Framework
const https = require('https');
const router = express.Router();
const PORT = 3000;
// app.listen(PORT, function(err) {
// if(err) console.log(err);
// console.log(`Server is listening on port ${PORT}`)
// })
const api = 'https://www.breakingbadapi.com/api/characters/?limit=20';
router.get("/", function(req, res) {
https.get(api, (response) => {
console.log(response.statusCode);
response.on('data', (d) => {
const data = JSON.parse(d);
res.send(data);
})
// res.send("Running")
})
})
module.exports = router;
Here is my Characters.jsx file where I'm using Fetch
import React, {useEffect, useState} from 'react';
import Card from '#mui/material/Card';
import axios from 'axios';
export default function Character() {
const [data, setData] = useState();
useEffect(() => {
fetch('/characters')
.then(res => res.json())
.then(data => setData(data))
// console.log(data);
}, []);
}
When I run my front end and check the response I receive, it returns my index.html.
If I fetch the API URL itself, then it correctly returns the JSON in my frontend. But When I try to fetch the API endpoint I created in express, I don't get any data. Any suggestions?
You did not set the endpoint for the fetch function. It doesn't know what API '/characters' is. It is similar to saying the house number but not telling the street, you don't know where to go. So you need to pass the absolute path to fetch to request data from the server, because the server is a different 'entity', it's not the same with your front-end. Therefore, you need to provide the full API URL. Or, if using axios, since I see you imported it above, you must set axios.defaults.baseURL = <API_URL>, (or provide the full URL in the request itself just like in fetch) and then make the request with the help of axios, not fetch.
Therefore your React code will look a little something like this:
import React, {useEffect, useState} from 'react';
import Card from '#mui/material/Card';
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000';
export default function Character() {
const [data, setData] = useState();
useEffect(() => {
const getData = async () => {
try {
const {data} = await axios.get('/characters');
setData(data);
} catch (err) {
console.error(err);
}
};
getData();
}, []);
}
But I suggest you create a different file with all the axios requests and there you set the baseUrl - that's a better practice.
I think is that you are not using cors in your app.
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
Try this, I hope it helps you!
Error pattern
This kind of error occurs when we send a request from the client-side to the server-side using a relative URL (/characters in this case). But the frontend app and the backend app run on 2 different ports.
Reason
When we use relative URLs, the URL will be concatenated with the current host (the frontend host, not the backend). Usually, we receive a 404 error because the resource doesn't exist.
Example
Backend Express app running on port 5000. React app running on port 3000 for development. In React app, if we send a request to /users, then the full URL is http://localhost:3000/users. The request goes to the React app, not the backend server. And we don't receive the desired output.
Action
You should use an absolute URL, something like: http://localhost:5000/users to send the request to your backend app. Consider saving the host part (http://localhost:5000) in a global variable in order to use it in multiple places in your frontend code. When you have different environments for the backend (DEV/STAGING/PRODUCTION), you can change the backend host in only 1 place.

What is the best way to make a request from a node express app to a third party api that requires its own authentication?

I have a node express app that uses keycloak authentication to secure all API endpoints. Express middleware has been set up for authentication to make sure that each incoming request from the front end has the appropriate keycloak token. I need to make a post request from my node app to a third party backend API to subscribe users to an email service that uses a different authentication method which my middleware would not work with.
What would be the best practice for making a request from the third party API? I am considering creating a new express instance and use a separate middleware specific for that post request. Is this an ok thing to do or is there a better way?
Here is a simplified version of my node app. See the
index.js
import { authmware } from "./authmware";
import express from "express";
import { router } from "./router";
const app = express();
authmware(app);
router(app);
app.use((err, req, res, next) => {
logger.error(err.message);
const code = err.code ? err.code : 500;
const message = err.message ? err.message : "Internal Server Error";
res.status(code).json({ error: message, success: false });
});
export default app;
router.js
import express from "express";
import createProfile from "../../controllers/createProfile";
const router = express.Router();
router.post("/", createProfile);
export const router = (app) => {
app.use("/api/v1/createProfile", router);
};
controllers/createProfile.js
const createProfile = async (req, res) => {
// ... Do some stuff
// ** make request to a different api here **
await makeThirdPartyApiRequest();
}
How would I make this third party api request that uses a different style of authentication?
This is a very common use case. You can use 10 third party APIs in your node server and all having different authentication mechanisms irrespective of the auth you are using for your client requests.
await makeThirdPartyApiRequest();
// http request to API
// attach proper auth headers (API key / jwt / basic auth / oAuth token). This will be based on the authentication method the API is offering.
}
Update based on your recent comment:
The API should have some documentation on how to authenticate using the user key and secret key. For example: Google APIs just require you to send API key with request https://cloud.google.com/api-keys/docs/overview

Set a module export before the file finishes in Node.js module (`import`/`export` syntax)

I am creating an API using express and I want to have a file for each endpoint. Ideally, I'd want to create the router in it's file, have it be exported and then import the endpoints files so they're run. The endpoint files would import the router again and then use .get, or .post or whatever to create the actual endpoint on the router.
So basically, my goal is to have one file run another file that uses a value the first file exports before running the different file.
I've done this before in a project using the require() syntax, but now I am trying to translate this to import/export syntax for a different project.
Before, I've (successfully) used something like this in another project:
Router.js
const express = require("express");
// Create and export a new router
const TestRouter = express.Router();
module.exports = TestRouter;
// Make the endpoints register themselves
require("./getdata.js");
require("./createdata.js");
And then in the endpoints (getdata.js and createdata.js):
const TestRouter = require("./TestRouter.js");
TestRouter.get("/:id", async (req, res) => {
// ...
});
Now, I am trying to do the same with package syntax (import/export):
TestRouter.js
import { Router } from "express";
// Create and export our new router
const TestRouter = Router();
export default TestRouter;
// Import endpoints so they can register themselves
import "./getdata.js";
import "./createdata.js";
Endpoints (getdata.js and createdata.js):
import TestRouter from "./TestRouter.js";
TestRouter.get("/:id", async (req, res) => {
// ...
});
But this direct translation doesn't work.
When I now try to run this project, I get an error like this:
file:///.../src/routes/getdata.js:3
TestRouter.get("/:id", async (req, res) => {
^
ReferenceError: Cannot access 'SessionRouter' before initialization
at file:///.../src/routes/getdata.js:3:1
...
My most likely guess for the problem at the moment would be that exports in modules aren't actually set at the time of the export statement, but rather when their respective file reaches the end after processing everything else.
So my question would be if there was a way to set the export at the time of the export statement, or perhaps an easy way (about 1-2 lines in the endpoint files) to make the endpoints wait for the TestRouter to actually be set before calling .get, .post or whatever on it.
I know I could have the endpoint files just export the actual endpoint handlers:
const getdata = async (req, res) => {
// ...
}
export default getdata;
and then have the .get calls in the router file:
import { Router } from "express";
// Import endpoint handlers
import getdata from "./getdata.js";
// Create our new router
const TestRouter = Router();
// Register endpints
TestRouter.get("/:id", getdata);
But I would like to avoid this - if at all possible - because every endpoint will need different middleware configurations, meaning that I have to specify all the middleware in the TestRouter.get (etc.) calls, which I don't really want to cram into the router file due to readability.
You can solve this by splitting TestRouter.js into two parts:
The part that creates the router (let's leave that in TestRouter.js)
The part that loads the endpoints (let's call that main.js)
...and make the second part your entry point. That breaks the cycle between TestRouter.js and getdata.js/createdata.js that causes the latter to try to access the exported binding from TestRouter.js before it's initialized.
main.js:
// Create and export our new router
import TestRouter from "./TestRouter.js";
// Or, actually, just: import "./TestRouter.js";
// Import endpoints so they can register themselves
import "./getdata.js";
import "./createdata.js";
TestRouter.js:
import { Router } from "express";
// Create and export our new router
const TestRouter = Router();
export default TestRouter;
getdata.js / createdata.js remain unchanged.

Using https with Axios request in Nestjs

I currently have a Nestjs server setup and am attempting to perform an Axios request when one of the endpoints is hit with a GET request. Here is the controller.ts code:
#Controller()
export class TestController {
constructor(private readonly testService: TestService) {}
#Get('testData')
testData() {
return this.testService.testData();
}
}
Service.ts:
#Injectable()
export class TestService {
status(): string {
return 'OK'
}
testData(): Promise<any> {
return helper.getTestData();
}
}
Where helper.getTestData() is just a call to a helper file with the following function:
export async function getTestData(): Promise<any> {
const result = await axios({
url: tempURL,
method: 'GET',
timeout: 3000,
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
});
I am able to hit this endpoint tempURL but encounter the following error message: Cannot read property 'Agent' of undefined. I know that the endpoint I am attempting to hit requires a cert, which is why I must include the httpsAgent argument inside the Axios request. If I don't include the httpsAgent argument, I receive the following message Error: unable to verify the first certificate in nodejs.
Is there a way to configure Nestjs to work with https? Or is there another way to handle this authorization issue inside of Nestjs? Using Postman everything works fine so I'm assuming it is a Nestjs issue. Any help is appreciated.
instead of import https from 'https'; you should use the namespace import: import * as https from 'https'; or set the esModuleInterop to true in your tsconfig file (under compilerOptions)

How to call restfull API inside router using express

I want to retrieve data that is at this link: https://api.rajaongkir.com/starter/cost using express.js.
I created the Single Page Application website using react.js for frontend so i need to call this route : /shipping/check/cost in my backend for get the data. but i dont know how to request inside router express.
I have never done a Restful API from someone else's website.
I just copied what was in the documentation, but in the documentation using the node.js not express.js. https://rajaongkir.com/dokumentasi/starter#cost-response
when I run this I get nothing.
My routes
import {Router} from 'express';
import * as ShippingRouter from './controller'
const routes = new Router();
routes.post('/shipping/check/cost',ShippingRouter.checkCost);
export default routes;
Controller
import db from '../../config/conn';
import keys from '../../config/keys';
import jwt from 'jsonwebtoken';
import qs from 'querystring';
import request from 'request';
export const checkCost =(req,res)=>{
var options = {
"method": "POST",
"hostname": "api.rajaongkir.com",
"port": null,
"path": "/starter/cost",
"headers": {
"key": "mykey",
"content-type": "application/x-www-form-urlencoded"
}
};
var reqCost= https.request(options,function(ress){
var chunks = [];
ress.on("data", function (chunk) {
chunks.push(chunk);
});
ress.on("end", function () {
var body = Buffer.concat(chunks);
res.json(body.toString());
});
})
reqCost.write(qs.stringify({
origin: '501',
destination: '114',
weight: 1700,
courier: 'jne'
}));
reqCost.end();
}
The first thing I would check is if the POST HTTP verb you are using is the correct one. You said you wanted to get data from the API, in this case, you should be using GET instead of POST (check the API documentation to know more).
Besides that, please check this repository where I'm using express and how I handle calls.

Categories