1

I am trying to implement the library @google-cloud/storage on a Nuxt 3 app. It works correctly locally. It does not work on Vercel, and it seems like it's related to the environment, as I'm getting an error about the "uuid" module being missing, which is why I tried making it a dependency and importing it into the file where the endpoint is defined—no change in error. Changing the import of @google-cloud/storage to a require does not work, as Vercel throws a different error saying require is not defined.

How do I get Google Cloud Storage to work properly in Nuxt 3 API endpoints on Vercel? Please note that I am not referring to Next, but Nuxt 3, the Vite-building-Vue framework powered by Nitro, which itself uses h3. Nuxt 3 deploys API endpoints as either a server or serverless functions. There may be details related to this, or configuration needed, but I couldn't find anything on Vercel about making sure a library that you import into a serverless function has access to the modules it requires.

File: /server/api/pics/[bucket-name].get.ts

import { H3Event } from "h3";
import "uuid";
import {
  GetFilesResponse,
  GetSignedUrlConfig,
  GetSignedUrlResponse,
  Storage,
} from "@google-cloud/storage";
import { bucketScope } from "@/utils/googleStorage";

import { reqHasScope } from "@/utils/authUtils";

// export type BucketScopes = {
//   [key: string]: string;
// };

const makeConfig = function (): GetSignedUrlConfig {
  return {
    action: "read",
    expires: Date.now() + 604800,
    version: "v4",
  };
};

// Gets signed file URLs for temporary access to bucket images
export default defineEventHandler(async (event: H3Event) => {
  const bucketName: string = event.context.params.bucket;
  if (!bucketName) {
    return [];
  }

  const requiredScope = bucketScope(bucketName);
  if (!requiredScope) {
    return [];
  }
  const runtimeConfig = useRuntimeConfig();
  const privKey = runtimeConfig.gcpPrivateKey;
  const clientEmail = runtimeConfig.gcpClientEmail;
  const storage: Storage = new Storage({
    projectId: runtimeConfig.public.gcpProjectId,
    credentials: {
      type: "service_account",
      private_key: privKey.split(String.raw`\n`).join("\n"),
      client_email: clientEmail,
    },
  });
  if ((await reqHasScope(event, requiredScope)) === true) {
    const fileListResponse: GetFilesResponse = await storage
      .bucket(bucketName)
      .getFiles();
    const signedURLs: Array<Promise<GetSignedUrlResponse>> = [];

    const fileList = fileListResponse[0];
    fileList.reduce((urlList, file) => {
      const signedURL = file.getSignedUrl(makeConfig());
      urlList.push(signedURL);
      return urlList;
    }, signedURLs);

    return await Promise.all(signedURLs).then((urlList) =>
      urlList.map((res) => res[0])
    );
  } else {
    return [];
  }
});

Helper file: /utils/googleStorage.ts

export const bucketScope = function (bucketName: string): string {
  if (bucketName === "homepage-gallery") {
    return "read:content";
  }
  return "";
};

reqHasScope works correctly.

Vercel Logs Error stack

When navigating to API endpoint in browser: 500 error

I expected the code to work the same locally and in deployment, like my other API endpoints do, including async event handlers, calls to Nuxt's runtimeConfig, and uses of reqHasScope middleware which successfully imports the jose library.

2
  • Try removing import "uuid"; from your file if you are not using it? Commented Mar 11, 2023 at 17:42
  • That's what it was to begin with, same error. Commented Mar 15, 2023 at 16:37

1 Answer 1

1

I got it working, and the only things I changed narrow it down to either a problem with my package-lock.json, my npm version, or a problem with Nuxt 3.1.0. I deleted my package-lock.json, upgraded npm, upgraded Nuxt to 3.3.1, and pushed the new package and package-lock to origin. After the changes reached Vercel my app was working the way I expected.

It looks like some of the latest releases of Nuxt do indeed fix problems with certain layers being omitted incorrectly, so I think this was a recently fixed Nuxt bug.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.