Express.js Server: PUT Request using Middleware - javascript

I have a working Node/Express server and I'm using it to do requests via localhost to an external API. As you can see in my example code, I'm using node-fetch for my basic GET requests.
For every request, I prepare a const url = BASE_URL in advance, needed for the actual external server request.
But I'm getting stuck at my PUT-Request as I can't using node-fetch. So what do I have to do, to notify my Express server with the actual URL for the PUT-Request?
The PUT-Request doesn't work til here.
/* Route: Get Appointment it's availability times */
app.get('/availability/times/:id/:date/:locationId', (req, res) => {
var id = req.params.id;
var date = req.params.date;
var locationId = req.params.locationId;
const url = BASE_URL + '/availability/times?appointmentTypeID=' + id + '&date=' + date + '&calendarID=' + locationId;;
fetch(url, {
mode: "no-cors",
method: "GET",
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'content-type'
},
})
.then(response => response.json())
.then(json => {
res.json(json);
});
});
app.put('/appointments/:id/cancel', (req, res) => {
var id = req.params.id;
const url = BASE_URL + '/appointments/' + id + '/cancel';
res.send(req.body)
});

If you're saying that fetch is undefined in your put request, make sure you are requiring it at the top before any routes var fetch = require('node-fetch'). For your base url, you should store that in a config file. Create a file called config.js that looks something like this:
module.exports = {
'BASE_URL': 'yoururlhere'
}
then require this in your express server var config = require('pathToConfig'); and you can use it by specifying config.BASE_URL
If that doesn't help, please be more specific with what your actual problem is

Related

Etsy API authetication - Cannot GET error

I'm very rusty in javascript and haven't touched it in a decade so I was following an Etsy tutorial on how to request an API access token.
I'm running a node.js node on my localhost and ngrok to get a proper URL for it (seems like localhost doesn't work for etsy). The authentication seems to work (I can log in in etsy with it), until the part in the tutorial where I need to send the API information onto the next page (to actually start pulling the etsy store data).
As soon as the etsy authentication page get redirected to the next page I alway get the error "Cannot GET views/index.hbs"
The page is in the views folder in the project folder.
I'm really not sure what the problem is, maybe the way the files are structured in the folder?
Thanks a lot for the help.
This is how the code looks like:
// Import the express library
const express = require('express')
const fetch = require("node-fetch");
const hbs = require("hbs");
// Create a new express application
const app = express();
app.set("view engine", "hbs");
app.set("views", `${process.cwd()}/views`);
// Send a JSON response to a default get request
app.get('/ping', async (req, res) => {
const requestOptions = {
'method': 'GET',
'headers': {
'x-api-key': 'xxxxxxxxxxxxxxx',
},
};
const response = await fetch(
'https://api.etsy.com/v3/application/openapi-ping',
requestOptions
);
if (response.ok) {
const data = await response.json();
res.send(data);
} else {
res.send("oops");
}
});
// This renders our `index.hbs` file.
app.get('/', async (req, res) => {
res.render("index");
});
/**
These variables contain your API Key, the state sent
in the initial authorization request, and the client verifier compliment
to the code_challenge sent with the initial authorization request
*/
const clientID = 'xxxxxxxxxxxxxxxx';
const clientVerifier = 'xxxxxxxxxxxxxxxxxxxxxx';
const redirectUri = 'https://xxxxxxx/views/index.hbs';
app.get("/oauth/redirect", async (req, res) => {
// The req.query object has the query params that Etsy authentication sends
// to this route. The authorization code is in the `code` param
const authCode = req.query.code;
const tokenUrl = 'https://api.etsy.com/v3/public/oauth/token';
const requestOptions = {
method: 'POST',
body: JSON.stringify({
grant_type: 'authorization_code',
client_id: clientID,
redirect_uri: redirectUri,
code: authCode,
code_verifier: clientVerifier,
}),
headers: {
'Content-Type': 'application/json'
}
};
const response = await fetch(tokenUrl, requestOptions);
// Extract the access token from the response access_token data field
if (response.ok) {
const tokenData = await response.json();
res.send(tokenData);
} else {
res.send("oops");
}
});
app.get("/welcome", async (req, res) => {
// We passed the access token in via the querystring
const { access_token } = req.query;
// An Etsy access token includes your shop/user ID
// as a token prefix, so we can extract that too
const user_id = access_token.split('.')[0];
const requestOptions = {
headers: {
'x-api-key': clientID,
// Scoped endpoints require a bearer token
Authorization: `Bearer ${access_token}`,
}
};
const response = await fetch(
`https://api.etsy.com/v3/application/users/${user_id}`,
requestOptions
);
if (response.ok) {
const userData = await response.json();
// Load the template with the first name as a template variable.
res.render("welcome", {
first_name: userData.first_name
});
} else {
res.send("oops");
}
});
// Start the server on port 3003
const port = 3003;
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});

How to send data to the client and save it as a cookie

I know the basics of coding but I'm trying to understand API's, at the moment I'm trying to make an API that authorizes a user so I can see their information in a game.
Essentially I need to send data to my client from my server which is running Node.js and Express. I have managed to get the user authenticated but I then need to save that information as a cookie for later use.
The webapp starts on index.html and the API redirects the user back to auth.html.
Server Side Code
require('dotenv').config();
const express = require('express');
const {
addAsync
} = require('#awaitjs/express');
const app = addAsync(express());
const path = require('path');
const url = require('url');
const fetch = require("node-fetch");
const base64 = require('base-64');
const http = require('http');
// config libraries
const client_secret = process.env.CLIENT_SECRET;
// get env variables
function getCode(req) {
var ru = url.format({
protocol: req.protocol,
host: req.get('host'),
pathname: req.originalUrl
});
return ru.split("code=")[1];
}; // parse url to get auth code
const port = process.env.PORT || 4645;
app.listen(port, () => {
console.log(`listening on port ${port}`);
}); // set http server
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
}); // set '/' as index.html
app.getAsync('/auth', async (req, res) => {
res.sendFile(path.join(__dirname, 'auth.html'));
const code = getCode(req);
const options = {
method: 'POST',
headers: {
'Authorization': `Basic ${base64.encode(`35544:${client_secret}`)}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `grant_type=authorization_code&code=${code}`
}
const obj = await fetch('https://www.bungie.net/platform/app/oauth/token/', options); // response
const data = await obj.json(); // json response = data
console.log(data);
// send json to client
res.json(data);
res.end();
});
app.get('/logout', async (req, res) => {
res.redirect('/');
});
Client Side Code (index.html)
<head>
<script>
// code
</script>
</head>
<body>
index.html <br>
<a href='https://www.bungie.net/en/OAuth/Authorize?client_id=35544&response_type=code'>log in</a> <br>
</body>
Client Side Code (auth.html)
<head>
<script>
// catch json from server
const options = {
url: '/auth',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
fetch(options).then(response => {
console.log(response);
})
</script>
</head>
<body>
auth.html <br>
<a href='/logout'>log out</a>
</body>
I know it's a lot but I hope someone can help me on this...
Thanks.
Edit:
I forgot to say that currently the client does not recieve the information at any point, and if it did i am unsure how to catch the response at the right time...
Thanks to everyone who already responded.
Without bothering to puzzle-out your code ... "never trust the client."
Never try to send the client any meaningful data as the content of a cookie. The cookie's value should always be a perfectly-meaningless value – a "nonce" – which you can then refer to in order to look up anything you need to know from your server-side database. "You can never trust the client-side."

Parse response body from request and return back to client

I am new to node.js and JavaScript in general, but I'm trying to use node.js as a simple REST API. A client request comes to node.js, then node.js reaches out to a database to perform crud operations and returns the response back. However, I would like to be able to parse this response and format it in my own JSON before sending back to the client. Right now im using the request library and .pipe() to send back. Can I .pipe() into a variable that I could then parse or do I need to change my approach entirely?
Here is what my code looks like at the current moment:
const request = require('request');
var username = "admin";
var password = "admin";
var auth = "Basic " + new Buffer(username + ":" + password).toString("base64");
exports.getPosts = (req, res, next) => {
request({
uri: 'http://localhost:8000/LATEST/search?q=caesar',
headers: {"Accept": "application/json",
"Authorization": auth
}
}).pipe(res);
};
I am aware that request has been deprecated so maybe there is a better way to do this currently. I appreciate any help and feedback as I'm new to this.
You can use request module or axios or anything. You can just save the JSON body and process it and sent it to the client. Below is how to do it.
const request = require('request');
var username = "admin";
var password = "admin";
var auth = "Basic " + new Buffer(username + ":" + password).toString("base64");
exports.getPosts = (req, res, next) => {
request({
uri: 'http://localhost:8000/LATEST/search?q=caesar',
headers: {
"Accept": "application/json",
"Authorization": auth
}
// You can set json:true here so that you don't have to do JSON.parse below.
}, (err, response, body)=>{
//body is the json body
const jsonBody = JSON.parse(body);
//do something with json request
res.json(jsonBody);
})
};

Sending an image with axios to Node.js and then using it further

I am trying to upload an image from the front-end, post it with axios to back-end (node.js) and then from there post it again to the GroupMe image service.
The main thing is to avoid using the API token in the front-end and so I was trying to first send a request to the back-end and then send the actual API request to the GroupMe image service which expects to get FormData of an image and sends back converted image URL.
I have tried to send FormData directly to the GroupMe image service from the front-end and everything works fine. However, in order to do so, I had to store the token in the front-end, which is not a good idea I believe.
The working code below:
let config = {
headers : {
'X-Access-Token': myToken,
'Content-Type' : 'multipart/form-data'
}
}
let fd = new FormData()
fd.append('name', 'image')
fd.append('file', fileToUpload)
axios.post'(https://image.groupme.com/pictures', fd, config)
.then((response)=>{
console.log(response)
})
.catch(err =>{
console.log(err.response)
})
What I need to happen instead is to send the request to the back-end like so:
axios.post(process.env.baseUrl+'/messengerRequests/upload-file/', fd, config)
.then((response)=>{
console.log(response)
})
.catch(err =>{
console.log(err.response)
})
And now in the back-end somehow be able to get that FormData and then create another post request to the GroupMe image service as I initially did in the front-end.
sendMessage: async(req, res) => {
axios.post('https://image.groupme.com/pictures', ???, config)
.then((response)=>{
res.send(response)
})
.catch(err =>{
console.log(err.response)
})
}
I do not know where it appears in the axios request. There is nothing in the req.body or req.params so I am not able to simply pass it further for the next post.
Is there a way somehow pass this FormData again?
Or maybe there is a way to safely use the token in the frond-end?
So, it should be relatively straightforward to post the image to GroupMe using Node.js and Express / Multer / Request. I've gone for Request rather than Axios on the backend since I'm more familiar with the API, but it's the same difference really.
Node.js Code (index.js)
const request = require("request");
const express = require("express");
const multer = require("multer");
const upload = multer();
const app = express();
const port = 3000;
const myToken = "" // Your API token goes here.
app.use(express.static("./"));
/* Here we take the image from the client and pass it on to GroupMe */
app.post("/uploadFile", upload.any(), (req, res) => {
sendImageToGroupMe(req, res);
});
function sendImageToGroupMe(req, res) {
const options = {
uri: "https://image.groupme.com/pictures",
body: req.files[0].buffer,
method: "POST",
headers: {
"X-Access-Token" : myToken
}
}
request(options, (err, response, body) => {
console.log("Request complete: Response: ", body);
if (err) {
console.error("Request err: ", err);
res.status(500).send("Upload failed: ", err.message);
} else {
res.status(201).send("Upload successful: GroupMe response: " + body);
}
});
}
app.listen(port);
Client side
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
function uploadFile() {
var fileToUpload = document.querySelector('input[type=file]').files[0];
let config = {
headers : {
'Content-Type' : 'multipart/form-data'
}
}
let fd = new FormData()
fd.append('name', 'image')
fd.append('file', fileToUpload)
axios.post('http://localhost:3000/uploadFile', fd, config)
.then((response)=>{
console.log("Image posted successfully: ", response);
showOutput("Image posted successfully: " + response.data);
})
.catch(err =>{
console.error("Image post failed: ", err)
showOutput("Image post failed!");
})
}
function showOutput(html) {
document.getElementById("output").innerHTML = html;
}
</script>
</head>
<body style="margin:50px">
<input type="file" onchange="uploadFile()"><br>
<p id="output"></p>
</body>
</html>
All files go in the same directory. You can go to http://localhost:3000/ to test the index.html code, this will be served by the Node.js server as a static file.
I get a response like below from the GroupMe API:
{
"payload": {
"url": "https://i.groupme.com/157x168.png.940f20356cd048c98478da2b181ee971",
"picture_url": "https://i.groupme.com/157x168.png.940f20356cd048c98478da2b181ee971"
}
}
We'll serve locally on port 3000, so to start the server:
node index.js
If you are using Express, you will need something to process the FormData. I have used multer for something similar before. I had to save the files into local storage, then resend the file with axios.

Http GET Request from NodeJS to external API with http.get()

I am trying to do an HTTP GET request to an external API with NodeJS (using Express), but I am not getting any data back. My code is the nextone:
import * as http from "http";
const options = {
host: "EXAMPLE.COM",
path: "/MY/PATH",
headers: {
"Content-Type": "application/json",
"Authorization": "Basic XXXXXXXXXXXXXXXXXX"
}
};
const req = http.get(options, function(res) {
console.log("statusCode: " + res.statusCode);
res.on("data", function (chunk) {
console.log("BODY: " + chunk);
});
});
But the response I get is:
statusCode : 302 and BODY is empty.
The external API works properly (I have tried doing a http GET Request with INSOMNIA and returns data)
The request I am doing NEEDS an Authorization Token
What am I doing wrong? or what can I do to get the data back?
Cheers
You are just throwing data to console.log and not responding to request.
You did not mention if what http server you are using with node. In case you are using express.js (most common one) you should have something like:
const express = require("express");
const app = express();
const port = 3003;
const http = require("http");
// your webserver url localhost:3003/fetch-something
app.get("/fetch-something", (req, res) => {
const options = {
host: "EXAMPLE.COM",
path: "/MY/PATH",
headers: {
"Content-Type": "application/json",
Authorization: "Basic XXXXXXXXXXXXXXXXXX"
}
};
const httpReq = http.get(options, function(httpRes) {
//output status code to your console
console.log("statusCode: " + httpRes.statusCode);
httpRes.on("data", function(chunk) {
// still nothing happens on client - this will also just print to server console
console.log("data", chunk);
// return some data for requested route
return res.send(chunk);
});
});
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));

Categories