I am trying to use Marea Tide Api to build a tide application returning results based on location (latitude, longitude).
I was originally struggling to make a successful fetch from the front end due to a CORS error. I am now trying to build an express backend to both hide my api keys and navigate the CORS issue with a proxy.
I keep getting an unauthorized error when trying to navigate to localhost:8000/tides.
Express:
const PORT = 8000;
const express = require('express');
const cors = require('cors');
const axios = require('axios');
const { application, json } = require('express');
require('dotenv').config
const backend = express()
backend.listen(PORT, () => console.log(`Tide backend is working on PORT:${PORT}`))
backend.get('/tides', (req, res, next) => {
const url = 'https://api.marea.ooo/v2/tides';
const options = {
params: {
duration: 1440,
interval: 60,
latitude: 36.888,
longitude: -76.100,
}
};
const config = {
headers: {
'x-marea-api-token': process.env.MareaToken,
}
};
axios.get(url, options, config)
.then(response => {console.log(response)})
.catch(err => console.log(err))
});
Terminal Error:
[nodemon] starting `node backend.js`
Tide backend is working on PORT:8000
[AxiosError: Request failed with status code 401] {
code: 'ERR_BAD_REQUEST',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [Function: httpAdapter],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: [Function] },
validateStatus: [Function: validateStatus],
headers: {
Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/0.27.2'
},
params: {
duration: 1440,
interval: 60,
latitude: 36.888,
longitude: -76.1
},
method: 'get',
url: 'https://api.marea.ooo/v2/tides',
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
secureConnecting: false,
_SNICallback: null,
servername: 'api.marea.ooo',
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object: null prototype],
_eventsCount: 10,
connecting: false,
_hadError: false,
_parent: null,
_host: 'api.marea.ooo',
_readableState: [ReadableState],
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular *1],
[Symbol(res)]: [TLSWrap],
[Symbol(verified)]: true,
[Symbol(pendingSession)]: null,
[Symbol(async_id_symbol)]: 17,
[Symbol(kHandle)]: [TLSWrap],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 60,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object],
[Symbol(RequestTimeout)]: undefined
},
_header: 'GET /v2/tides?duration=1440&interval=60&latitude=36.888&longitude=-76.1 HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Host: api.marea.ooo\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype],
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/v2/tides?duration=1440&interval=60&latitude=36.888&longitude=-76.1',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 4,
_maxListeners: undefined,
socket: [TLSSocket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [Array],
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 401,
statusMessage: 'Unauthorized',
client: [TLSSocket],
_consuming: false,
_dumped: false,
req: [Circular *1],
responseUrl: 'https://api.marea.ooo/v2/tides?duration=1440&interval=60&latitude=36.888&longitude=-76.1',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 16,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'api.marea.ooo',
protocol: 'https:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: 'https://api.marea.ooo/v2/tides?duration=1440&interval=60&latitude=36.888&longitude=-76.1',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'user-agent': [Array],
host: [Array]
}
},
response: {
status: 401,
statusText: 'Unauthorized',
headers: {
'cache-control': 'max-age=0, private, must-revalidate',
'content-length': '40',
'content-type': 'application/json; charset=utf-8',
date: 'Tue, 19 Jul 2022 18:46:46 GMT',
server: 'Caddy',
'x-request-id': 'FwNPaorYW0wySfUApCrB',
connection: 'close'
},
config: {
transitional: [Object],
adapter: [Function: httpAdapter],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: [Object],
validateStatus: [Function: validateStatus],
headers: [Object],
params: [Object],
method: 'get',
url: 'https://api.marea.ooo/v2/tides',
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: [TLSSocket],
_header: 'GET /v2/tides?duration=1440&interval=60&latitude=36.888&longitude=-76.1 HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Host: api.marea.ooo\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/v2/tides?duration=1440&interval=60&latitude=36.888&longitude=-76.1',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'api.marea.ooo',
protocol: 'https:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: { status: 401, error: 'Unauthorized' }
}
}
axios.get() accepts only two parameters...
axios.get(url[, config])
Combine your options and config objects and don't forget to send an actual response.
axios.get(url, { ...options, ...config })
.then(({ data }) => res.json(data))
.catch(next);
Related
I am trying to get the youtube transcription with youtube API on the Express.js server. From what I understand, I need to use the client id for this, so I am trying to authenticate to google API. For this purpose I use quickstart.js from google docs and Express listener on redirect_uri to handle authentication:
interface GoogleOAuthTokenResponse {
access_token: string
token_type: string
expires_in: number
id_token: string
refresh_token?: string
}
// Handle authentication callback from Google
app.get('/auth/google/callback', async (req, res, next) => {
const code = req.query.code
const response = await axios.post<GoogleOAuthTokenResponse>('https://oauth2.googleapis.com/token', {
code,
client_id: process.env.YOUTUBE_CLIENT_ID,
client_secret: process.env.YOUTUBE_CLIENT_SECRET,
redirect_uri: process.env.YOUTUBE_REDIRECT_URI,
grant_type: 'authorization_code'
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
const accessToken = response.data.access_token
// Now that we have the access token and id token, we can use them to call Google API services
const googleResponse = await axios.get('https://www.googleapis.com/some_google_api', {
headers: {
Authorization: `Bearer ${accessToken}`
}
})
res.send(googleResponse.data)
})
But I get the following error:
/Users/demian/Desktop/work/youtube-navigator/server/node_modules/axios/lib/core/settle.js:19
reject(new AxiosError(
^
AxiosError: Request failed with status code 400
at settle (/Users/demian/Desktop/work/youtube-navigator/server/node_modules/axios/lib/core/settle.js:19:12)
at Unzip.handleStreamEnd (/Users/demian/Desktop/work/youtube-navigator/server/node_modules/axios/lib/adapters/http.js:548:11)
at Unzip.emit (node:events:525:35)
at Unzip.emit (node:domain:489:12)
at endReadableNT (node:internal/streams/readable:1359:12)
at processTicksAndRejections (node:internal/process/task_queues:82:21) {
code: 'ERR_BAD_REQUEST',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [ 'xhr', 'http' ],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: [Function], Blob: [class Blob] },
validateStatus: [Function: validateStatus],
headers: AxiosHeaders {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'axios/1.3.2',
'Content-Length': '313',
'Accept-Encoding': 'gzip, compress, deflate, br'
},
method: 'post',
url: 'https://oauth2.googleapis.com/token',
data: 'code=4%2F0AWtgzh5yCRs6xsZCCAEUhJGVTNE22-xpI9u3KaaCrgGl_wI618yJSGHUta53Z-w5VB7_Lw&client_id=252564275188-g55c2qnu6ajii45m3d154uuj9fnhlbfb.apps.googleusercontent.com&client_secret=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&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fauth%2Fgoogle%2Fcallback&grant_type=authorization_code'
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
finish: [Function: requestOnFinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: '313',
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: true,
socket: TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
secureConnecting: false,
_SNICallback: null,
servername: 'oauth2.googleapis.com',
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object: null prototype],
_eventsCount: 9,
connecting: false,
_hadError: false,
_parent: null,
_host: 'oauth2.googleapis.com',
_closeAfterHandlingError: false,
_readableState: [ReadableState],
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
timeout: 5000,
parser: null,
_httpMessage: null,
[Symbol(res)]: [TLSWrap],
[Symbol(verified)]: true,
[Symbol(pendingSession)]: null,
[Symbol(async_id_symbol)]: -1,
[Symbol(kHandle)]: [TLSWrap],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: [Timeout],
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 1,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object]
},
_header: 'POST /token HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Content-Type: application/x-www-form-urlencoded\r\n' +
'User-Agent: axios/1.3.2\r\n' +
'Content-Length: 313\r\n' +
'Accept-Encoding: gzip, compress, deflate, br\r\n' +
'Host: oauth2.googleapis.com\r\n' +
'Connection: keep-alive\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype] {},
freeSockets: [Object: null prototype],
keepAliveMsecs: 1000,
keepAlive: true,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/token',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 4,
_maxListeners: undefined,
socket: null,
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [Array],
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 400,
statusMessage: 'Bad Request',
client: [TLSSocket],
_consuming: true,
_dumped: false,
req: [Circular *1],
responseUrl: 'https://oauth2.googleapis.com/token',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 30,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'oauth2.googleapis.com',
protocol: 'https:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 313,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: 'https://oauth2.googleapis.com/token',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kEndCalled)]: true,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'content-type': [Array],
'user-agent': [Array],
'content-length': [Array],
'accept-encoding': [Array],
host: [Array]
},
[Symbol(errored)]: null,
[Symbol(kUniqueHeaders)]: null
},
response: {
status: 400,
statusText: 'Bad Request',
headers: AxiosHeaders {
date: 'Tue, 07 Feb 2023 12:04:34 GMT',
expires: 'Mon, 01 Jan 1990 00:00:00 GMT',
pragma: 'no-cache',
'cache-control': 'no-cache, no-store, max-age=0, must-revalidate',
'content-type': 'application/json; charset=utf-8',
vary: 'Origin, X-Origin, Referer',
server: 'scaffolding on HTTPServer2',
'x-xss-protection': '0',
'x-frame-options': 'SAMEORIGIN',
'x-content-type-options': 'nosniff',
'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000',
'transfer-encoding': 'chunked'
},
config: {
transitional: [Object],
adapter: [Array],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: [Object],
validateStatus: [Function: validateStatus],
headers: [AxiosHeaders],
method: 'post',
url: 'https://oauth2.googleapis.com/token',
data: 'code=4%2F0AWtgzh5yCRs6xsZCCAEUhJGVTNE22-xpI9u3KaaCrgGl_wI618yJSGHUta53Z-w5VB7_Lw&client_id=252564275188-g55c2qnu6ajii45m3d154uuj9fnhlbfb.apps.googleusercontent.com&client_secret=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&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fauth%2Fgoogle%2Fcallback&grant_type=authorization_code'
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: '313',
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: true,
socket: [TLSSocket],
_header: 'POST /token HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Content-Type: application/x-www-form-urlencoded\r\n' +
'User-Agent: axios/1.3.2\r\n' +
'Content-Length: 313\r\n' +
'Accept-Encoding: gzip, compress, deflate, br\r\n' +
'Host: oauth2.googleapis.com\r\n' +
'Connection: keep-alive\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/token',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'oauth2.googleapis.com',
protocol: 'https:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kEndCalled)]: true,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(errored)]: null,
[Symbol(kUniqueHeaders)]: null
},
data: { error: 'invalid_grant', error_description: 'Bad Request' }
}
}
I checked few times that my env variables are correct. So I think this is problem with my code.
Maybe this is stupid question, but I really couldn't understand what I do incorrectly. What to do?
I have a little Node application that is supposed to fetch data from a website and notify me when a new article is posted. The fetching part looks something like this
console.log("Fetching...");
const url = "https://www.jw.org/ro/ce-e-nou/";
const response = await axios(url, {
headers: {
"Access-Control-Allow-Origin": "*",
},
}).catch((err) => console.log(err));
console.log("Fetching done");
If i run my code locally everything works just fine. But if I deploy it as a worker on Heroku (because I can't do what I want to with a Express server) it shows "Fetching..." for a long time and after some minutes I get this massive timeout error
AxiosError: read ETIMEDOUT
at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
syscall: 'read',
code: 'ETIMEDOUT',
errno: -110,
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [Function: httpAdapter],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: [Function] },
validateStatus: [Function: validateStatus],
headers: {
Accept: 'application/json, text/plain, */*',
'Access-Control-Allow-Origin': '*',
'User-Agent': 'axios/0.27.2'
},
url: 'https://www.jw.org/ro/ce-e-nou/',
method: 'get',
data: undefined
},
request: <ref *1> Writable {
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
_events: [Object: null prototype] {
response: [Function: handleResponse],
error: [Function: handleRequestError],
socket: [Function: handleRequestSocket]
},
_eventsCount: 3,
_maxListeners: undefined,
_options: {
maxRedirects: 21,
maxBodyLength: 10485760,
protocol: 'https:',
path: '/ro/ce-e-nou/',
method: 'GET',
headers: [Object],
agent: undefined,
agents: [Object],
auth: undefined,
hostname: 'www.jw.org',
port: null,
nativeProtocols: [Object],
pathname: '/ro/ce-e-nou/'
},
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: [TLSSocket],
_header: 'GET /ro/ce-e-nou/ HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Access-Control-Allow-Origin: *\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Host: www.jw.org\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/ro/ce-e-nou/',
_ended: false,
res: null,
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'www.jw.org',
protocol: 'https:',
_redirectable: [Circular *1],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
_currentUrl: 'https://www.jw.org/ro/ce-e-nou/',
[Symbol(kCapture)]: false
}
What have I done wrong?
const headers = {
'Authorization': `Bearer ${api.oathToken.accessToken}`,
'Content-Type': 'application/json',
}
const body = {
subject: "No Subject",
note: "Please pay this within 10 days!",
}
console.log(invoiceDraft.links[1].href)
await axios({
data : body,
method: "post",
headers: headers,
url: invoiceDraft.links[1].href,
}).then((response) => {
console.log(response.data)
}).catch((err)=> {
console.log(err.response.data.details)
interaction.channel.send(err.toString().slice(0, 1900))
})
I have this code here to send a created invoice
But for some reason it returns this
Error: Request failed with status code 422
This is a UNPROCESSABLE_ENTITY error from Axios and Paypal Invoicing
Im not able to figure out what the issue is.
I am using Node JS and Discord integerations to do this.
Error: Request failed with status code 422
at createError (C:\Users\AriesAsAkshay\OneDrive\Documents\GitHub\AspectServices\node_modules\axios\lib\core\createError.js:16:15)
at settle (C:\Users\AriesAsAkshay\OneDrive\Documents\GitHub\AspectServices\node_modules\axios\lib\core\settle.js:17:12)
at IncomingMessage.handleStreamEnd (C:\Users\AriesAsAkshay\OneDrive\Documents\GitHub\AspectServices\node_modules\axios\lib\adapters\http.js:269:11)
at IncomingMessage.emit (node:events:402:35)
at endReadableNT (node:internal/streams/readable:1343:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
config: {
url: 'https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-4PSV-VKBJ-AK8G-P5CL/send',
method: 'post',
data: '{"subject":"No Subject","note":"Please pay this within 10 days!","send_to_invoicer":true,"send_to_recipient":true,"additional_recipients":[]}',
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json',
Authorization: '',
'User-Agent': 'axios/0.21.4',
'Content-Length': 141
},
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus],
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
}
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
secureConnecting: false,
_SNICallback: null,
servername: 'api.sandbox.paypal.com',
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object: null prototype],
_eventsCount: 10,
connecting: false,
_hadError: false,
_parent: null,
_host: 'api.sandbox.paypal.com',
_readableState: [ReadableState],
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular *1],
[Symbol(res)]: [TLSWrap],
[Symbol(verified)]: true,
[Symbol(pendingSession)]: null,
[Symbol(async_id_symbol)]: 933,
[Symbol(kHandle)]: [TLSWrap],
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object],
[Symbol(RequestTimeout)]: undefined
},
_header: 'POST /v2/invoicing/invoices/INV2-4PSV-VKBJ-AK8G-P5CL/send HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Content-Type: application/json\r\n' +
'Authorization: \r\n' +
'User-Agent: axios/0.21.4\r\n' +
'Content-Length: 141\r\n' +
'Host: api.sandbox.paypal.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype],
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/v2/invoicing/invoices/INV2-4PSV-VKBJ-AK8G-P5CL/send',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
socket: [TLSSocket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [Array],
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 422,
statusMessage: 'Unprocessable Entity',
client: [TLSSocket],
_consuming: true,
_dumped: false,
req: [Circular *1],
responseUrl: 'https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-4PSV-VKBJ-AK8G-P5CL/send',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 18,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'api.sandbox.paypal.com',
protocol: 'https:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 141,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: 'https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-4PSV-VKBJ-AK8G-P5CL/send',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'content-type': [Array],
authorization: [Array],
'user-agent': [Array],
'content-length': [Array],
host: [Array]
}
},
response: {
status: 422,
statusText: 'Unprocessable Entity',
headers: {
'content-type': 'application/json',
'content-length': '377',
connection: 'close',
date: 'Wed, 16 Mar 2022 01:35:50 GMT',
application_id: 'APP-80W284485P519543T',
'cache-control': 'max-age=0, no-cache, no-store, must-revalidate',
caller_acct_num: '7K54CC6QVVV78',
'paypal-debug-id': 'b5b17b1cdd466',
'strict-transport-security': 'max-age=31536000; includeSubDomains'
},
config: {
url: 'https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-4PSV-VKBJ-AK8G-P5CL/send',
method: 'post',
data: '{"subject":"No Subject","note":"Please pay this within 10 days!","send_to_invoicer":true,"send_to_recipient":true,"additional_recipients":[]}',
headers: [Object],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus],
transitional: [Object]
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: [TLSSocket],
_header: 'POST /v2/invoicing/invoices/INV2-4PSV-VKBJ-AK8G-P5CL/send HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Content-Type: application/json\r\n' +
'Authorization: \r\n' +
'User-Agent: axios/0.21.4\r\n' +
'Content-Length: 141\r\n' +
'Host: api.sandbox.paypal.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/v2/invoicing/invoices/INV2-4PSV-VKBJ-AK8G-P5CL/send',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'api.sandbox.paypal.com',
protocol: 'https:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: {
name: 'UNPROCESSABLE_ENTITY',
message: 'The requested action could not be performed, semantically incorrect, or failed business validation.',
debug_id: 'b5b17b1cdd466',
details: [Array],
links: [Array]
}
},
isAxiosError: true,
toJSON: [Function: toJSON]
}
Error Response Details
[
{
issue: 'USER_NOT_FOUND',
description: 'User is not associated with paypal based on invoicer email.'
}
]
const sampleInvoice = (
invoiceNumber,
fromEmail = "sb-pbqgq14333806#business.example.com",
toEmail = "sb-47vx64314481641#personal.example.com"
) => ({
detail: {
invoice_number: invoiceNumber,
currency_code: "USD",
payment_term: { term_type: "NET_10" },
invoice_date: new Date().toISOString().split("T")[0],
},
invoicer: {
name: {
given_name: "John",
surname: "Doe",
},
email_address: fromEmail,
website: "https://www.aspecthosts.com/",
},
primary_recipients: [
{
billing_info: {
name: {
given_name: "John",
surname: "Doe",
},
email_address: toEmail,
},
},
],
items: [
{
name: service,
description: "Hello!",
quantity: "1",
unit_amount: {
currency_code: "USD",
value: amount,
},
unit_of_measure: "QUANTITY",
},
],
});
Most likely there's a problem with the invoice draft's original creation. Show that invoice creation request/response bodies in their entirety. In particular, what is the sandbox email of the invoicer account ? Does it correspond to an actual www.sandbox.paypal.com PayPal business account that you can log into? If not, create it with the correct email via the developer dashboard -> sandbox accounts.
I'm working on a React Native app with Firebase Functions. I am unable to add new contacts to a SendGrid list consistently.
This is my function:
exports.addToSendGridMembersList = functions.https.onCall(async (data) => {
const {first, last, email} = data
p(first,last,email)
const axios = require('axios');
let e = JSON.stringify(email)
let f = JSON.stringify(first)
let l = JSON.stringify(last)
p("JSON.stringify: ", e, f, l)
axios({
method: "PUT",
url: "https://api.sendgrid.com/v3/marketing/contacts",
headers:
{ 'content-type': 'application/json',
authorization: 'Bearer ' + SEND_GRID_KEY },
body:
{ "list_ids": [
"eda0bc01-b098-4366-ad58-8bab03ec9b33"
],
"contacts": [
{
"email": "o#gmail.com",
"first_name": "o",
"last_name": "o"
}
]
}
})
.then(function (error, response, body) {
console.log(error)
console.log(response)
console.log(body);
})
});
I at first thought the axios body was unable to read constants that's why I stringified them and was going to insert them in the body (first, last and email) values. This is the response I get in Google Cloud logs
JSON.stringify: "gg#gmail.com" "gg" "gg"
> Unhandled error { Error: Request failed with status code 400 at createError (/workspace/node_modules/axios/lib/core/createError.js:16:15) at settle (/workspace/node_modules/axios/lib/core/settle.js:17:12) at IncomingMessage.handleStreamEnd (/workspace/node_modules/axios/lib/adapters/http.js:260:11) at IncomingMessage.emit (events.js:203:15) at IncomingMessage.EventEmitter.emit (domain.js:466:23) at endReadableNT (_stream_readable.js:1145:12) at process._tickCallback (internal/process/next_tick.js:63:19) config: { url: 'https://api.sendgrid.com/v3/marketing/contacts', method: 'put', headers: { Accept: 'application/json, text/plain, */*', 'Content-Type': 'application/json', authorization: 'Bearer HIDDEN', 'User-Agent': 'axios/0.21.1' }, transformRequest: [ [Function: transformRequest] ], transformResponse: [ [Function: transformResponse] ], timeout: 0, adapter: [Function: httpAdapter], xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, validateStatus: [Function: validateStatus], body: { list_ids: [Array], contacts: [Array] }, data: undefined }, request: ClientRequest { domain: Domain { domain: null, _events: [Object], _eventsCount: 3, _maxListeners: undefined, members: [], [Symbol(kWeak)]: WeakReference {} }, _events: [Object: null prototype] { socket: [Function], abort: [Function], aborted: [Function], connect: [Function], error: [Function], timeout: [Function], prefinish: [Function: requestOnPrefinish] }, _eventsCount: 7, _maxListeners: undefined, output: [], outputEncodings: [], outputCallbacks: [], outputSize: 0, writable: true, _last: true, chunkedEncoding: false, shouldKeepAlive: false, useChunkedEncodingByDefault: true, sendDate: false, _removedConnection: false, _removedContLen: false, _removedTE: false, _contentLength: 0, _hasBody: true, _trailer: '', finished: true, _headerSent: true, socket: TLSSocket { _tlsOptions: [Object], _secureEstablished: true, _securePending: false, _newSessionPending: false, _controlReleased: true, _SNICallback: null, servername: 'api.sendgrid.com', alpnProtocol: false, authorized: true, authorizationError: null, encrypted: true, _events: [Object], _eventsCount: 9, connecting: false, _hadError: false, _handle: [TLSWrap], _parent: null, _host: 'api.sendgrid.com', _readableState: [ReadableState], readable: true, domain: [Domain], _maxListeners: undefined, _writableState: [WritableState], writable: false, allowHalfOpen: false, _sockname: null, _pendingData: null, _pendingEncoding: '', server: undefined, _server: null, ssl: [TLSWrap], _requestCert: true, _rejectUnauthorized: true, parser: null, _httpMessage: [Circular], [Symbol(res)]: [TLSWrap], [Symbol(asyncId)]: 81, [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0, [Symbol(connect-options)]: [Object] }, connection: TLSSocket { _tlsOptions: [Object], _secureEstablished: true, _securePending: false, _newSessionPending: false, _controlReleased: true, _SNICallback: null, servername: 'api.sendgrid.com', alpnProtocol: false, authorized: true, authorizationError: null, encrypted: true, _events: [Object], _eventsCount: 9, connecting: false, _hadError: false, _handle: [TLSWrap], _parent: null, _host: 'api.sendgrid.com', _readableState: [ReadableState], readable: true, domain: [Domain], _maxListeners: undefined, _writableState: [WritableState], writable: false, allowHalfOpen: false, _sockname: null, _pendingData: null, _pendingEncoding: '', server: undefined, _server: null, ssl: [TLSWrap], _requestCert: true, _rejectUnauthorized: true, parser: null, _httpMessage: [Circular], [Symbol(res)]: [TLSWrap], [Symbol(asyncId)]: 81, [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0, [Symbol(connect-options)]: [Object] }, _header: 'PUT /v3/marketing/contacts HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/json\r\nauthorization: Bearer HIDDEN\r\nUser-Agent: axios/0.21.1\r\nHost: api.sendgrid.com\r\nConnection: close\r\nContent-Length: 0\r\n\r\n', _onPendingData: [Function: noopPendingOutput], agent: Agent { domain: null, _events: [Object], _eventsCount: 1, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object], requests: {}, sockets: [Object], freeSockets: {}, keepAliveMsecs: 1000, keepAlive: false, maxSockets: Infinity, maxFreeSockets: 256, maxCachedSessions: 100, _sessionCache: [Object] }, socketPath: undefined, timeout: undefined, method: 'PUT', insecureHTTPParser: undefined, path: '/v3/marketing/contacts', _ended: true, res: IncomingMessage { _readableState: [ReadableState], readable: false, domain: [Domain], _events: [Object], _eventsCount: 3, _maxListeners: undefined, socket: [TLSSocket], connection: [TLSSocket], httpVersionMajor: 1, httpVersionMinor: 1, httpVersion: '1.1', complete: true, headers: [Object], rawHeaders: [Array], trailers: {}, rawTrailers: [], aborted: false, upgrade: false, url: '', method: null, statusCode: 400, statusMessage: 'Bad Request', client: [TLSSocket], _consuming: false, _dumped: false, req: [Circular], responseUrl: 'https://api.sendgrid.com/v3/marketing/contacts', redirects: [] }, aborted: undefined, timeoutCb: null, upgradeOrConnect: false, parser: null, maxHeadersCount: null, _redirectable: Writable { _writableState: [WritableState], writable: true, domain: [Domain], _events: [Object], _eventsCount: 2, _maxListeners: undefined, _options: [Object], _ended: true, _ending: true, _redirectCount: 0, _redirects: [], _requestBodyLength: 0, _requestBodyBuffers: [], _onNativeResponse: [Function], _currentRequest: [Circular], _currentUrl: 'https://api.sendgrid.com/v3/marketing/contacts' }, [Symbol(isCorked)]: false, [Symbol(outHeadersKey)]: [Object: null prototype] { accept: [Array], 'content-type': [Array], authorization: [Array], 'user-agent': [Array], host: [Array] } }, response: { status: 400, statusText: 'Bad Request', headers: { server: 'nginx', date: 'Mon, 28 Dec 2020 01:39:11 GMT', 'content-type': 'application/json', 'content-length': '50', connection: 'close', 'x-amzn-requestid': '7cec2ea8-620a-4096-af97-c58c3f5f12d2', 'access-control-allow-origin': '*', 'access-control-allow-headers': 'AUTHORIZATION, Content-Type, On-behalf-of, x-sg-elas-acl, X-Recaptcha, X-Request-Source', 'x-amz-apigw-id': 'YPWR7E1GvHcFVXg=', 'access-control-allow-methods': 'PUT,DELETE,OPTIONS', 'access-control-expose-headers': 'Link, Location', 'x-amzn-trace-id': 'Root=1-5fe9373f-69accb7255a34e6321797cf3;Sampled=0', 'x-envoy-upstream-service-time': '93', 'referrer-policy': 'strict-origin-when-cross-origin', 'x-content-type-options': 'nosniff', 'x-ratelimit-limit': '200', 'x-ratelimit-remaining': '199', 'x-ratelimit-reset': '49' }, config: { url: 'https://api.sendgrid.com/v3/marketing/contacts', method: 'put', headers: [Object], transformRequest: [Array], transformResponse: [Array], timeout: 0, adapter: [Function: httpAdapter], xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, validateStatus: [Function: validateStatus], body: [Object], data: undefined }, request: ClientRequest { domain: [Domain], _events: [Object], _eventsCount: 7, _maxListeners: undefined, output: [], outputEncodings: [], outputCallbacks: [], outputSize: 0, writable: true, _last: true, chunkedEncoding: false, shouldKeepAlive: false, useChunkedEncodingByDefault: true, sendDate: false, _removedConnection: false, _removedContLen: false, _removedTE: false, _contentLength: 0, _hasBody: true, _trailer: '', finished: true, _headerSent: true, socket: [TLSSocket], connection: [TLSSocket], _header: 'PUT /v3/marketing/contacts HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/json\r\nauthorization: Bearer HIDDEN\r\nUser-Agent: axios/0.21.1\r\nHost: api.sendgrid.com\r\nConnection: close\r\nContent-Length: 0\r\n\r\n', _onPendingData: [Function: noopPendingOutput], agent: [Agent], socketPath: undefined, timeout: undefined, method: 'PUT', insecureHTTPParser: undefined, path: '/v3/marketing/contacts', _ended: true, res: [IncomingMessage], aborted: undefined, timeoutCb: null, upgradeOrConnect: false, parser: null, maxHeadersCount: null, _redirectable: [Writable], [Symbol(isCorked)]: false, [Symbol(outHeadersKey)]: [Object] }, data: { errors: [Array] } }, isAxiosError: true, toJSON: [Function: toJSON] }
Turns out axios was failing. I switched to request and it worked like a charm. Here's my final code:
var request = require("request");
var options = { method: 'PUT',
url: 'https://api.sendgrid.com/v3/marketing/contacts',
headers:
{ 'content-type': 'application/json',
authorization: 'Bearer ' + SEND_GRID_KEY},
body:
{ list_ids: [ 'eda0bc01-b098-4366-ad58-8bab03ec9b33' ],
contacts: [
{
"email": e,
"first_name": f,
"last_name": l
}
]
},
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
You can test out SendGrid's api and generate code here: https://sendgrid.api-docs.io/v3.0/contacts/add-or-update-a-contact
I generate an access token using 'simple-oauth2' that looks like this
import { AuthorizationCode } from 'simple-oauth2';
scopes = ['offline_access', 'https://outlook.office.com/IMAP.AccessAsUser.All',
'https://outlook.office.com/SMTP.Send','User.Read'];
async getCredentials(code: string) {
const client = new AuthorizationCode(this.oauthConfig);
let tokenParams = {
code,
redirect_uri: config.MICROSOFT_CREDENTIALS.REDIRECT_URIS[0],
scope: this.scopes.join(' ')
}
return await client.getToken(tokenParams)
};
// Result from getCredentials
AccessToken {
token: {
token_type: 'Bearer',
scope: 'https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send https://outlook.office.com/User.Read',
expires_in: 3599,
ext_expires_in: 3599,
access_token: 'eyJ0eXAiOiJKV1QiLCJub25jZSI6I...',
refresh_token: '0.ATUAd-LjmxfEgkWp0wIFCKwhpRA-z-...',
expires_at: 2020-09-25T01:13:08.970Z
}
}
Following this guide, I then pass the access_token field to a GET request to https://graph.microsoft.com/v1.0/me
getUserProfile(accessToken: string) {
return axios.get("https://graph.microsoft.com/v1.0/me", {
headers: {
Authorization: 'Bearer ' + accessToken,
'Content-Type': 'application/json'
}
})
.then((res) => {
return res.data;
})
.catch((reason) => {
console.log("getUserProfile failed");
console.log(reason);
})
};
This fails with the following error
getUserProfile failed
Error: Request failed with status code 401
at createError (C:\Users\me\Desktop\projects\proj\node_modules\axios\lib\core\createError.js:16:15)
at settle (C:\Users\me\Desktop\projects\proj\node_modules\axios\lib\core\settle.js:17:12)
at IncomingMessage.handleStreamEnd (C:\Users\me\Desktop\projects\proj\node_modules\axios\lib\adapters\http.js:236:11)
at IncomingMessage.emit (events.js:323:22)
at IncomingMessage.EventEmitter.emit (domain.js:482:12)
at endReadableNT (_stream_readable.js:1204:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21) {
config: {
url: 'https://graph.microsoft.com/v1.0/me',
method: 'get',
headers: {
Accept: 'application/json, text/plain, */*',
Authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IjByZ0t3MFRLOEF...',
'Content-Type': 'application/json',
'User-Agent': 'axios/0.19.2'
},
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
data: undefined
},
request: ClientRequest {
_events: [Object: null prototype] {
socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 6,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'graph.microsoft.com',
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object: null prototype],
_eventsCount: 9,
connecting: false,
_hadError: false,
_parent: null,
_host: 'graph.microsoft.com',
_readableState: [ReadableState],
readable: true,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
[Symbol(res)]: [TLSWrap],
[Symbol(asyncId)]: 1233,
[Symbol(kHandle)]: [TLSWrap],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object]
},
connection: TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'graph.microsoft.com',
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object: null prototype],
_eventsCount: 9,
connecting: false,
_hadError: false,
_parent: null,
_host: 'graph.microsoft.com',
_readableState: [ReadableState],
readable: true,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
[Symbol(res)]: [TLSWrap],
[Symbol(asyncId)]: 1233,
[Symbol(kHandle)]: [TLSWrap],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object]
},
_header: 'GET /v1.0/me HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IjByZ0t...\r\n' +
'Content-Type: application/json\r\n' +
'User-Agent: axios/0.19.2\r\n' +
'Host: graph.microsoft.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'GET',
insecureHTTPParser: undefined,
path: '/v1.0/me',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
readable: false,
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
socket: [TLSSocket],
connection: [TLSSocket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 401,
statusMessage: 'Unauthorized',
client: [TLSSocket],
_consuming: false,
_dumped: false,
req: [Circular],
responseUrl: 'https://graph.microsoft.com/v1.0/me',
redirects: [],
[Symbol(kCapture)]: false
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
_redirectable: Writable {
_writableState: [WritableState],
writable: true,
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest: [Circular],
_currentUrl: 'https://graph.microsoft.com/v1.0/me',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
authorization: [Array],
'content-type': [Array],
'user-agent': [Array],
host: [Array]
}
},
response: {
status: 401,
statusText: 'Unauthorized',
headers: {
'cache-control': 'private',
'content-type': 'application/json',
'request-id': '6026815f-6c31-48c3-9b70-a6eb3cda8e44',
'client-request-id': '6026815f-6c31-48c3-9b70-a6eb3cda8e44',
'x-ms-ags-diagnostic': '{"ServerInfo":{"DataCenter":"West US","Slice":"SliceC","Ring":"5","ScaleUnit":"002","RoleInstance":"AGSFE_IN_13"}}',
'www-authenticate': 'Bearer realm="", authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize", client_id="00000003-0000-0000-c000-000000000000"',
'strict-transport-security': 'max-age=31536000',
date: 'Fri, 25 Sep 2020 00:13:11 GMT',
connection: 'close',
'content-length': '330'
},
config: {
url: 'https://graph.microsoft.com/v1.0/me',
method: 'get',
headers: [Object],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
data: undefined
},
request: ClientRequest {
_events: [Object: null prototype],
_eventsCount: 6,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [TLSSocket],
connection: [TLSSocket],
_header: 'GET /v1.0/me HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IjByZ0...\r\n' +
'Content-Type: application/json\r\n' +
'User-Agent: axios/0.19.2\r\n' +
'Host: graph.microsoft.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
method: 'GET',
insecureHTTPParser: undefined,
path: '/v1.0/me',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: { error: [Object] }
},
isAxiosError: true,
toJSON: [Function]
Documentation for /me and the guide states I just need User.Read for my permissions and that my headers are correct so I do not know why it is failing. The Microsoft account I am using for testing is a Work Account.
I have briefly tried using the Graph SDK but I feel like this approach is 99% of the way there.
Big picture wise I am trying to save the user's email address so I can use the OAuth2 token to connect to O365 through IMAP and SMTP. If I hardcode what I know the email address to be everything works so automating the retrieval of the user's email address through Graphs is the last thing I need.
You should not use a token that does not belong to the api. You are calling the Microsoft graph api, but you are requesting a token for Outlook, so you only need to change the scope to:
scopes = ['offline_access', 'https://graph.microsoft.com/.default'];
Another method is keeping your current scopes and call GET https://outlook.office365.com/api/v2.0/me/ to get the signed-in user's information.