I'm trying to send a post request with header in koa.js routes like this:
Here is request function
const request = require('request').defaults({
json: true
});
function *requestPromise(url, method, header, body) {
return new Promise(function (resolve, reject) {
delete header["content-length"];
let newHeader = {
"user-agent": header["user-agent"],
"host": header["host"],
"connection": 'keep-alive'
};
console.log(newHeader)
request({
method: method,
url: url,
body: body,
headers: newHeader
}, function(error, httpResponse, body) {
if (error) {
console.error(url + " : " + error);
} else if (httpResponse.statusCode !== 204) {
reject(body.message);
} else {
resolve(body);
}
});
});
}
Here is route:
router.post('/v3_6/autoevents', function *() {
// save to db
yield EventAuto.save(this.request.body);
let akkaEndConfig = {
url: "http://127.0.0.1:8080/v3_6/autoevents",
method: 'POST',
header: this.header,
body: this.request.body
};
// request to another server
yield requestPromise(akkaEndConfig.url, akkaEndConfig.method, akkaEndConfig.header, akkaEndConfig.body);
this.status = 204;
});
But when I wanna run this server,got this error:
xxx POST /api/v3_6/autoevents 500 195ms -
TypeError: Cannot read property 'name' of undefined
at Object.callee$1$0$ (/koa/src/lib/error-trace.js:10:11)
at tryCatch(/koa/node_modules/regenerator/runtime.js:61:40)
at GeneratorFunctionPrototype.invoke [as _invoke](/node_modules/regenerator/runtime.js:328:22)
at GeneratorFunctionPrototype.prototype.(anonymous function) [as throw] (/node_modules/regenerator/runtime.js:94:21)
at onRejected (/node_modules/co/index.js:81:24)
at run (/node_modules/core-js/modules/es6.promise.js:104:47)
at /node_modules/core-js/modules/es6.promise.js:115:28
at flush (/node_modules/core-js/modules/$.microtask.js:19:5)
at doNTCallback0 (node.js:428:9)
at process._tickDomainCallback (node.js:398:13)
I just wanna requset form serverA's route to serverB. Is this method wrong?
If you want to send a request from serverA to serverB, I created a sample app on what you want to achieve.
'use strict';
var koa = require('koa');
var route = require('koa-route');
var request = require('request-promise');
var rawBody = require('raw-body');
var appA = koa();
var appB = koa();
appA.use(route.get('/v3_6/autoevents', function *(){
let response = yield request({
method: 'POST',
url: 'http://127.0.0.1:8081/v3_6/autoevents',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ param1: 'Hello World from A' })
});
this.body = response;
}));
appB.use(route.post('/v3_6/autoevents', function *(){
this.req.body = yield rawBody(this.req,{limit:'10kb', encoding:'utf8'});
this.req.body = JSON.parse(this.req.body);
this.body = 'Hello from World B; ' + this.req.body.param1;
}));
appA.listen(8080);
appB.listen(8081);
Server A( appA ) has an endpoint named /v3_6/autoevents using GET method, accessing it will send a POST request to Server B's( appB ) /v3_6/autoevents endpoint that in return will send a truncated value with A's request body and Hello World from B;.
The final output after you execute it on the browser using http://127.0.0.1:8080/v3_6/autoevents will be Hello World from B; Hello World from A
Related
I'm using the sandbox API at the moment, and I can query the products, including individually, but if I try and place a buy order, the response I get is { message: 'Product not found' }.
Here's my code:
async function cb_request( method, path, headers = {}, body = ''){
var apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
apiSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
apiPass = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx';
//get unix time in seconds
var timestamp = Math.floor(Date.now() / 1000);
// set the request message
var message = timestamp + method + path + body;
//create a hexedecimal encoded SHA256 signature of the message
var key = Buffer.from(apiSecret, 'base64');
var signature = crypto.createHmac('sha256', key).update(message).digest('base64');
//create the request options object
var baseUrl = 'https://api-public.sandbox.pro.coinbase.com';
headers = Object.assign({},headers,{
'CB-ACCESS-SIGN': signature,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': apiKey,
'CB-ACCESS-PASSPHRASE': apiPass,
'USER-AGENT': 'request'
});
// Logging the headers here to ensure they're sent properly
console.log(headers);
var options = {
baseUrl: baseUrl,
url: path,
method: method,
headers: headers
};
return new Promise((resolve,reject)=>{
request( options, function(err, response, body){
if (err) reject(err);
resolve(JSON.parse(response.body));
});
});
}
async function main() {
// This queries a product by id (successfully)
try {
console.log( await cb_request('GET','/products/BTC-USD') );
}
catch(e) {
console.log(e);
}
// Trying to place a buy order here (using the same id as above) returns { message: 'Product not found' }
var buyParams = {
'type': 'market',
'side': 'buy',
'funds': '100',
'product_id': 'BTC-USD'
};
try {
var buy = await cb_request('POST','/orders',buyParams);
console.log(buy);
}
catch(e) {
console.log(e);
}
}
main();
I've tried sending the params in the body, which responds with invalid signature, even when stringified. I've also tried using the params shown in the API docs, but that responds with product not found too.
Any ideas? TIA
As j-petty mentioned you need to send data as request body for POST operation as described in the API documentation so this is why you get "product not found".
Here is working code based on what your shared:
var crypto = require('crypto');
var request = require('request');
async function cb_request( method, path, headers = {}, body = ''){
var apiKey = 'xxxxxx',
apiSecret = 'xxxxxxx',
apiPass = 'xxxxxxx';
//get unix time in seconds
var timestamp = Math.floor(Date.now() / 1000);
// set the request message
var message = timestamp + method + path + body;
console.log('######## message=' + message);
//create a hexedecimal encoded SHA256 signature of the message
var key = Buffer.from(apiSecret, 'base64');
var signature = crypto.createHmac('sha256', key).update(message).digest('base64');
//create the request options object
var baseUrl = 'https://api-public.sandbox.pro.coinbase.com';
headers = Object.assign({},headers,{
'content-type': 'application/json; charset=UTF-8',
'CB-ACCESS-SIGN': signature,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': apiKey,
'CB-ACCESS-PASSPHRASE': apiPass,
'USER-AGENT': 'request'
});
// Logging the headers here to ensure they're sent properly
console.log(headers);
var options = {
'baseUrl': baseUrl,
'url': path,
'method': method,
'headers': headers,
'body': body
};
return new Promise((resolve,reject)=>{
request( options, function(err, response, body){
console.log(response.statusCode + " " + response.statusMessage);
if (err) reject(err);
resolve(JSON.parse(response.body));
});
});
}
async function main() {
// This queries a product by id (successfully)
try {
console.log('try to call product------->');
console.log( await cb_request('GET','/products/BTC-USD') );
console.log('product------------------->done');
}
catch(e) {
console.log(e);
}
var buyParams = JSON.stringify({
'type': 'market',
'side': 'buy',
'funds': '10',
'product_id': 'BTC-USD'
});
try {
console.log('try to call orders------->');
var buy = await cb_request('POST','/orders', {}, buyParams);
console.log(buy);
console.log('orders----------------------->done');
}
catch(e) {
console.log(e);
}
}
main();
You need to send a POST request to the /orders endpoint and include the body in the request payload.
There are some example answers in this question.
var options = {
baseUrl: baseUrl,
url: path,
method: method,
headers: headers
json: true,
body: body
}
request.post(options, function(err, response, body){
if (err) reject(err);
resolve(JSON.parse(response.body));
});
It's worth mentioning that the sandbox API has different results than the production API. Consider the following CURLs.
Sandbox API:
❯ curl --request GET \
--url https://api-public.sandbox.exchange.coinbase.com/products/ETH-USD \
--header 'Accept: application/json'
{"message":"NotFound"}%
Production API:
❯ curl --request GET \
--url https://api.exchange.coinbase.com/products/ETH-USD \
--header 'Accept: application/json'
{"id":"ETH-USD","base_currency":"ETH","quote_currency":"USD","base_min_size":"0.00029","base_max_size":"2800","quote_increment":"0.01","base_increment":"0.00000001","display_name":"ETH/USD","min_market_funds":"1","max_market_funds":"4000000","margin_enabled":false,"fx_stablecoin":false,"max_slippage_percentage":"0.02000000","post_only":false,"limit_only":false,"cancel_only":false,"trading_disabled":false,"status":"online","status_message":"","auction_mode":false}%
You'll notice that the paths are identical but you get different results so keep that in mind. For testing purposes BTC-USD can be used.
I'm using the sandbox API at the moment, and I can query the products, including individually, but if I try and place a buy order, the response I get is { message: 'Product not found' }.
Here's my code:
async function cb_request( method, path, headers = {}, body = ''){
var apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
apiSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
apiPass = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx';
//get unix time in seconds
var timestamp = Math.floor(Date.now() / 1000);
// set the request message
var message = timestamp + method + path + body;
//create a hexedecimal encoded SHA256 signature of the message
var key = Buffer.from(apiSecret, 'base64');
var signature = crypto.createHmac('sha256', key).update(message).digest('base64');
//create the request options object
var baseUrl = 'https://api-public.sandbox.pro.coinbase.com';
headers = Object.assign({},headers,{
'CB-ACCESS-SIGN': signature,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': apiKey,
'CB-ACCESS-PASSPHRASE': apiPass,
'USER-AGENT': 'request'
});
// Logging the headers here to ensure they're sent properly
console.log(headers);
var options = {
baseUrl: baseUrl,
url: path,
method: method,
headers: headers
};
return new Promise((resolve,reject)=>{
request( options, function(err, response, body){
if (err) reject(err);
resolve(JSON.parse(response.body));
});
});
}
async function main() {
// This queries a product by id (successfully)
try {
console.log( await cb_request('GET','/products/BTC-USD') );
}
catch(e) {
console.log(e);
}
// Trying to place a buy order here (using the same id as above) returns { message: 'Product not found' }
var buyParams = {
'type': 'market',
'side': 'buy',
'funds': '100',
'product_id': 'BTC-USD'
};
try {
var buy = await cb_request('POST','/orders',buyParams);
console.log(buy);
}
catch(e) {
console.log(e);
}
}
main();
I've tried sending the params in the body, which responds with invalid signature, even when stringified. I've also tried using the params shown in the API docs, but that responds with product not found too.
Any ideas? TIA
As j-petty mentioned you need to send data as request body for POST operation as described in the API documentation so this is why you get "product not found".
Here is working code based on what your shared:
var crypto = require('crypto');
var request = require('request');
async function cb_request( method, path, headers = {}, body = ''){
var apiKey = 'xxxxxx',
apiSecret = 'xxxxxxx',
apiPass = 'xxxxxxx';
//get unix time in seconds
var timestamp = Math.floor(Date.now() / 1000);
// set the request message
var message = timestamp + method + path + body;
console.log('######## message=' + message);
//create a hexedecimal encoded SHA256 signature of the message
var key = Buffer.from(apiSecret, 'base64');
var signature = crypto.createHmac('sha256', key).update(message).digest('base64');
//create the request options object
var baseUrl = 'https://api-public.sandbox.pro.coinbase.com';
headers = Object.assign({},headers,{
'content-type': 'application/json; charset=UTF-8',
'CB-ACCESS-SIGN': signature,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': apiKey,
'CB-ACCESS-PASSPHRASE': apiPass,
'USER-AGENT': 'request'
});
// Logging the headers here to ensure they're sent properly
console.log(headers);
var options = {
'baseUrl': baseUrl,
'url': path,
'method': method,
'headers': headers,
'body': body
};
return new Promise((resolve,reject)=>{
request( options, function(err, response, body){
console.log(response.statusCode + " " + response.statusMessage);
if (err) reject(err);
resolve(JSON.parse(response.body));
});
});
}
async function main() {
// This queries a product by id (successfully)
try {
console.log('try to call product------->');
console.log( await cb_request('GET','/products/BTC-USD') );
console.log('product------------------->done');
}
catch(e) {
console.log(e);
}
var buyParams = JSON.stringify({
'type': 'market',
'side': 'buy',
'funds': '10',
'product_id': 'BTC-USD'
});
try {
console.log('try to call orders------->');
var buy = await cb_request('POST','/orders', {}, buyParams);
console.log(buy);
console.log('orders----------------------->done');
}
catch(e) {
console.log(e);
}
}
main();
You need to send a POST request to the /orders endpoint and include the body in the request payload.
There are some example answers in this question.
var options = {
baseUrl: baseUrl,
url: path,
method: method,
headers: headers
json: true,
body: body
}
request.post(options, function(err, response, body){
if (err) reject(err);
resolve(JSON.parse(response.body));
});
It's worth mentioning that the sandbox API has different results than the production API. Consider the following CURLs.
Sandbox API:
❯ curl --request GET \
--url https://api-public.sandbox.exchange.coinbase.com/products/ETH-USD \
--header 'Accept: application/json'
{"message":"NotFound"}%
Production API:
❯ curl --request GET \
--url https://api.exchange.coinbase.com/products/ETH-USD \
--header 'Accept: application/json'
{"id":"ETH-USD","base_currency":"ETH","quote_currency":"USD","base_min_size":"0.00029","base_max_size":"2800","quote_increment":"0.01","base_increment":"0.00000001","display_name":"ETH/USD","min_market_funds":"1","max_market_funds":"4000000","margin_enabled":false,"fx_stablecoin":false,"max_slippage_percentage":"0.02000000","post_only":false,"limit_only":false,"cancel_only":false,"trading_disabled":false,"status":"online","status_message":"","auction_mode":false}%
You'll notice that the paths are identical but you get different results so keep that in mind. For testing purposes BTC-USD can be used.
Getting a dreaded JSON error .
I am using an external API that allegedly takes a POST to add a user to a group.
in my nodeJS express app - I want to pass-thru the data coming from my app to the external API.
my "GET" methods work - but now I am trying to take a form submitted to my app, and pass the data to an external API using "POST".
Here is the code I am testing (assume the api url and credentials are correct - and NOT the issue)
I have tested the external API passing the same JSON object directly to the external API using Postman, and it works without error.
const express = require('express');
const router = express.Router();
const https = require('https');
function callExternalAPI( RequestOptions ) {
return new Promise((resolve, reject) => {
https.request(
RequestOptions,
function(response) {
const { statusCode } = response;
if (statusCode >= 300) {
reject(
new Error( response.statusMessage )
);
}
const chunks = [];
response.on('data', (chunk) => {
chunks.push(chunk);
});
response.on('end', () => {
const result = Buffer.concat(chunks).toString();
resolve( JSON.parse(result) );
});
}
)
.end();
})
}
router.get('/thing', /*auth,*/ ( req, res, next ) => {
callExternalAPI(
{
host: 'api_url',
path: '/list',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + new Buffer( auth_un + ':' + auth_pw ).toString('base64')
}
}
)
.then(
response => {
console.log(response);
}
)
.catch(
error => {
console.log(error);
}
);
});
router.post('/thing', /*auth,*/ ( req, res, next ) => {
callExternalAPI(
{
host: 'api_url',
path: '/addThing',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + new Buffer( auth_un + ':' + auth_pw ).toString('base64')
},
data: {
'group_id': req.body.group_id,
'person_id': req.body.person_id
}
}
)
.then(
response => {
console.log(response);
}
)
.catch(
error => {
console.log(error);
}
);
});
module.exports = router;
console logging the req.body looks lie this
{ group_id: '45b62b61-62fa-4684-a058-db3ef284f699', person_id: '3b1c915c-3906-42cf-8084-f9a25179d6b2' }
And the error looks like this
undefined:1
<html><title>JSpring Application Exception</title>
<h2>JSpring Exception Stack Trace</h2>
<pre>SafeException: FiberServer.parsers.parseJSONBuf(): JSON parse failed.
^
SyntaxError: Unexpected token < in JSON at position 0
Granted the console.log of the req.body does not have the required double quotes, but I think that is just the log dump format - but it might be munging the JSON. I have tried wrapping this in stringify; meaning something like this: data: JSON.stringify( req.body ) (but the same error occurs).
callExternalAPI(
{
host: 'api_url',
path: '/addThing',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + new Buffer( auth_un + ':' + auth_pw ).toString('base64')
},
**data: JSON.stringify( req.body )**
}
)
I am testing this in postman, having the body be 'raw json' with headers as 'application/json'
the body is this:
{
"group_id": "45b62b61-62fa-4684-a058-db3ef284f699",
"person_id": "3b1c915c-3906-42cf-8084-f9a25179d6b2"
}
You should try to write the POST payload in the request body instead of passing it inside the options object:
function callExternalAPI( RequestOptions ) {
const { data, ...options } = RequestOptions;
return new Promise((resolve, reject) => {
const req = https.request(
options,
function(response) {
const { statusCode } = response;
if (statusCode >= 300) {
reject(
new Error( response.statusMessage )
);
}
const chunks = [];
response.on('data', (chunk) => {
chunks.push(chunk);
});
response.on('end', () => {
const result = Buffer.concat(chunks).toString();
resolve( JSON.parse(result) );
});
}
);
req.write(JSON.stringify(data));
req.end();
})
}
In express you must use bodyParser
At the top of the file when you initializing your express app add this lines
const app = express()
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
https://medium.com/#adamzerner/how-bodyparser-works-247897a93b90
my server can't find the api's that i created in api directory. it leads to 500 internal server.
I have checked routes.js but i see that everything is right. i have an error.js file for file handling. Here's my code.
'use strict';
let router = require('express').Router();
// Middleware
let middleware = require('./controllers/middleware');
router.use(middleware.doSomethingInteresting);
// Tasks
let tasks = require('./controllers/tasks');
let createkeypairs = require('./controllers/createkeypairs');
let importaddress = require('./controllers/importaddress');
let getwalletinfo = require('./controllers/getwalletinfo');
router.get('/tasks', tasks.findAll2);
router.get('/createkeypairs', createkeypairs.findAll);
router.get('/importaddress', importaddress.findAll);
router.get('/getwalletinfo', getwalletinfo.findAll);
router.post('/buggyroute', tasks.buggyRoute);
// Error Handling
let errors = require('./controllers/errors');
router.use(errors.errorHandler);
// Request was not picked up by a route, send 404
router.use(errors.nullRoute);
// Export the router
module.exports = router;
now showing you my createkeypairs.js
'use strict';
let errors = require('./errors.js');
var request = require("request");
var options = { method: 'POST',
url: '127.0.0.1:18332',
headers:
{ 'Authorization': 'Basic bXVsdGljaGFpbnJwYzpHTmJ5enJhMnlHRjN4Ymp1cnluRTFucTlnV1ExRXV3OTFpYVBqSkt5TkJxdA==',
'cache-control': 'no-cache',
'Cache-Control': 'no-cache',
'Content-Type': 'application/json' },
body: { method: 'createkeypairs', params: [], chain_name: 'tokenchain' },
json: true };
exports.findAll = (req, res, next) => {
// Simulate task list, normally this would be retrieved from a database
let createkeypairs ;
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log("working here ");
// res.json(body);
});
};
exports.buggyRoute = (req, res, next) => {
// Simulate a custom error
next(errors.newHttpError(400, 'bad request'));
};
I think the problem is in createkeypair file.
Try this code once for your createkeypairs.js:
'use strict';
let errors = require('./errors.js');
var request = require("request");
let config = require('config');
var auth = 'Basic ' + Buffer.from(config.user + ':' + config.pass).toString('base64');
var url = config.url;
var chain = config.chain;
var options = { method: 'POST',
url: url,
headers:
{ 'cache-control': 'no-cache',
Authorization : auth,
'Content-Type': 'application/json' },
body: { method: 'importaddress', params: ["address"], chain_name: chain },
json: true };
exports.findAll = (req, res, next) => {
// Simulate task list, normally this would be retrieved from a database
let createkeypairs ;
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
res.json(body);
});
};
exports.buggyRoute = (req, res, next) => {
// Simulate a custom error
next(errors.newHttpError(400, 'bad request'));
};
Do tell me if it works or not.
I want to make a REST call and get the result out in a variable (access_token). My variable AFAIK is global. Why is my variable access_token undefined at the end, even though i get a result in the console ? I understand that this call is async, but I put a 'false' in the call.
var https = require('https'),
querystring = require('querystring');
require('request-to-curl');
// First, get access token
var postData = querystring.stringify({
'grant_type': 'password',
'username':'<myusername>',
'password':'<mypassword>'
});
var options = {
hostname: 'myserver.com',
port: 443,
path: '/v1/oauth2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
function postVal(options, value) {
//var responseObject;
var access_token;
var responseObject;
var req = https.request(options, function(res)
{
res.setEncoding('utf-8');
var responseString = '';
res.on('data', function(data) {
responseString += data;
});
res.on('end', function() {
console.log(responseString);
responseObject = (JSON.parse(responseString));
access_token = responseObject.access_token;
console.log('console access:' + access_token);
value(access_token);
// return responseObject;
});
});
req.on('error', (e) => {
console.log(`problem with request: ${e.message}`);
});
// write data to request body
req.write(postData);
req.end();
}
console.log("Access:"+ postVal(options, function(access) {
console.log(access);
}));
Result:
$ node curl.js
Access:undefined
{"token_type":"Bearer","access_token":"f3DzDqW..dbMo0","refresh_token":"4jXqo4..kuuc2rMux3","scope":"global","access_token_type":"USER_GENERATED","note":"","expires_in":900}
console access:f3DzDqWpnrgxnxt5vE42ih8ew..gOKyJY5dbMo0
mlieber-ltm12: