I'm currently working on creating a Firebase Cloud Function that retrieve a list of books from the NYTimes API and grabs some additional information from the Google Books API. By using the following URL : "https://www.googleapis.com/books/v1/volumes?q=isbn:&key=" I'm able to retrieve books' details based on an ISBN.
My FREE quota for Google Books API is 1000 request per day and 100 request every 100 seconds per user. I'm only sending 15 requests and my current quota is below 200.
My key is restricted only to be used with Google Books API and it could be use from anywhere (iOS, web, android...etc)
After building the function I'm testing everything locally and data gets populated correctly although once I deploy the function on firebase I get a 403 status code when trying to access to the API.
Unfortunately I'm not getting more details about the error. It just this:
{ Error: Request failed with status code 403
at createError (/user_code/node_modules/axios/lib/core/createError.js:16:15)
at settle (/user_code/node_modules/axios/lib/core/settle.js:18:12)
at IncomingMessage.handleStreamEnd (/user_code/node_modules/axios/lib/adapters/http.js:201:11)
at emitNone (events.js:91:20)
at IncomingMessage.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:80:11)
at process._tickDomainCallback (internal/process/next_tick.js:128:9)
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers:
{ Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/0.18.0' },
method: 'get',
url: 'https://www.googleapis.com/books/v1/volumes?q=isbn:0525520384&key=##########’,
data: undefined },
request:
ClientRequest {
domain: null,
_events:
{ socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish] },
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedHeader: {},
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: null,
npnProtocol: false,
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 10,
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'www.googleapis.com',
_readableState: [Object],
readable: false,
domain: null,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
destroyed: true,
_bytesDispatched: 210,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: null,
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
read: [Function],
_consuming: true,
write: [Function: writeAfterFIN],
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1 },
connection:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: null,
npnProtocol: false,
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 10,
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'www.googleapis.com',
_readableState: [Object],
readable: false,
domain: null,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
destroyed: true,
_bytesDispatched: 210,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: null,
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
read: [Function],
_consuming: true,
write: [Function: writeAfterFIN],
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1 },
_header: 'GET /books/v1/volumes?q=isbn:0525520384&key=##### HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nUser-Agent: axios/0.18.0\r\nHost: www.googleapis.com\r\nConnection: close\r\n\r\n',
_headers:
{ accept: 'application/json, text/plain, */*',
'user-agent': 'axios/0.18.0',
host: 'www.googleapis.com' },
_headerNames: { accept: 'Accept', 'user-agent': 'User-Agent', host: 'Host' },
_onPendingData: null,
agent:
Agent {
domai
I was facing a similar issue. I was able to sucessfully use the Books API when I was running my functions locally (ie. firebase serve). But when I deployed the functions and the app to hosting, I was getting a mysterious 403: Forbidden error.
I tried several things so I'm not sure which helped so I'll just list everything:
Updated firebase functions: npm install --save firebase-functions#latest
Switched from using node-fetch to using axios for calling the API
Add country to URL: https://www.googleapis.com/books/v1/volumes?country=US&q=insubject:${randomWord}&key=${functions.config().books.api.key}
Regenerated Google Books API key
Removed all Application restrictions in the google cloud console
Hope this helps somebody out there because I was wrestling with it for hours!
By adding more restriction to the key (HTTP Only) and adding a referer header to my request and finally adding a country code to the end of the request my original issue is now gone.
Note: Referer should match the http restriction value on your key.
Final URL :
https://www.googleapis.com/books/v1/volumes?q=isbn:#book-isbn#&key=#app-key#&country=US
Google books apis need to know the country to show relevant books. either pass it through the country parameter or you can use the client to call the api's directly.
Related
First question I'm asking, hoping for a "miracle", safe to say I'm a Jr Developer:
I have a NextJs project and I am using Prisma & Supabase. I made it very simple, I only have one item in the database, one index.js page with a getserversideprops function. When I use axios to get from the API folder, this works locally, but when I deploy it it doesn't work, this is the log on Vercel when I deploy:
[GET] /
22:01:04:79
d: 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: Infinity,
protocol: 'http:',
path: '/api/fetches/get-products',
method: 'GET',
headers: [Object: null prototype],
agents: [Object],
auth: undefined,
beforeRedirect: [Function: dispatchBeforeRedirect],
beforeRedirects: [Object],
hostname: 'localhost',
port: '3000',
agent: undefined,
nativeProtocols: [Object],
pathname: '/api/fetches/get-products'
},
_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,
strictContentLength: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: [Socket],
_header: 'GET /api/fetches/get-products HTTP/1.1\r\n' +
'Accept: application/json, text/plain, /\r\n' +
'User-Agent: axios/1.3.0\r\n' +
'Accept-Encoding: gzip, compress, deflate, br\r\n' +
'Host: localhost:3000\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/api/fetches/get-products',
_ended: false,
res: null,
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'localhost',
protocol: 'http:',
_redirectable: [Circular *1],
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kEndCalled)]: true,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(kUniqueHeaders)]: null
},
_currentUrl: 'http://localhost:3000/api/fetches/get-products',
[Symbol(kCapture)]: false
},
cause: Error: connect ECONNREFUSED 127.0.0.1:3000
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1300:16) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 3000
},
page: '/'
}
RequestId: 91b310f3-2890-4aa3-b936-1cea9a199692 Error: Runtime exited with error: exit status 1
Runtime.ExitError
this is my index.js
export default function Home({products}) {
return (
<>
<main>
{JSON.stringify(products)}
</main>
</>
)
}
export async function getServerSideProps() {
const port = process.env.API_HOST_PORT || 3000
const host = `http://localhost:${port}`
const { data: products } = await axios.get(`${host}/api/fetches/get-products`);
return { props: { products } };
}
the .env files are imported at the top.. You will need to let me know what other information you need from my end so I can inform you.
It's hard for me to debug because it doesn't make sense on my end, I've tried deploying with Vercel and Digital Ocean, but both have a 500 error and when I check the logs it says what I shared. Schema was built correctly, the API Keys are correct. I actually "copied" this from a different project so I could practice more with prisma, but this one doesn't work..
I tried to configure a next js project with supabase
But the data I receive is not consistent
To do this I have configured the following 3 files :
.env.local
NEXT_PUBLIC_SUPABASE_URL= ********
NEXT_PUBLIC_SUPABASE_ANON_KEY= ********
supabase.js
import { createClient } from '#supabase/supabase-js'
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
)
export default supabase;
pages/api/posts.js
import supabase from '../../utils/supabase';
export default function handler(req, res) {
const posts = supabase.from("posts").select("*");
res.status(200).json(posts);
}
When I console log my posts variable, I have no data property.
It returned this,
PostgrestFilterBuilder {
fetch: [Function (anonymous)],
shouldThrowOnError: false,
allowEmpty: false,
url: URL {
href: '********/rest/v1/posts?select=*',
origin: '********',
protocol: 'https:',
username: '',
password: '',
host: '********',
hostname: '********',
port: '',
pathname: '/rest/v1/posts',
search: '?select=*',
searchParams: URLSearchParams { 'select' => '*' },
hash: ''
},
headers: {
'X-Client-Info': 'supabase-js/1.35.4',
apikey: '********',
Authorization: 'Bearer ********'
},
schema: 'public',
_subscription: null,
_realtime: RealtimeClient {
accessToken: null,
channels: [],
endPoint: 'wss://********/realtime/v1/websocket',
headers: { 'X-Client-Info': 'supabase-js/1.35.4' },
params: {
apikey: '********'
},
timeout: 10000,
transport: [Function: W3CWebSocket],
heartbeatIntervalMs: 30000,
longpollerTimeout: 20000,
heartbeatTimer: undefined,
pendingHeartbeatRef: null,
ref: 0,
logger: [Function: noop],
conn: null,
sendBuffer: [],
serializer: Serializer { HEADER_LENGTH: 1 },
stateChangeCallbacks: { open: [], close: [], error: [], message: [] },
reconnectAfterMs: [Function (anonymous)],
encode: [Function (anonymous)],
decode: [Function: bound decode],
reconnectTimer: Timer {
callback: [Function (anonymous)],
timerCalc: [Function (anonymous)],
timer: undefined,
tries: 0
}
},
_headers: {
'X-Client-Info': 'supabase-js/1.35.4',
apikey: '********',
Authorization: 'Bearer ********'
},
_schema: 'public',
_table: 'posts',
method: 'GET',
cs: [Function: contains],
cd: [Function: containedBy],
sl: [Function: rangeLt],
sr: [Function: rangeGt],
nxl: [Function: rangeGte],
nxr: [Function: rangeLte],
adj: [Function: rangeAdjacent],
ov: [Function: overlaps]
}
Also when I look at the dashboard of my supabase database I see that no query is receive
It seems that this query is not sent to the supabase API but I have no idea why ?
You need to await the Promise: https://supabase.com/docs/reference/javascript/select
import supabase from '../../utils/supabase';
export default async function handler(req, res) {
const posts = await supabase.from("posts").select("*");
res.status(200).json(posts);
}
I have Next JS Application already hosted in VPS and data fetching using GetStaticProps and GetStaticPaths, then this application will consume REST Api from my subdomain with same VPS.
Next JS Application : http://zeffry.my.id
Rest API http://admin.zeffry.my.id/api
When i running npm run build on VPS, I get an error which indicates it is related to getStaticPaths.
zeffry#zeffryportofolio:/var/www/zeffry-reynando$ npm run build
> zeffry-reynando#0.1.0 build
> next build
info - Loaded env from /var/www/zeffry-reynando/.env
info - SWC minify release candidate enabled. https://nextjs.link/swcmin
info - Linting and checking validity of types
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data ..[AxiosError: Request failed with status code 400] {
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'
},
method: 'get',
url: "'http://admin.zeffry.my.id/api';/portfolio",
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: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: 'localhost',
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular *1],
[Symbol(async_id_symbol)]: 13,
[Symbol(kHandle)]: [TCP],
[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(RequestTimeout)]: undefined
},
_header: 'GET %27http://admin.zeffry.my.id/api%27;/portfolio HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Host: localhost\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
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,
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '%27http://admin.zeffry.my.id/api%27;/portfolio',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 4,
_maxListeners: undefined,
socket: [Socket],
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: [Socket],
_consuming: false,
_dumped: false,
req: [Circular *1],
responseUrl: '%27http://admin.zeffry.my.id/api%27;/portfolio',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 10,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'localhost',
protocol: 'http:',
_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: '%27http://admin.zeffry.my.id/api%27;/portfolio',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'user-agent': [Array],
host: [Array]
},
[Symbol(kUniqueHeaders)]: null
},
response: {
status: 400,
statusText: 'Bad Request',
headers: {
server: 'nginx/1.18.0 (Ubuntu)',
date: 'Fri, 02 Sep 2022 11:48:55 GMT',
'content-type': 'text/html',
'content-length': '166',
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],
method: 'get',
url: "'http://admin.zeffry.my.id/api';/portfolio",
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: [Socket],
_header: 'GET %27http://admin.zeffry.my.id/api%27;/portfolio HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Host: localhost\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '%27http://admin.zeffry.my.id/api%27;/portfolio',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'localhost',
protocol: 'http:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(kUniqueHeaders)]: null
},
data: '<html>\r\n' +
'<head><title>400 Bad Request</title></head>\r\n' +
'<body>\r\n' +
'<center><h1>400 Bad Request</h1></center>\r\n' +
'<hr><center>nginx/1.18.0 (Ubuntu)</center>\r\n' +
'</body>\r\n' +
'</html>\r\n'
}
}
> Build error occurred
Error: Failed to collect page data for /portfolio/[slug]
at /var/www/zeffry-reynando/node_modules/next/dist/build/utils.js:743:15
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
type: 'Error'
}
This line indicate the rror :
export const getStaticProps: GetStaticProps = async (context) => {
try {
const { slug } = context.params as IParams;
const url =
process.env["NODE_ENV"] == "development"
? process.env["BASE_API_LOCALHOST_URL"]
: process.env["BASE_API_URL"];
const portfolio = await axios.get(`${url}/portfolio/${slug}`);
if (!portfolio.data.data) throw "Data tidak ditemukan";
const data: PortfolioDetailInterface = portfolio.data.data;
const props = {
portfolio: data,
};
return {
props: props,
};
} catch (error) {
return {
notFound: true,
};
}
};
export const getStaticPaths: GetStaticPaths = async (context) => {
const url =
process.env["NODE_ENV"] == "development"
? process.env["BASE_API_LOCALHOST_URL"]
: process.env["BASE_API_URL"];
const portfolio = await axios.get(`${url}/portfolio`);
const arrPortfolio: PortfolioInterface[] = portfolio.data.data;
const slug = arrPortfolio.map(function (val) {
return {
params: {
slug: val.title_slug,
},
};
});
return {
paths: slug,
fallback: "blocking",
};
};
export default PortfolioDetailPage;
I can confirm that when testing on my local machine, the application runs smoothly without any problems.
If you access this endpoint http://admin.zeffry.my.id/api/portfolio/amerta, response is
// 20220902190903
// http://admin.zeffry.my.id/api/portfolio/amerta
{
"success": true,
"data": {
"id": 1,
"type_application_id": 11,
"main_technology_id": 15,
"title": "Amerta",
"title_slug": "amerta",
"short_description": "amerta",
"full_description": "<p>amerta</p>",
"banner_image": "http://admin.zeffry.my.id/storage/images/portfolio/banner/6311b9c0e52cc1662106048.png",
"github_url": null,
"web_url": null,
"google_playstore_url": null,
"app_store_url": null,
"created_at": "2022-09-02T08:07:30.000000Z",
"updated_at": "2022-09-02T08:07:30.000000Z",
"created_by": null,
"updated_by": null,
"previewImages": [
{
"id": "c9a51e0b-4357-44a5-9b92-07f404050561",
"portfolio_id": 1,
"image": "http://admin.zeffry.my.id/storage/images/portfolio/preview/6311b9c2c9b281662106050.png"
}
],
"main_technology": {
"id": 15,
"name": "Flutter"
},
"type": {
"id": 11,
"name": "Mobile Apps"
},
"other_technology": [
{
"id": "faaf2db5-1c0c-4c05-8fa9-3da43fb4e2ce",
"portfolio_id": 1,
"technology_id": 15,
"technology": {
"id": 15,
"name": "Flutter"
}
}
],
"preview_images": [
{
"id": "c9a51e0b-4357-44a5-9b92-07f404050561",
"portfolio_id": 1,
"image": "http://admin.zeffry.my.id/storage/images/portfolio/preview/6311b9c2c9b281662106050.png"
}
]
}
}
Are there any settings for deploying missing?
I have a method that should return a response from another server. I use request-promise and put the URL in the options object.
As you can see in the code below, all is in good shape, but when I send the request, it returns 404 - resource not found.
When I change the request(options) method with request("https://api.quickpay.net/payments"), I get a positive answer from the server -- it tells me to add headers and so forth, which is positive.
public requestNewQuickpayPayment(order_id: String, currency : String, callback: Function) {
var options = {
method: 'POST',
uri: 'https://api.quickpay.net/payments',
form:{
order_id : "order123",
currency : "dkk"
},
headers: {
"Content-Type" : "application/json",
'Accept-Version': 'v10'
},
json: true
};
request(options).then((response:any)=>{
console.log(response);
return response;
}).catch((error:any)=>{
console.log(error);
return error;
}).finally(()=>{
console.log("done");
})
}
Something from the console
Request {
_events: [Object],
_eventsCount: 5,
_maxListeners: undefined,
method: 'POST',
uri: [Url],
transform2xxOnly: true,
headers: [Object],
readable: true,
writable: true,
explicitMethod: true,
_qs: [Querystring],
_auth: [Auth],
_oauth: [OAuth],
_multipart: [Multipart],
_redirect: [Redirect],
_tunnel: [Tunnel],
_rp_resolve: [Function],
_rp_reject: [Function],
_rp_promise: [Promise],
_rp_callbackOrig: undefined,
callback: [Function],
_rp_options: [Object],
setHeader: [Function],
hasHeader: [Function],
getHeader: [Function],
removeHeader: [Function],
localAddress: undefined,
pool: {},
dests: [],
__isRequestRequest: true,
_callback: [Function: RP$callback],
proxy: null,
tunnel: true,
setHost: true,
originalCookieHeader: undefined,
_disableCookies: true,
_jar: undefined,
port: 443,
host: 'api.quickpay.net',
body: 'order_id=asdasdasd¤cy=dkk',
path: '/payments',
_json: true,
httpModule: [Object],
agentClass: [Function],
agent: [Agent],
_started: true,
href: 'https://api.quickpay.net/payments',
req: [ClientRequest],
ntick: true,
response: [Circular],
originalHost: 'api.quickpay.net',
originalHostHeaderName: 'host',
responseContent: [Circular],
_destdata: true,
_ended: true,
_callbackCalled: true },
toJSON: [Function: responseToJSON],
caseless: Caseless { dict: [Object] },
body: '404 Not Found' } }
What is wrong here? The path to the ressource is checked many times - nothing is wrong there....
For api.quickpay.net, 404 Not Found does not mean the URI is not recognized, but indicating invalid request body. It has nothing to do with whether the URI is stated in options object, or stated as string parameter of request().
Here is a simple experiment. The code below would return "positive" result, warning missing headers ({"error":"Accept-Version http header is required"}), which indicates that the URI is "recognized":
request({
method: 'POST',
uri: 'https://api.quickpay.net/payments'
}, function(err, res, body) {
console.log(body);
});
However, after the missing Accept-Version header is added, we get 404 Not Found:
request({
method: 'POST',
uri: 'https://api.quickpay.net/payments',
headers: {
'Accept-Version': 'v10'
}
}, function(err, res, body) {
console.log(body);
});
Thus, in order to make the API call work, you need to make the HTTP request valid (following to the document).
I was trying to use the thumbnail code from the google cloud functions example.
However, I keep getting the following as a response.
{ code: 1, message: '`convert /tmp/users/ZSE7ZRkFGKZq0Bc3tESEG2uT8922/images/1489813436054.jpg -thumbnail 500x500> -limit memory 32MB /tmp/users/ZSE7ZRkFGKZq0Bc3tESEG2uT8922/images/thumbnail/thumb_1489813436054.jpg` failed with code 1',
childProcess:
ChildProcess {
domain: null,
_events: { error: [Function: t], close: [Function] },
_eventsCount: 2,
_maxListeners: undefined,
_closesNeeded: 3,
_closesGot: 3,
connected: false,
signalCode: null,
exitCode: 1,
killed: false,
spawnfile: 'convert',
_handle: null,
spawnargs:
[ 'convert',
'/tmp/users/ZSE7ZRkFGKZq0Bc3tESEG2uT8922/images/1489813436054.jpg',
'-thumbnail',
'500x500>',
'-limit',
'memory 32MB',
'/tmp/users/ZSE7ZRkFGKZq0Bc3tESEG2uT8922/images/thumbnail/thumb_1489813436054.jpg' ],
pid: 14,
stdin:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: null,
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 2,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
destroyed: true,
_bytesDispatched: 0,
_sockname: null,
_writev: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
write: [Function: writeAfterFIN],
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1 },
stdout:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: null,
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
destroyed: true,
_bytesDispatched: 0,
_sockname: null,
_writev: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
write: [Function: writeAfterFIN] },
stderr:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: null,
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
destroyed: true,
_bytesDispatched: 0,
_sockname: null,
_writev: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
read: [Function],
_consuming: true,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
write: [Function: writeAfterFIN] },
stdio: [ [Object], [Object], [Object] ] },
toString: [Function: toString] }
My code...
'use strict';
const functions = require('firebase-functions');
const mkdirp = require('mkdirp-promise');
const gcs = require('#google-cloud/storage')();
const spawn = require('child-process-promise').spawn;
const LOCAL_TMP_FOLDER = '/tmp/';
// Max height and width of the thumbnail in pixels.
const THUMB_MAX_HEIGHT = 500;
const THUMB_MAX_WIDTH = 500;
// Thumbnail prefix added to file names.
const THUMB_PREFIX = 'thumb_';
const uploadDir = 'thumbnail/'; // Not a great var name...
/**
* When an image is uploaded in the Storage bucket We generate a thumbnail automatically using
* ImageMagick.
*/
exports.generateThumbnail = functions.storage.object().onChange(event => {
const filePath = event.data.name;
const filePathSplit = filePath.split('/');
const fileName = filePathSplit.pop();
const fileDir = filePathSplit.join('/') + (filePathSplit.length > 0 ? '/' : '');
const thumbFilePath = `${fileDir}${uploadDir}${THUMB_PREFIX}${fileName}`;
const tempLocalDir = `${LOCAL_TMP_FOLDER}${fileDir}`;
const tempLocalFile = `${tempLocalDir}${fileName}`;
const tempLocalThumbFile = `${LOCAL_TMP_FOLDER}${thumbFilePath}`;
// Exit if this is triggered on a file that is not an image.
if (!event.data.contentType.startsWith('image/')) {
console.log('This is not an image.');
return;
}
// Exit if the image is already a thumbnail.
if (fileName.startsWith(THUMB_PREFIX)) {
console.log('Already a Thumbnail.');
return;
}
// Exit if this is a move or deletion event.
if (event.data.resourceState === 'not_exists') {
console.log('This is a deletion event.');
return;
}
// Create the temp directory where the storage file will be downloaded.
return mkdirp(tempLocalDir).then(() => {
// Download file from bucket.
const bucket = gcs.bucket(event.data.bucket);
return bucket.file(filePath).download({
destination: tempLocalFile
}).then(() => {
console.log('The file has been downloaded to', tempLocalFile);
// Generate a thumbnail using ImageMagick.
return spawn('convert', [tempLocalFile, '-thumbnail', `${THUMB_MAX_WIDTH}x${THUMB_MAX_HEIGHT}>`, '-limit', 'memory 32MB', '-debug', 'all', tempLocalThumbFile]).then(() => {
console.log('Thumbnail created at', tempLocalThumbFile);
// Uploading the Thumbnail.
return bucket.upload(tempLocalThumbFile, {
destination: thumbFilePath
}).then(() => {
console.log('Thumbnail uploaded to Storage at', thumbFilePath);
});
});
});
});
});
I've tried a few different configurations, but nothing seems to work. Kind of at a loss here.
Image:
Apart from increasing your memory limit on the cloud function, you should also delete temporary files before your cloud function finishes. Please watch this recent official Firebase video on the subject: https://www.youtube.com/watch?v=2mjfI0FYP7Y
Files that are written to the temp file location is counted towards the memory usage of your cloud function.
The temporary files are not automatically cleaned up after a function finishes. The files will remain for as long as the server instance is around. So a cloud function that is frequently triggered can cause your server instance to run out of memory regardless of how much memory you allocated in your configuration.
At the top of your file, add a requirement for node filesystem module
const fs = require('fs');
And then in after you have done the file upload, you delete the local file.
return bucket.upload(tempLocalThumbFile, { destination: thumbFilePath })
.then(() => {
console.log('Thumbnail uploaded to Storage at', thumbFilePath);
fs.unlinkSync(tempLocalThumbFile);
});