I need to get data from Spotify API then send the response to the front-end. To avoid CORS issue and to hide key and secret from Spotify, I would like to use Lambda to make the API call then send back the response. To be more precise about my application:
1. FrontEnd > API Gateway
2. API Gateway > Lambda
3. Lambda > Spotify API (request their API to get token)
4. Spotify API > Lambda (token in the response)
5. Lambda > API Gateway
6. API Gateway > FrontEnd
Spotify endpoint is:
https://accounts.spotify.com/api/token?grant_type=client_credentials
Header is:
Content-Type: 'application/x-www-form-urlencoded'
Authorization: 'Basic XXX'
So far I was able to do this using a Lambda function:
const https = require('https');
exports.handler = async (event, context) => {
return new Promise((resolve, reject) => {
const options = {
hostname: 'accounts.spotify.com',
path: '/api/token?grant_type=client_credentials',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic XXX'
}
}
const req = https.request(options, (res) => {
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
resolve('Success');
});
req.on('error', (e) => {
reject(e.message);
});
// send the request
req.write('');
req.end();
});
};
But I can't get the response from the API:
{
"access_token": "YYY",
"token_type": "Bearer",
"expires_in": 3600,
"scope": ""
}
And I don't know how to send the data back to the front-end. Do you have any guidance to achieve what I'm looking for?
Edit: I also tried using axios as suggested:
const axios = require("axios");
module.exports.handler = (event, context, callback) => {
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic XXX'
}
axios.post('https://accounts.spotify.com/api/token?grant_type=client_credentials', {}, {
headers: headers
})
.then(function(response) {
console.log(response)
callback(null, response);
})
.catch(function(err) {
console.error("Error: " + err);
callback(err);
});
};
But got the following error:
Response:
{
"errorType": "Error",
"errorMessage": "Request failed with status code 400",
"trace": [
"Error: Request failed with status code 400",
" at createError (/var/task/node_modules/axios/lib/core/createError.js:16:15)",
" at settle (/var/task/node_modules/axios/lib/core/settle.js:17:12)",
" at IncomingMessage.handleStreamEnd (/var/task/node_modules/axios/lib/adapters/http.js:237:11)",
" at IncomingMessage.emit (events.js:215:7)",
" at endReadableNT (_stream_readable.js:1183:12)",
" at processTicksAndRejections (internal/process/task_queues.js:80:21)"
]
}
Thanks to #jarmod and #Ashish Modi, the solution below is working for me:
const axios = require("axios");
const querystring = require('querystring');
module.exports.handler = (event, context, callback) => {
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic XXX'
}
axios.post('https://accounts.spotify.com/api/token?grant_type=client_credentials', querystring.stringify({}), {
headers: headers
})
.then(function(response) {
const res = {
statusCode: 200,
body: (response.data.access_token)
};
callback(null, res);
})
.catch(function(err) {
console.error("Error: " + err);
callback(err);
});
};
try this
const https = require('https');
function hitApi() {
return new Promise((resolve, reject) => {
const options = {
hostname: 'accounts.spotify.com',
path: '/api/token?grant_type=client_credentials',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic XXX'
}
}
https.request(options, (res) => {
res.setEncoding("utf8");
let body = "";
res.on('data', function (chunk) {
body += chunk;
});
res.on("error", err => {
reject(err);
});
res.on('end', function () {
resolve(body);
});
});
});
}
exports.handler = async (event, context) => {
const result = await hitApi();
return result;
};
hope this helps
Related
I have this post request to get access token and its working fine but would like to know how can I use this access token to call another post request ? Or how do I use async or promises to use in this ?
Here is my code :
function getAccessToken() {
const querystring = require('querystring');
const https = require('https')
const postData = querystring.stringify({
'grant_type': 'client_credentials'
});
const options = {
"hostname":'api.xxx.com',
"method": "POST",
"path" : "/token",
"port" : 443,
"encoding": "utf8",
"followRedirect": true,
"headers": {
"Authorization": 'Basic ' + Buffer.from("client_id" + ':' + "client_secret").toString('base64'),
"Content-Type": 'application/x-www-form-urlencoded',
"Content-Length": Buffer.byteLength(postData),
},
'muteHttpExceptions': true
}
const body = []
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`)
res.on('data', (chunk) => body.push(chunk))
res.on('end', () => {
const access_token = Buffer.concat(body).toString()
console.log(access_token)
})
})
req.on('error', error => {
console.error(error)
})
req.write(postData);
req.end()
}
getAccessToken();
You can save token in database,file or other memory database and use it in all requests in the Authorization header but depends on type of token,for example for set JWT token:
request.setHeader('Authorization', 'Bearer '+accessToken)
OR in option object:
options = {
host: '<URL>',
path: '<path of endpoint>',
port: '<portNumber>',
headers: {'Authorization', 'Bearer '+accessToken}
};
also for the async request to another endpoint, you can use Axios axios example:
const axios = require('axios').default;
const sendGetRequest = async () => {
try {
const resp = await
axios.get('https://jsonplaceholder.typicode.com/posts', {
headers: {
'authorization': 'Bearer YOUR_JWT_TOKEN_HERE'
}
});
console.log(resp.data);
} catch (err) {
// Handle Error Here
console.error(err);
}
};
sendGetRequest();
Here's the code of my AWS Lambda function:
console.log('Loading function');
const AWS = require('aws-sdk');
const https = require('https');
const data = JSON.stringify({
secretKey:"someSecretKey",
userType:"someCoolUser",
})
const options = {
hostname: "backend-staging.programmingpathshala.com:8080/rest/admin",
path: '/sendEmailToUsers',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}
exports.handler = function(event, context, callback) {
var dataString = '';
const req = https.request(options, function(res) {
res.on('data', chunk => {
dataString += chunk;
});
res.on('end', () => {
callback(null,dataString);
});
});
req.write(data)
req.end();
req.on('error', (e) => {
console.error(e);
});
}
When I test my API using postman it works fine. But when it is called from lambda function I get the following error:
Also, When I run the same API using ngrok and use its link in my lambda function it works then too.
Based on the comments, the options should be:
const options = {
hostname: "backend-staging.programmingpathshala.com",
port: 8080,
path: '/rest/admin/sendEmailToUsers',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}
I am hoping someone can help me point my mistake here.
I am trying to send a firebase push notification through a lambda using the HTTP legacy endpoint
https://fcm.googleapis.com/fcm/send
I am following the guide from:
https://craigrussell.io/2019/03/send-firebase-fcm-push-notification-from-aws-lambda/
Here is my code:
const authHeader ='key=A****IV';
const deviceToken ='eut4****pm';
console.log('sending Push notification');
return new Promise((resolve, reject) => {
const options = {
host: 'fcm.googleapis.com',
path: '/fcm/send',
method: 'POST',
headers: {
'Authorization': authHeader,
'Content-Type': 'application/json',
},
};
const req = http.request(options, (res) => {
console.log('success');
resolve('success');
});
req.on('error', (e) => {
console.log('failuree' + e.message);
reject(e.message);
});
// const reqBody = '{"to":"' + deviceToken + '", "priority" : "high"}';
const reqBody = '{"to":"' + deviceToken + '", "priority": "high", "notification": {"title": "Test", "body": "Test"}}';
console.log(reqBody);
req.write(reqBody);
req.end();
});
};
I do not receive any push notification after this . Am i doing something wrong here?
I think the key point is that you use the http module. Actually, it works well when using https module on my side.
var https = require('https');
The complete code is as follows:
var https = require('https');
exports.handler = async(event) => {
const authHeader = 'key=AA***y';
const deviceToken = 'fG***-';
return new Promise((resolve, reject) => {
const options = {
host: 'fcm.googleapis.com',
path: '/fcm/send',
method: 'POST',
headers: {
'Authorization': authHeader,
'Content-Type': 'application/json',
},
};
console.log(options);
const req = https.request(options, (res) => {
console.log('success');
console.log(res.statusCode);
resolve('success');
});
req.on('error', (e) => {
console.log('failuree' + e.message);
reject(e.message);
});
// const reqBody = '{"to":"' + deviceToken + '", "priority" : "high"}';
const reqBody = '{"to":"' + deviceToken + '", "priority": "high", "notification": {"title": "Test", "body": "Test"}}';
console.log(reqBody);
req.write(reqBody);
req.end();
});
};
For more details, you can see the res field after http request, it will show statusCode 403 when you use http.
I am creating node.js function through aws lambda which makes a GET request to Hybris Market Place and gets a CSRF Token. Then I am using that token to make another POST request to post some data to Hybris Market place but I am getting an error of 403 Forbidden. Same thing works in Postman which I believe due to POSTMAN keeps GET session alive and hence CSRF token is still valid. How May I achieve that in AWS Lambda function. Below is my code. I am using promise to make two requests.
const https = require('https');
exports.handler = async (event, context, callback) => {
const tokenOptions = {
"host": "*******.s4hana.ondemand.com",
"path": "/sap/opu/odata/sap/***********/",
"port": null,
"headers":{
"authorization": "Basic ************=",
"cache-control": "no-cache",
"x-csrf-token": "fetch"
},
"method": "GET"
};
var getToken = (tokenOptions) => {
return new Promise((resolve,reject)=>{
const req = https.request(tokenOptions, (res) => {
var xToken = res.headers["x-csrf-token"];
var sCookies = res.headers["set-cookie"];
var response = [xToken,sCookies]
res.on('data', () => {
console.log('Successfully processed HTTPS response');
resolve(response);
});
res.on('end', () => {
});
});
req.on('error', function(){
reject('Request to get token failed.');
});
req.end();
});
};
var postContent = (response) => {
return new Promise((resolve,reject)=>{
var options = {
"method": "POST",
"host": "*********-***.s4hana.ondemand.com",
"path": "/sap/opu/odata/sap/*********/*******",
"port":null,
"headers":
{ "authorization": "Basic *******==",
"x-csrf-token": response[0],
"accept": "application/json",
"content-type": "application/json",
"cache-control": "no-cache",
},
"cookie":response[1],
"body":
{
/* Data I want to POST */
},
"json": true
};
const req = https.request(options, (res,data) => {
console.log(res.statusCode);
res.on('data', () => {
resolve('Successfully submitted.');
});
res.on('end', () => {
});
});
req.on('error', function(err,res){
reject('Request to get Post failed.');
});
req.end();
});
};
getToken(tokenOptions).then((response) =>{
console.log('Result: ' +response[0]);
return postContent(response);
}).then((successMsg) =>{
callback(null,successMsg);
}).catch((errMsg)=>{
callback();
});
};
This curl request to the spotify API works perfectly fine
curl -X "POST" -H "Authorization: Basic <my-key-here>" -d grant_type=client_credentials https://accounts.spotify.com/api/token
I'm trying to do this in node, but it's not working and returns a 400 Bad Request. Here is my code. What am I doing wrong?
function AuthRequest(key) {
const req_body_params = JSON.stringify({
grant_type: "client_credentials"
})
const base64_enc = new Buffer(key).toString("base64")
const options = {
host: "accounts.spotify.com",
port: 443,
path: "api/token",
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Basic ${base64_enc}`
}
}
const req = https.request(options, function (res) {
res.on('data', function (data) {
alert("success: " + data)
})
})
req.on('error', function (err) {
alert("error: " + err)
})
req.write(req_body_params)
req.end()
}
I'm trying to use the Client Credentials method as explained here: https://developer.spotify.com/web-api/authorization-guide/
The request should be in application/x-www-form-urlencoded instead of JSON, it should be const req_body_params = "grant_type=client_credentials" with a header of "Content-Type": "application/x-www-form-urlencoded"
function AuthRequest(key) {
const req_body_params = "grant_type=client_credentials";
const base64_enc = new Buffer(key).toString("base64");
const options = {
host: "accounts.spotify.com",
port: 443,
path: "/api/token",
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": `Basic ${base64_enc}`
}
};
const req = https.request(options, function(res) {
res.on('data', function(data) {
alert("success: " + data)
})
});
req.on('error', function(err) {
alert("error: " + err)
});
req.write(req_body_params);
req.end();
}
Because token expires is good to request them dynamically, I created a method that output a token as a promise, then you can consume in your request.
const axios = require('axios')
const client_id = process.env.SPOTIFY_CLIENT_ID;
const client_secret = process.env.SPOTIFY_CLIENT_SECRET;
function getToken( ) {
return axios({
url: 'https://accounts.spotify.com/api/token',
method: 'post',
params: {
grant_type: 'client_credentials'
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
auth: {
username: client_id,
password: client_secret
}
})
}
async function getSpotifyData( endpoint ){
const tokenData = await getToken( );
const token = tokenData.data.access_token;
axios({
url: `https://api.spotify.com/v1/${endpoint}`,
method: 'get',
headers: {
'Authorization': 'Bearer ' + token
}
}).then( response => {
console.log( response.data );
return response.data;
}).catch( error => {
throw new Error(error);
});
}
getSpotifyData( 'browse/new-releases' );