Trying to keep my code organized. I have a controller directory and router directory. The goal is to make an api call and retrieve data.
CONTROLLER
function searchName(req, res) {
res.setHeader("user-key", process.env.APIKEY)
res.redirect(`https://api-endpoint.igdb.com/games/?search=${req.params.game}&fields=*`)
}
ROUTER
router.get('/search/:game', Controller.searchName)
I export router, and require it in my server.js file. In POSTMAN, this works fine; I do have my API Key hard coded in Postman. I've tried many different methods but can't seem to pass the header with my ApiKey when making the initial request in the code. Additionally, being new to nodejs, I'm not sure if the request redirect is sufficient in this scenario. Ultimately, setHeader is not working
It sounds like you're trying to make an async call to another service, pull back some data, and send that back to the client that called your service. In a nodejs application it's quite trivial to make a new HTTP request. Nodejs has a built in HTTP.Agent, but I'd suggest trying out the axios library as it makes it even easily. This will result in something like this:
const axios = require('axios');
function searchGame(req, res) {
const requestOptions = {
method: 'GET',
uri: `https://api-endpoint.igdb.com/games/?search=${req.params.game}&fields=*`,
headers: {
"user-key": process.env.API_KEY
}
}
axios.get(requestOptions)
.then(function (response) {
// This is the data the remote service gave back
res.send(response.data);
})
.catch(function (error) {
// The remote gave an error, lets just forward that for now
res.send({error: error});
});
}
Here we build an axios request from the req object, we make the request using axios.get, we then wait for the "promise" (this is an async javascript concept if you've not come across it before), then we take the response from that and forward it back as the res. Lets pretend for argument sake that this is a JSON response, but it could be anything.
Related
In my svelte-kit application I was struggeling with this NODE error ERR_INVALID_URL but was able to fix it with a solution provided in this thread. Unfortunately a deeper explanation as of why NODE can't parse the url - which is obviously only a valid route when the code runs on the client - was ommitted.
In svelte-kit's load function I'm implicitly fetch -ing an, from nodejs' perspective, invalid url (ERR_INVALID_URL)
So what I'd love to understand is, WHY does NODE fail to resolve/parse the given url?
Prerequisits:
// in $lib/utils/http.js
export function post(endpoint, data = {}) {
return fetch(endpoint, {
method: "POST",
credentials: "include",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
},
}).then((r) => r.json());
}
// in routes/auth/login.js -> this endpoint can't be be found by NODE
export async function post({ locals, request }) {
// ...code here
return {
body: request.json()
}
}
Here the distinction has to be made of whether the code runs on the client or on the server:
// in routes/login.svelte
import { browser } from '$app/env';
import { post } from '$lib/utils/http.js';
export async function load() {
const { data } = someDataObject;
if (browser) { // NODE wouldn't be able to find the endpoint in question ('/auth/login'), whereas the client does
return await post(`/auth/login`, { data }).then((response) => {
// ...do something with the response
});
}
return {};
}
Thanks for any explanation that sheds some light into this.
You should refactor your load function to use the fetch provided by SvelteKit. This will allow you to use relative requests on the server, which normally requires an origin. From the docs (emphasis mine):
fetch is equivalent to the native fetch web API, with a few additional
features:
it can be used to make credentialed requests on the server, as it inherits the cookie and authorization headers for the page request
it can make relative requests on the server (ordinarily, fetch requires a URL with an origin when used in a server context)
requests for endpoints go direct to the handler function during server-side rendering, without the overhead of an HTTP call
during server-side rendering, the response will be captured and inlined into the rendered HTML
during hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request
So, get the fetch from the parameter passed to load...
export async function load({ fetch }) {
const { data } = someDataObject;
return await post(`/auth/login`, fetch, { data }).then((response) => {
// ...do something with the response
});
}
... and use it in your post function
// in $lib/utils/http.js
export function post(endpoint, fetch, data = {}) { /* rest as before */ }
A future enhancement to SvelteKit may make it so you don't have to pass fetch to your utility function, but this is what you have to do for now.
I have a website that will use EJS templating to display data from my database. Instead of creating a second API just to do this, I would like to use the pre-existing API that other developers can use. Is there a way to get data from my API in the most meta way possible.
main app route
app.get("/facts/random", async (req, res) => {
/* Make request to /api/random here and get JSON data */
});
API route
app.get("/api/random/", async (req, res) => {
let results = await Fact.find();
const randomResult = Math.floor(Math.random() * results.length);
return res.json(results[randomResult]);
})
I want to make a request from the first route to the second route using the most meta way possible.
If /api/random is in another microservice then you can use Axios to make an http call, but if it is in the same Express App then refactor the code and turn it into a function and call that function in both the controllers.
Usually, you don't have your own http server make a request to itself. Usually, you just factor out the functionality the other API is using into an exported function and you call the function directly.
It's definitely more efficient that way since there's really no reason to package it up into an http request, go through the network stack, parse it as an http request in your server, process it, create an http response, send that back through the network stack, parse the response, then process it when you could avoid 6 of those 8 steps by just calling a common function you factored out.
app.get("/facts/random", async (req, res) => {
let randomResult = await apiRandom();
...
});
async function apiRandom() {
let results = await Fact.find();
const randomResult = Math.floor(Math.random() * results.length);
return results[randomResult];
}
app.get("/api/random/", async (req, res) => {
let randomResult = await apiRandom();
res.json(randomResult);
});
If, for some reason, you do want to make requests to your own http server, then get a library like got() or axios() that makes that easy.
I've been researching this issue for several hours now and found something odd. Using ExpressJS, Firebase, and React for a small app, and need to call the Firebase Database via the Express Backend, and I also need to make post requests to store data in the database via the Express Backend.
Functionality: I make a post request to the backend to add data to the database. Since Firebase is real time db, the data will immediately reflect on the page.
Problem: The issue is, when I make a post call to the backend and that completes, the page refreshes but the data doesn't show because of this
ERROR: [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
/**
* Add new note to Firebase
* Real-Time Database
*/
app.post('/addNote', (req, res)=> {
var title = req.body.note.title;
var body = req.body.note.body;
var userId= req.body.note.uid;
db.notes.push({
title: title,
body: body,
uid: userId
})
res.send("Success")
})
app.get('/all', (req, res, next)=> {
db.notes.on('value', snapshot => {
return res.send(snapshot.val());
})
})
Possible Solution: I've found that using the code below, I can make a post request, manually refresh the page, and the data will reflect with no header error. I'm trying to code the proper functionality but can't seem to figure out where the code is sending multiple responses with the db.notes.on because I'm only sending res.send one time. The clear difference is (.on listens and updates immediately, while .once requires manual refresh)
/**
* Add new note to Firebase
* Real-Time Database
*/
app.post('/addNote', (req, res)=> {
var title = req.body.note.title;
var body = req.body.note.body;
var userId= req.body.note.uid;
db.notes.push({
title: title,
body: body,
uid: userId
})
res.send("Success")
})
app.get('/all', (req, res, next)=> {
db.notes.once('value', snapshot => {
return res.send(snapshot.val());
})
})
An on("value" listener to Firebase will fire:
straight away with the current value of the data,
and will then later also fire when the data changes.
Since you're sending the data in the response to the client in #1, the response will be closed/finished by the time #2 happens.
By using a once("value" listener this problem doesn't happen, since once() removes the listener after #1.
I am trying to make a slack app and to complete Oauth2, I have to send the URI below and get a JSON response back in the body.
The problem is, every time I am trying to use the function request() in my app.get() function, ejs is always trying to go and get my views. Now I tried rendering my specific view for app.get() but then when I use request() again, ejs is again trying to get a view.
How can I redirect to another url from my app.get and receive the JSON. I can use req.redirect() but I don't know how to get the response back.
Please please help! Thanks
app.get('/', (req, res) =>{
var options = {
uri: 'https://slack.com/api/oauth.access code='+req.query.code+'&client_id='+client_id+'&client_secret='+client_secret,
method: 'GET'
}
request(options, (error, response, body) => {
var JSONresponse = JSON.parse(body)
if (!JSONresponse.ok){
console.log(JSONresponse)
res.send("Error encountered: \n"+JSON.stringify(JSONresponse)).status(200).end()
}else{
console.log(JSONresponse)
res.send("Success!")
}
})
})
so I have the following Scenario; I have a private API key that Angular will show in XHR request. To combat this, I decided to use Express as a proxy and make server side requests. However, I cannot seem to find documentation on how to make my own get requests.
Architecture:
Angular makes request to /api/external-api --> Express handles the route and makes request to externalURL with params in req.body.params and attaches API key from config.apiKey. The following is pseudocode to imitate what I'm trying to accomplish:
router.get('/external-api', (req, res) => {
externalRestGetRequest(externalURL, req.body.params, config.apiKey)
res.send({ /* get response here */})
}
You are half way there! You need something to make that request for you. Such as the npm library request.
In your route something like
var request = require('request');
router.get('/external-api', function(req, res){
request('http://www.google.com', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred and handle it
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
res.send(body)
});
})
This allows you to make any type of request using whatever URL or API keys you need. However it's important to note you also need to handle any errors or bad response codes.
The accepted answer is good, but in case anyone comes across this question later, let's keep in mind that as of February, 2020, request is now deprecated.
So what can we do? We can use another library. I would suggest Axios.
Install it and do something like:
const axios = require('axios')
const url = "https://example.com"
const getData = async (url) => {
try {
const response = await axios.get(url)
const data = response.data
console.log(data)
} catch (error) {
console.log(error)
}
}
getData(url)