N
Nuxtβ€’5mo ago
Jelmer

Nitro server plugin to cache Nuxt images: how to respond with a file from disk?

Hi, I am writing a server plugin like below. I am using the @nuxt/image module with the ipx provider. I would like it to save its generated image files to disk, and serve those files whenever the exact same file is requested. This example below works. I get served files from disk. But I am using a new Response and doing event.respondWith(res); which keeps resulting in this error in the CLI:
[nuxt] [request error] [unhandled] [500] Cannot set headers after they are sent to the client
at ServerResponse.setHeader (node:_http_outgoing:659:11)
at setResponseHeaders (./node_modules/h3/dist/index.mjs:778:20)
[nuxt] [request error] [unhandled] [500] Cannot set headers after they are sent to the client
at ServerResponse.setHeader (node:_http_outgoing:659:11)
at setResponseHeaders (./node_modules/h3/dist/index.mjs:778:20)
My knowledge of Nitro is limited; if I were to create a plugin like this, is this the correct way? How can I respond with the image file inside the request hook without getting [500] Cannot set headers after they are sent to the client ? Should I use another method to create this functionality? Help is greatly appreciated πŸ™‚
// server/plugins/cache.ts
export default defineNitroPlugin(async (nitroApp) => {
await mkdir(cacheFolder, { recursive: true });

nitroApp.hooks.hook('request', async (event) => {
const { path } = event;

if (!path.startsWith('/_ipx/')) return;

const targetCacheFilePath = getCacheKey(path);
const exists = existsSync(targetCacheFilePath);

if (exists) {
const file = await readFile(targetCacheFilePath);
const res = new Response(file, {
status: 200,
headers: {
'X-Nuxt-Image-Cache': 'HIT',
},
});

await event.respondWith(res);
console.log('Image served from cache:', targetCacheFilePath);
return;
}
});

nitroApp.hooks.hook('afterResponse', async (event, response) => {
// save generated image file to disk, not important right now
});
});
// server/plugins/cache.ts
export default defineNitroPlugin(async (nitroApp) => {
await mkdir(cacheFolder, { recursive: true });

nitroApp.hooks.hook('request', async (event) => {
const { path } = event;

if (!path.startsWith('/_ipx/')) return;

const targetCacheFilePath = getCacheKey(path);
const exists = existsSync(targetCacheFilePath);

if (exists) {
const file = await readFile(targetCacheFilePath);
const res = new Response(file, {
status: 200,
headers: {
'X-Nuxt-Image-Cache': 'HIT',
},
});

await event.respondWith(res);
console.log('Image served from cache:', targetCacheFilePath);
return;
}
});

nitroApp.hooks.hook('afterResponse', async (event, response) => {
// save generated image file to disk, not important right now
});
});
1 Reply
Jelmer
JelmerOPβ€’5mo ago
Complete error:
ERROR [nuxt] [request error] [unhandled] [500] Cannot set headers after they are sent to the client
at ServerResponse.setHeader (node:_http_outgoing:659:11)
at setResponseHeaders (./node_modules/h3/dist/index.mjs:778:20)
at Object.handler (./node_modules/nitropack/dist/runtime/route-rules.mjs:19:7)
at ./node_modules/h3/dist/index.mjs:1963:31
at async Object.callAsync (./node_modules/unctx/dist/index.mjs:72:16)
at async Server.toNodeHandle (./node_modules/h3/dist/index.mjs:2250:7)
ERROR [nuxt] [request error] [unhandled] [500] Cannot set headers after they are sent to the client
at ServerResponse.setHeader (node:_http_outgoing:659:11)
at setResponseHeaders (./node_modules/h3/dist/index.mjs:778:20)
at Object.handler (./node_modules/nitropack/dist/runtime/route-rules.mjs:19:7)
at ./node_modules/h3/dist/index.mjs:1963:31
at async Object.callAsync (./node_modules/unctx/dist/index.mjs:72:16)
at async Server.toNodeHandle (./node_modules/h3/dist/index.mjs:2250:7)
Should I be ending the original response somehow? Should I not use event.respondWith()?
Want results from more Discord servers?
Add your server