I'd like to display Google Place Photos in my NextJS application. To GET these images from their specified URL an API key is needed, but at the same time, I do not want to expose this API key to the public.
My goal is to implement a NextJS API route that fetches and returns a specified image from Google Places Photos, while also being able to be accessed directly from an image tag like so:
<img src={`/api/photos/${place?.photos[0].photo_reference}`} alt='' />
I found a few different sources online that recommended I pipe the response stream from my google request directly to the response stream of the outgoing response like so:
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const response = await fetch(
`https://maps.googleapis.com/maps/api/place/photo
&photo_reference=${id}
&key=${process.env.GOOGLE_PLACE_API_KEY}`,
);
if (!response.ok) {
console.log(response);
res.status(500).end();
return;
}
response.body.pipe(res);
}
however as response.body is a ReadableStream, it does not have a .pipe() function. Instead it has .pipeTo() and .pipeThrough().
I then tried
response.body.pipeTo(res);
however, this also fails to work because res is a NextApiResponse rather than a WritableStream. Although I searched online, I haven't found out how to write to NextApiResponse similarly to WritableStreams.
Finally, I tried manually converting the response into a buffer and writing it to the NextApiResponse like so:
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const response = await fetch(
`https://maps.googleapis.com/maps/api/place/photo
&photo_reference=${id}
&key=${process.env.GOOGLE_PLACE_API_KEY}`,
);
if (!response.ok) {
console.log(response);
res.status(500).end();
return;
}
const resBlob = await response.blob();
const resBufferArray = await resBlob.arrayBuffer();
const resBuffer = Buffer.from(resBufferArray);
const fileType = await fileTypeFromBuffer(resBuffer);
res.setHeader('Content-Type', fileType?.mime ?? 'application/octet-stream');
res.setHeader('Content-Length', resBuffer.length);
res.write(resBuffer, 'binary');
res.end();
}
and while this completes the response, no image is displayed.
How can I directly pass the retrieved google places image from the server to the frontend so that it can be used by a tag?
What worked for me was to simply cast the response.body
of type ReadableStream<Uint8Array>
to a NodeJS.ReadableStream
and then use the pipe
function.
const readableStream = response.body as unknown as NodeJS.ReadableStream;
readableStream.pipe(res);