I have a VueJS-3 application, using Quasar Framework with Server Side Rendering (SSR). The Frontend makes API-requests to my SSR layer and the SSR-Layer is forwarding any request to the “real” backend via API.
Every 10th ’til 20th request to my application, the full application is crashing (and intentionally automatically getting restarted by Docker). It wouldn’t happen when running the App as SPA. It only happens with enabled SSR-stack.
Having a hard time of several weeks and even months to figure out, why this happens, I came up with putting everything inside the SSR middleware into try
–catch
-blocks to log any potential error. However, the application is still crashing.
The only log I can find on the server is then always this:
myApp | /app/server/server-entry.js:85
myApp | `}};function K(t,n,o,r=""){function i(d,c){const s=d[0],l=d.slice(1),u=c[s];return typeof u=="string"?l.length===0?u:"":u&&typeof u=="object"?i(l,u):""}try{const d=[n,"de","en","es"];for(const s of d){const l=i(t.split("."),Vr[s]);if(l)return l}let c=t.replaceAll(o,"");switch(r){case"capitalize":c=c.charAt(0).toUpperCase()+c.slice(1);break}return c}catch{return t}}const dt=e.defineComponent({__name:"ToolBarItem",__ssrInlineRender:!0,props:{category:{type:String,required:!0},section:{type:String,default:"home"},css:{type:String,default:"hidden-on-mobile"}},setup(t){const n=t,o=H.useQuasar(),r=ne(),i=Ne(),d=le(),{language:c}=F.storeToRefs(r),{isLoading:s}=F.storeToRefs(i),{filter:l}=F.storeToRefs(d),u=e.computed(()=>l.value.category);e.watch(s,(f,p)=>{f||o.loading.hide()});const m=e.computed(()=>{const f=q[n.category],p=c.value;if(!f)return n.category;switch(p){case"de":return f.de;case"en":return f.en;case"es":return f.es;default:return f.en}});return(f,p,v,g)=>{const x=e.resolveComponent("q-btn");p(a.ssrRenderComponent(x,e.mergeProps({to:{name:t.section+":lang:category",params:{lang:e.unref(c),category:t.category}},stretch:"",flat:"",label:(u.value===t.category?"u27A4 ":"")+m.value,class:t.css+" "+(u.value===t.category?"current":"")},g),null,v))}}}),Un=dt.setup;dt.setup=(t,n)
It is not helpful to look into line 85 of /app/server/server-entry.js
, as this is the full sourcecode combined by the build-process.
Locally, in Dev-Mode, this issue never happens. Only in production build. I can’t reproduce this. But thanks to the traffic on production, I spot this error multiple times a minute.
I encapsulated my middleware into try-catch
:
export default ssrMiddleware(
async ({ app, resolve /*, resolveUrlPath, publicPath, render */ }) => {
app.use(bodyParser.json());
app.all(resolve.urlPath('*'), async (req, res, next) => {
try {
if (req.url.substring(0, 7) === '/health') {
res.status(200).json({ status: 'ok' });
}
//console.log(':__: ', req.url);
else if (req.url.substring(0, 3) === '/d/') {
await makeDataRequest(res, req, next);
} else if (req.url.substring(0, 3) === '/a/') {
await makeAnalyticsRequest(res, req);
} else if (req.url.substring(0, 4) === '/api') {
await makeApiRequest(res, req, next);
} else {
next();
}
}catch(err: any) {
console.log('Error in API middleware', typeof err?.message !== 'undefined' ? err.message : 'unspecified error in middleware');
next();
}
});
}
);
Since I can’t find out, which of the requests is making trouble, I have to consider all methods inside the Middleware. However, the error must be so hard that NodeJS is crashing.
Here are my middlewares that will be executed during the block above:
async function makeDataRequest(res: any, req: any, next: any) {
const url = req.url.substring(3);
const what = url.split('/')[0];
let ttl = TTL_DEFAULT;
if (what !== 'today') {
ttl = TTL_ARTICLE;
}
// console.log('DATA FETCH: ', ttl, url);
const cacheKey = 'auto:' + url;
const cached = await cache.get<CacheItem>(cacheKey, ttl);
if (cached) {
//console.log('Cache Hit for ' + req.url);
res.status(cached.code).send(cached.content);
return;
}
try {
console.log('[API] URL: ' + url);
let data = await api.get(url);
let result = data.data;
if (Array.isArray(result)) {
switch (what) {
case 'today':
try {
result = await transformDailyData(result);
} catch (error) {
console.error('Error transforming daily data', error);
}
break;
default:
result = data.data;
}
}
const cacheItem = {
code: 200,
content: result,
};
await cache.set<CacheItem>(cacheKey, cacheItem, ttl);
//console.log('******* RESULT', result);
res.status(200).send(result);
} catch (error: any) {
console.log("** API Error caught");
handleAxiosError(error, res);
next();
}
}
async function makeAnalyticsRequest(res: any, req: any) {
const url = 'analytics/push';
try {
const _res = await api.post(url, req.body);
if (_res.data) {
res.status(201).send(_res.data);
} else {
res.send('OK');
}
} catch (error: any) {
handleAxiosError(error, res);
}
}
async function makeApiRequest(res: any, req: any, next: any) {
let url = req.url;
const method = (req.method as string)?.toLowerCase();
let invalidate = false;
console.log('API: ' + url);
let ttl = url.substring(5).split('/')[0];
let ttlLen = 0;
if (ttl.startsWith('ttl:')) {
ttlLen = ttl.length;
ttl = ttl.substring(4);
//console.log('TTL 2: ' + ttl);
//console.log('URL 2: ' + url);
if (ttl.substring(0, 4) === 'inv:') {
ttl = ttl.substring(4);
console.log('TTL: ' + ttl);
invalidate = true;
}
url = url.substring(5 + ttlLen + 1);
if (invalidate) {
console.log('Invalidate cache for ' + url);
} else {
//console.log("URL: '" + url + "'");
}
} else {
url = url.substring(5);
}
try {
if (!invalidate && ttl !== '0' && method?.toUpperCase() === 'GET') {
const cached = await cache.get<CacheItem>('auto:' + url, parseInt(ttl));
if (cached) {
//console.log('Cache Hit for ' + req.url);
res.status(cached.code).send(cached.content);
return;
}
}
//process.env.VUE_APP_LOG_API && console.log('** AXIOS ', method, url);
const data =
method === 'post' ? await api.post(url, req.body) : await api.get(url);
method?.toUpperCase() === 'GET' &&
(await cache.set(
'auto:' + url,
{
code: 200,
content: data.data,
},
parseInt(ttl)
));
res.status(200).send(data.data);
} catch (error: any) {
handleAxiosError(error, res);
next();
}
}
function handleAxiosError(error: any, res: any) {
console.error('Error on API', Object.keys(error));
if (typeof error.response === 'object') {
console.log('Error response-object found', Object.keys(error.response));
if (typeof error.response.data === 'object') {
console.log('Error response-object.data found', error.response.data);
console.log('- Status: ', error.response.status);
console.log('- Msg: ', error.response.data.message);
res.status(400).json({
code: error.response.status,
status: 'error',
message: error.response.data.message,
});
/*res.status(error.response.status).json({
status: 'error',
message: error.response.data.message,
});*/
return;
}
res.status(error.response.status).send(error.response.statusText);
} else {
console.log("Don't know this kind of error");
// res.status(400).send(error.message);
res.status(200).json({
code: 400,
status: 'error',
message: 'unknown error',
});
}
}
Is there any idea that could help me to narrow it down?