NextJs and Socket IO is not connecting - next.js

I am trying to create a SocketIO solution with NextJS as below:
folder: api/socket.js
import { Server } from "socket.io";
const ioHandler = (req, res) => {
if (!res.socket.server.io) {
console.log("*First use, starting socket.io");
const io = new Server(res.socket.server);
io.on("connection", (socket) => {
console.log("Connected socket.io");
socket.broadcast.emit("a user connected");
socket.on("hello", (msg) => {
socket.emit("hello", "world!");
});
});
res.socket.server.io = io;
} else {
console.log("socket.io already running");
}
res.end();
};
export const config = {
api: {
bodyParser: false,
},
};
export default ioHandler;
folder: api/test.js
import io from "socket.io-client";
fetch("http://localhost:3000/api/socket");
const socket = io();
console.log(socket.connection);
socket.on("connect", () => {
console.log("connected");
});
But when I run api/test.js the SocketIO is not connected as below:
Socket {
connected: false,
receiveBuffer: [],
sendBuffer: [],
ids: 0,
acks: {},
flags: {},
io: Manager {
nsps: { '//undefined//undefined': [Circular *1] },
subs: [
[Function: subDestroy],
[Function: subDestroy],
[Function: subDestroy]
],
opts: {
path: '/socket.io',
hostname: 'undefined',
secure: false,
port: '80'
},
setTimeoutFn: [Function: bound setTimeout],
clearTimeoutFn: [Function: bound clearTimeout],
_reconnection: true,
_reconnectionAttempts: Infinity,
_reconnectionDelay: 1000,
_reconnectionDelayMax: 5000,
_randomizationFactor: 0.5,
backoff: Backoff {
ms: 1000,
max: 5000,
factor: 2,
jitter: 0.5,
attempts: 0
},
_timeout: 20000,
_readyState: 'opening',
uri: 'undefined//undefined//undefined',
encoder: Encoder { replacer: undefined },
decoder: Decoder { reviver: undefined },
_autoConnect: true,
engine: Socket {
setTimeoutFn: [Function: bound setTimeout],
clearTimeoutFn: [Function: bound clearTimeout],
secure: false,
hostname: 'undefined',
port: '80',
transports: [Array],
readyState: 'opening',
writeBuffer: [],
prevBufferLen: 0,
opts: [Object],
id: null,
upgrades: null,
pingInterval: null,
pingTimeout: null,
pingTimeoutTimer: null,
transport: [Polling],
_callbacks: [Object]
},
skipReconnect: false,
_callbacks: {
'$open': [Array],
'$packet': [Array],
'$error': [Array],
'$close': [Array]
}
},
nsp: '//undefined//undefined',
subs: [
[Function: subDestroy],
[Function: subDestroy],
[Function: subDestroy],
[Function: subDestroy]
]
}
Any suggestions?

According to documentation socket server options here socket.io v4
you need to specify a path same as client path
in your case
const io = new Server(res.socket.server, {path:'/api/socket'});

Related

AxiosError: connect ECONNREFUSED 127.0.0.1:3000

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..

Supabase JS reponse no data property

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);
}

Next JS : Failed to collect page data for when build on VPS

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?

vite config proxy config don't rewrite path

I have a cors issue in my development with vue3 & vite, so I create a proxy config in my vite.config.js
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'#': resolve(__dirname, 'src'),
},
},
server: {
proxy: {
"/api": {
target: "https://###.com",
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api/, ""),
},
}
}
})
when I use in my app my method:
createPayment() {
const url = "/api/webhook/###";
let formData = new FormData();
formData.append("firstName", this.first_name);
formData.append("lastName", this.last_name);
formData.append("email", this.email);
formData.append("phone", this.phone);
formData.append("cardNumber", this.c_number);
formData.append("cardExpiration", this.c_EXP);
formData.append("cardCCV", this.c_CVC);
const request = new Request(url, {
method: "POST",
body: formData,
headers: {
accept: 'application/json',
contentType: "application/json;charset=UTF-8",
AccessControlAllowOrigin: '*'
},
});
fetch(request)
.then(result => console.log(result))
.catch(error => console.log('error', error));
},
The Post all ways send from localhost, I can't understand why?
I make the config for change '/api' to 'https://###.com' ?
My Log:
Response {type: 'basic', url: 'http://localhost:3000/api/webhook/####', redirected: false, status: 404, ok: false, …}

Function killed. Error: memory limit exceeded [Thumbnail example]

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);
});

Resources