Closed sc0rp10n-py closed 1 month ago
I haven't taken a look at the source code so this might be wrong, but this probably happens because qr-code-styling requires the Window object (aka, self) to be defined, which you don't have on the backend. So you can't really use this library in a NodeJS environment.
This is also why issues such as #89 happen on the frontend as well when using NextJS, because the Window object is not defined during SSR.
@josipslavic so is there any workaround for node js backend type environment?
also interested in this!
Maybe JSDom could help you guys here: https://github.com/jsdom/jsdom
Disclaimer, I haven't tried it with this project but have used it with success before when needing to use other DOM-releated tools inside of Node.js.
You can probably use it to polyfill window, etc. as you need.
If this project requires a true DOM canvas, however, your only options may be to:
By far the easiest approach would be to run it on the client.
Getting the same error on Nuxt after upgrading it to version 3.4.2. Before updates it worked fine
I have done some playing around and came up with a band-aid solution for this problem (largely due to @jzombie and his jsdom proposition).
Here are a few things to note about this:
new Image()
, and because of that, you won't be able to use the image
property when creating your QR code (because it uses new Image()
under the hood). If somebody could manage to find a way to polyfill that, we could drastically improve the performance of this code because we wouldn't need to use nodeHtmlToImage
package (which when I was testing caused huge performance issues). We would also then be able to use qrCode.getRawData('png')
(which we can't currently use because it also uses new Image()
under the hood)import cloudinary from 'cloudinary';
import streamifier from 'streamifier';
import QRCodeStyling from 'qr-code-styling';
import { JSDOM } from 'jsdom';
import nodeHtmlToImage from 'node-html-to-image';
cloudinary.v2.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
global.document = dom.window.document;
global.self = document.defaultView;
global.XMLSerializer = dom.window.XMLSerializer;
const handler = async (req, res) => {
if (req.method === 'POST') {
// Use dynamic imports so that qr-code-styling has access to our global polyfills
import('qr-code-styling').then(async ({ default: QRCodeStyling }) => {
const { id } = req.body;
try {
const qrCode = new QRCodeStyling({
width: 300,
height: 300,
type: 'svg', // We have to create an svg that we could append to an HTML element
data: process.env.NEXT_PUBLIC_AUTH_URL + 'view/' + id,
// image: '/logo.png', Cannot set image due to Image being undefined (qr-code-styling uses new Image() under the hood)
margin: 10,
qrOptions: {
typeNumber: 0,
mode: 'Byte',
errorCorrectionLevel: 'Q',
},
imageOptions: {
hideBackgroundDots: true,
imageSize: 0.4,
margin: 0,
crossOrigin: 'anonymous',
},
dotsOptions: {
color: '#1f2791',
type: 'rounded',
},
backgroundOptions: {
color: '#ffffff',
},
cornersSquareOptions: {
color: '#f22d4e',
type: 'extra-rounded',
},
cornersDotOptions: {
color: '#f22d4e',
type: 'dot',
},
});
// Append the QR code to some HTML element
const htmlEl = dom.window.document.createElement('div');
qrCode.append(htmlEl);
// Convert that HTML to a Buffer
const buffer = await nodeHtmlToImage({
html: htmlEl.innerHTML,
});
// Upload buffer to cloudinary
let cld_upload_stream = cloudinary.v2.uploader.upload_stream(
{ folder: 'some_folder' },
function (error, result) {
if (!error) {
return res
.status(200)
.json({ success: true, url: result.secure_url });
}
}
);
streamifier.createReadStream(buffer).pipe(cld_upload_stream);
} catch (error) {
console.log(error);
res.status(400).json({
success: false,
message: 'Something went wrong!',
});
}
});
} else {
res.status(400).json({ success: false, message: 'Invalid request' });
}
};
export default handler;
Getting the same error on Nuxt after upgrading it to version 3.4.2. Before updates it worked fine
Solved using dynamic import on client-side
onMounted(() => {
import('qr-code-styling').then(({ default: QRCodeStyling }) => {
qrCode = new QRCodeStyling(options);
qrCode.append(qr.value);
});
});
Well for Next.js at least, there's a way to bypass this using Dynamic imports :
const QrCode = dynamic(() => import('@/path/to/qrCode'), {
ssr: false,
});
@josipslavic I used this fork https://github.com/Loskir/styled-qr-code and it works fine, this is my code, i am using NextJS 13.4.4
My repo: https://github.com/cresenciof/nextjs-qr-code-generator
// https://nextjs.org/docs/app/building-your-application/routing/router-handlers
import { NextResponse } from "next/server";
import { QRCodeCanvas, Options } from "@loskir/styled-qr-code-node";
const options: Partial<Options> = {
width: 400,
height: 400,
data: "https://www.facebook.com/",
image:
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/Facebook_Logo_%282019%29.png/1200px-Facebook_Logo_%282019%29.png",
dotsOptions: {
color: "#4267b2",
type: "rounded",
},
backgroundOptions: {
color: "#e9ebee",
},
imageOptions: {
crossOrigin: "anonymous",
margin: 20,
},
};
export async function GET() {
try {
const qrCode = new QRCodeCanvas(options);
const file = await qrCode.toDataUrl("png");
return NextResponse.json({
pnfFile: file,
});
} catch (error) {
return NextResponse.json(
{ error: "Failed to generate QR code" },
{ status: 500 }
);
}
}
This is the component where I am rendering the API response:
"use client";
import { useState, useEffect } from "react";
function ImageViewer() {
const [imageSrc, setImageSrc] = useState<string | null>(null);
useEffect(() => {
fetch("/api/generate")
.then((response) => response.json())
.then((data) => {
if (data.pngFile) {
// the pngFile contains the png image as a base64 string
setImageSrc(data.pngFile);
}
})
.catch((error) => {
console.error("Error fetching image:", error);
});
}, []);
if (!imageSrc) {
return <div>Loading...</div>;
}
return <>{imageSrc && <img src={imageSrc} alt="SVG Image" />}</>;
}
export default ImageViewer;
@josipslavic I used this fork https://github.com/Loskir/styled-qr-code and it works fine, this is my code, i am using NextJS 13.4.4
My repo: https://github.com/cresenciof/nextjs-qr-code-generator
// https://nextjs.org/docs/app/building-your-application/routing/router-handlers import { NextResponse } from "next/server"; import { QRCodeCanvas, Options } from "@loskir/styled-qr-code-node"; const options: Partial<Options> = { width: 400, height: 400, data: "https://www.facebook.com/", image: "https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/Facebook_Logo_%282019%29.png/1200px-Facebook_Logo_%282019%29.png", dotsOptions: { color: "#4267b2", type: "rounded", }, backgroundOptions: { color: "#e9ebee", }, imageOptions: { crossOrigin: "anonymous", margin: 20, }, }; export async function GET() { try { const qrCode = new QRCodeCanvas(options); const file = await qrCode.toDataUrl("png"); return NextResponse.json({ pnfFile: file, }); } catch (error) { return NextResponse.json( { error: "Failed to generate QR code" }, { status: 500 } ); } }
This is the component where I am rendering the API response:
"use client"; import { useState, useEffect } from "react"; function ImageViewer() { const [imageSrc, setImageSrc] = useState<string | null>(null); useEffect(() => { fetch("/api/generate") .then((response) => response.json()) .then((data) => { if (data.pngFile) { // the pngFile contains the png image as a base64 string setImageSrc(data.pngFile); } }) .catch((error) => { console.error("Error fetching image:", error); }); }, []); if (!imageSrc) { return <div>Loading...</div>; } return <>{imageSrc && <img src={imageSrc} alt="SVG Image" />}</>; } export default ImageViewer;
Thanks
this is my code
i get this error