Context:
I have a small Deno function deployed that takes a json body with 3 values. Those values are used in a fetch request to a API endpoint. Then return the data back to the requester.
Problem:
Testing with postman i get a 200 with the data. Testing with a tool like reqbin i get a 403.
What i think:
I think it has something to do with the fact that the reqbin request is redirected. But how does the end API know that if i "catch" the request in Deno and make a new fetch request to the end API?
My code:
import "https://deno.land/x/dotenv/load.ts";
import { listenAndServe } from "https://deno.land/std/http/mod.ts";
import { queryString } from "./queryString.js";
listenAndServe({ port: 8080 }, async (req) => {
//Check method
if (req.method != "POST") {
return new Response(JSON.stringify({ error: "Method not allowed" }), {
status: 405,
});
}
//Checking API key
const token = await req.headers.get("x-api-key");
if (token != Deno.env.get("KEY")) {
return new Response(JSON.stringify({ error: "Not authorized" }), {
headers: {
"content-type": "application/json",
},
status: 401,
});
}
const body = await req.json();
/*
{
id: "123",
currency: "USD",
country: "US"
}
*/
if (!typeof body.id == "string" || body.id == undefined) {
return new Response(
JSON.stringify({ error: "No ID or ID is in the wrong format" }),
{
headers: {
"content-type": "application/json",
},
status: 400,
}
);
}
const params = {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
query: queryString,
variables: {
id: body.id,
currency: body.currency,
country: body.country,
},
}),
};
const getDetails = await fetch("https://url.com", params);
const getDetailsStatus = getDetails.status;
if (getDetailsStatus == 200) {
const detailData = await getDetails.json();
return new Response(JSON.stringify(detailData), {
headers: {
"content-type": "application/json",
},
status: 200,
});
} else {
return new Response(JSON.stringify({ error: await getDetails.text() }), {
headers: {
"content-type": "application/json",
},
status: getDetailsStatus,
});
}
});
Headers:
const headers = {
postmanHeadersIncommingToDeno: {
accept: "*/*",
"accept-encoding": "gzip, deflate, br",
connection: "keep-alive",
"content-length": "104",
"content-type": "application/json",
host: "api-test.deno.dev",
"postman-token": "bfa856c6-82688ac6d",
referer: "http://api-test.deno.dev/",
"user-agent": "PostmanRuntime/7.29.0",
"x-api-key": "xxxxx",
"x-forwarded-for": "22.22.222.222"
},
APIresponseFromPostmanRequest: {
"access-control-allow-origin": "*",
"alt-svc": 'h3=":443"; ma=86400, h3-29=":443"; ma=86400',
"cache-control": "no-cache",
"cf-cache-status": "DYNAMIC",
"cf-ray": "6d7600655f39fa9c-AMS",
"content-type": "application/json; charset=utf-8",
date: "Wed, 02 Feb 2022 19:59:43 GMT",
etag: 'W/"556e-ki5bDB2SON5e6pQGbUxfMFEP0Jo"',
"expect-ct": 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"',
server: "cloudflare",
"strict-transport-security": "max-age=31536000; includeSubDomains; preload",
vary: "Accept-Encoding",
"x-amz-apigw-id": "M7hbZEtgoAMFjoA=",
"x-amzn-remapped-connection": "keep-alive",
"x-amzn-remapped-date": "Wed, 02 Feb 2022 19:59:43 GMT",
"x-amzn-remapped-server": "nginx/1.19.1",
"x-amzn-requestid": "7cc10db3-d179-4ed5-8428-3b1bb15279c7",
"x-amzn-trace-id": "Root=1-61fae2af-3a723f2479db4356007f1696",
"x-content-type-options": "nosniff",
"x-dns-prefetch-control": "off",
"x-download-options": "noopen",
"x-frame-options": "SAMEORIGIN",
"x-origin-region": "us-east-1",
"x-origin-server": "nginx",
"x-xss-protection": "1; mode=block"
},
reqbinIncommingToDeno: {
accept: "*/*",
"accept-encoding": "deflate, gzip",
"content-length": "104",
"content-type": "application/json",
host: "api-tester.deno.dev",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4...",
"x-api-key": "xxxxxxxxxx",
"x-forwarded-for": "111.111.111.111",
"x-real-ip": "22.22.222.222"
},
APIresponseFromReqbinRequest: {
"alt-svc": 'h3=":443"; ma=86400, h3-29=":443"; ma=86400',
"cf-ray": "6d760504cac382b6-IAD",
"content-type": "text/html",
date: "Wed, 02 Feb 2022 20:02:52 GMT",
"expect-ct": 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"',
server: "cloudflare",
"strict-transport-security": "max-age=31536000; includeSubDomains; preload",
vary: "Accept-Encoding",
"x-content-type-options": "nosniff"
}
}
Related
How do I turn this response into a downloadable file URL? I have a tried a few ways with blob but the file it creates is unreadable. Where am I going wrong?
My attempt:
let blob = new Blob([data.data], {type: "octet/stream"});
var url = window.URL.createObjectURL(blob);
window.location.assign(url);
enter code here
API Request to get the file data
export const getDocumentByFileName = async (fileName: string) => {
const client = await Client();
const response = await client.get(`${endpoint}/Documents/Get?fileName=${fileName}`, {
responseType: 'blob',
});
if (!response.ok) {
console.error('GET Document by fileName - ' + response.problem, response.data);
return response;
}
// 204 No Content
return response;
};
Response data containing file:
let data = {
"duration": 92,
"problem": null,
"originalError": null,
"ok": true,
"status": 200,
"headers": {
"date": "Thu, 04 Aug 2022 10:36:28 GMT",
"server": "Kestrel"
},
"config": {
"url": "Documents/Get?fileName=afbaa8fb-2669-4cf1-ad69-726a408934f6..docx",
"method": "get",
"headers": {
"Accept": "application/json",
"locale": "en-gb"
},
"params": {
"responseType": "arraybuffer"
},
"baseURL": "/",
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1,
"transitional": {
"silentJSONParsing": true,
"forcedJSONParsing": true,
"clarifyTimeoutError": false
}
},
"data": "PK\u0003\u0004\u0014\u0000\u0006\u0000\b\u0000\u0000\u0000!\u0000ߤ�lZ\u0001\u0000\u0000 \u0005\u0000\u0000\u0013\u0000\b\u0002[Content_Types].xml �\u0004\u0002(�\u0000\u0002\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\"
}
I'm using solana json rpc api to check a wallet's token balance from my javascript app. I have used the function for it like this
const getTokenBalance = async (walletAddress, tokenMintAddress) => {
const response = await axios({
url: `https://api.mainnet-beta.solana.com`,
method: "post",
headers: { "Content-Type": "application/json" },
data: {
jsonrpc: "2.0",
id: 1,
method: "getTokenAccountsByOwner",
params: [
walletAddress,
{
mint: tokenMintAddress,
},
{
encoding: "jsonParsed",
},
],
},
});
if (
Array.isArray(response?.data?.result?.value) &&
response?.data?.result?.value?.length > 0 &&
response?.data?.result?.value[0]?.account?.data?.parsed?.info?.tokenAmount
?.amount > 0
) {
return (
Number(
response?.data?.result?.value[0]?.account?.data?.parsed?.info
?.tokenAmount?.amount
) / 1000000000
);
} else {
return 0;
}
};
However I want to get all the token balance by one call instead of asking a token balance by giving a mint address for every token out there which makes my api respond like 10 minutes, is there any friendly way to do that?
I saw Covalent api can do it for checking ethereum wallet balance, wonder how they can do it
Most of the standard RPC accept batch requests, you should be able to send an array of all the requests you want, note that response will be an array as well.
// For example
const response = await axios({
url: `https://api.mainnet-beta.solana.com`,
method: "post",
headers: { "Content-Type": "application/json" },
data: [
{
jsonrpc: "2.0",
id: 1,
method: "getTokenAccountsByOwner",
params: [
walletAddress,
{
mint: tokenMintAddress,
},
{
encoding: "jsonParsed",
},
],
},
{
jsonrpc: "2.0",
id: 1,
method: "getTokenAccountsByOwner",
params: [
walletAddress2,
{
mint: tokenMintAddress2,
},
{
encoding: "jsonParsed",
},
],
},
]
});
As all tokens (that follow the standard) are "childs" of the Token Program you can get all with one RPC call :
curl https://api.mainnet-beta.solana.com -X POST -H "Content-Type:
application/json" -d ' {
"jsonrpc":"2.0",
"method":"getTokenAccountsByOwner",
"params": [
"walletAddress",
{
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
},
{
"encoding": "jsonParsed"
}
],
"id":4
}
If u need to get a balance of ur own token u can used the json rpc api with the follow post.
curl https://api.devnet.solana.com/ -X POST -H "Content-Type: application/json" -d '
{
"jsonrpc": "2.0",
"id": 1,
"method": "getTokenAccountsByOwner",
"params": [
"uja3w9XG1g6DQSVT6YASK99FVmdVwXoHVoQEgtEJdLv",
{
"mint": "7TMzmUe9NknkeS3Nxcx6esocgyj8WdKyEMny9myDGDYJ"
},
{
"encoding": "jsonParsed"
}
]
}
'
Im trying to use nodejs request to request data from an api called keywords everywhere, their documentation didnt have anything for nodejs, but they do have bash so what I did was I converted their bash to a nodejs request. for more context, here's their documentation :
https://api.keywordseverywhere.com/docs/#/keywords/get_keywords_data
My code seems to work because its returning a status code of 200 but the problem is that it returns a blank data:
{
"data": [],
"credits": 93735,
"time": 0
}
If the request is successful it should return something like this:
{
"data": [
{
"vol": 390,
"cpc": {
"currency": "$",
"value": "5.51"
},
"keyword": "keywords tool",
"competition": 0.33,
"trend": [
{
"month": "May",
"year": 2019,
"value": 480
}]
},
"credits": 95597600,
"time": 0.02
}
Im guessing there is something wrong with my code since I used a converter and it's not reading the body request properly. Here's my code:
var headers = {
'Authorization': 'Bearer (API-KEY-HERE)',
'Accept': 'application/x-www-form-urlencoded'
};
var options = {
url: 'https://api.keywordseverywhere.com/v1/get_keyword_data',
method: 'POST',
headers: headers,
body: 'dataSource=gkp&country=us¤cy=USD&kw[]=keywords&kw[]=keyword'
};
function callback(error, response, body) {
console.log(response.body)
if (!error && response.statusCode == 200) {
console.log(body);
}
}
request.post(options, callback);
the problem with your code is the body, anyway this is how the code should look like:
const request = require('request');
var options = {
'method': 'POST',
'url': 'https://api.keywordseverywhere.com/v1/get_keyword_data',
'headers': {
'Authorization': 'Bearer insertAPIKeyHere'
},
formData: {
'kw[]': 'keyword',
'country': 'us',
'currency': 'usd',
'dataSource': 'gkp'
}
};
request(options, function (error, response) {
if (error){console.log(error)};
console.log(response.body);
});
I'm trying to send an array of objects to an API endpoint using Zapier and can't seem to format my data correctly. Below is what the endpoint is expecting
[{
"date": "2014-02-13",
"do": "DO140213001",
"address": "63 Ubi Avenue 1 Singapore 408937"
},
{
"date": "2014-02-13",
"do": "DO140213002",
"address": "59 Ubi Avenue 1 Singapore 408938",
"delivery_time": "02:00 PM - 05:30 PM"
}
]
This is waht i have in the Zapier UI web builder
const options = {
url: 'https://app.detrack.com/api/v1/deliveries/update.json',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-KEY': bundle.authData.api_key
},
params: {
},
body: [{
}]
};
return z.request(options)
.then((response) => {
response.throwForStatus();
const results = z.JSON.parse(response.content);
// You can do any parsing you need for results here before returning them
return results;
});
I'm trying to use the Google Cloud Printing API. I previously had problems relating to the sending of my request. After some json/stringifying experimentation, I no longer get that error. Instead, my API calls are unsuccessful according to the response sent back by the Google API. Here's what I'm doing:
// Ticket used for google cloud printing
const ticket = {
"version":"1.0",
"print":{
"color":{"vendor_id":"psk:Color","type":0},
"duplex":{"type":0},
"page_orientation":{"type":0},
"copies":{"copies":1},
"dpi":{"horizontal_dpi":1200,"vertical_dpi":1200},
"media_size":{"width_microns":80000,"height_microns":58000,"is_continuous_feed":false},
"collate":{"collate":true},
"vendor_ticket_item":[
//Printer specific settings here, from the capabilities:
{"id":"psk:JobInputBin","value":"ns0000:Tray3"},
{"id":"psk:PageICMRenderingIntent","value":"psk:Photographs"},
{"id":"psk:PageMediaType","value":"ns0000:Auto"},
{"id":"psk:JobOutputBin","value":"ns0000:Auto"},
//etc.
]
}
}
request({
"method": "POST",
"content-type" : "application/json",
"url": googlePrintUrl + "submit",
"headers": {
"Authorization": "OAuth " + googleAccessToken
},
"body" : {
"printerid": "39875g133-ae7d-76hg-65af-jhe5bc682404",
"ticket": JSON.stringify(ticket),
"title": "TEST PRINT",
"content": "test msg",
"contentType": "text/plain"
},
"json": true
}, function (error, res, body){
if (error) {
console.log("There was an error with Google Cloud Print");
console.log(error);
return;
}
console.log("The server responded with:", body);
});
this request results in this response from the server:
The server responded with: { success: false,
request:
{ time: '0',
params: {},
user: 'mytest#gmail.com',
users: [ 'mytest#gmail.com' ] },
errorCode: 3,
message: 'Printer Id required for this request.' }
As you can see, the params field is empty. This is strange because when I use Postman to do the same request, this field is filled with the params I sent in the API call. Here's how I successfully did it in Postman:
Which generated the server response:
{
"success": true,
"request": {
"time": "0",
"params": {
"ticket": [
"{\"version\":\"1.0\",\"print\":{\"color\":{\"vendor_id\":\"psk:Color\",\"type\":0},\"duplex\":{\"type\":0},\"page_orientation\":{\"type\":0},\"copies\":{\"copies\":1},\"dpi\":{\"horizontal_dpi\":600,\"vertical_dpi\":600},\"media_size\":{\"width_microns\":80000,\"height_microns\":58000,\"is_continuous_feed\":false},\"collate\":{\"collate\":true},\"vendor_ticket_item\":[{\"id\":\"psk:JobInputBin\",\"value\":\"ns0000:Tray3\"},{\"id\":\"psk:PageICMRenderingIntent\",\"value\":\"psk:Photographs\"},{\"id\":\"psk:PageMediaType\",\"value\":\"ns0000:Auto\"},{\"id\":\"psk:JobOutputBin\",\"value\":\"ns0000:Auto\"}]}}"
],
"printerid": [
"39875g133-ae7d-76hg-65af-jhe5bc682404"
],
"title": [
"TEST"
],
"contentType": [
"text/plain"
],
"content": [
"**** test"
]
},
"user": "mytest#gmail.com",
"users": [
"mytest#gmail.com"
]
},
"xsrf_token": "AIp06DhAZRSLW9GlHWQLKykbpU-5fYRqcA:1531484990909",
"message": "Print job added.",
"job": {
"ticketUrl": "https://www.google.com/cloudprint/ticket?jobid\u003df11043fe-3e00-d912-11dd-c859718a5575",
"printerName": "",
"errorCode": "",
"updateTime": "1531484993830",
"title": "**** TEST",
"message": "",
"ownerId": "mytest#gmail.com",
"tags": [
"^own"
],
"uiState": {
"summary": "QUEUED",
"progress": "Delivery attempts: 1"
},
"numberOfPages": 1,
"createTime": "1531484991068",
"semanticState": {
"delivery_attempts": 1,
"state": {
"type": "QUEUED"
},
"version": "1.0"
},
"printerid": "39875g133-ae7d-76hg-65af-jhe5bc682404",
"fileUrl": "https://www.google.com/cloudprint/download?id\u003df11043fe-3e00-d912-11dd-c859718a5575",
"id": "f11043fe-3e00-d912-11dd-c859718a5575",
"rasterUrl": "https://www.google.com/cloudprint/download?id\u003df11043fe-3e00-d912-11dd-c859718a5575\u0026forcepwg\u003d1",
"contentType": "application/pdf",
"status": "QUEUED"
}
}
This is a successful printing job, and all the parameters sent by me are sent back in the response object.
So where am I going wrong in my node.js code?
Postman sends payload (printerid, content, title, etc) in formData rather than body
Underneath the "SEND" button is a button "Code" that can generate functional NodeJS (and other) snippets like
var request = require("request");
var options = { method: 'POST',
url: 'https://www.google.com/cloudprint/submit',
headers:
{ 'cache-control': 'no-cache',
Connection: 'keep-alive',
'Content-Length': '769',
'Accept-Encoding': 'gzip, deflate',
Host: 'www.google.com',
'Cache-Control': 'no-cache',
'Content-Type': 'application/json',
Authorization: 'Bearer ya29.GlxWB5_vr8QmJw3DChvVyqpRhNJ2hsuVzwNTJoYRH6r2VVGTwDE3MLNAN8pjTB3-BDWtZeIDrCDcP5DwYGywM1vgb9VMPhoi806HrMpOpKAaKzrgiliojec6IB2Cwg',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' },
formData:
{ printerid: 'd936d368-7ea4-6f66-84fd-6d5a7a357318',
title: 'Document Title',
content: 'Hello World',
ticket: '{"version":"1.0","print":{"vendor_ticket_item":[],"color":{"type":"STANDARD_MONOCHROME"},"copies":{"copies":1}}}',
contentType: 'text/plain' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});