Building a File Upload API with Next.js and Google Drive Integration
Tags: googlecloud, nextjs, api
Published: 2023-12-27 07:17:56

In this tutorial, we'll walk through the process of creating a serverless API using Next.js that allows users to upload files directly to Google Drive. We'll use the Google Drive API for file storage and Next.js for the serverless API endpoints.

## Prerequisites

Before getting started, make sure you have the following:

1. - A Google Cloud Platform (GCP) project with the [Google Drive API enabled.](https://youtu.be/bkaQTLCBBeo)
2. - API credentials in the form of a service account key (JSON file).
3. - Node.js and npm installed on your development machine.

## Setting Up the Project

1. Initialize a Next.js Project:

```
npx create-next-app my-drive-api
cd my-drive-api
```
2. Install Dependencies:

```
npm install googleapis multer stream
```
3. Api Code

```
// pages/api/googleDrive.js
import { google } from "googleapis";
const stream = require("stream");
import multer from "multer";
const apikeys = require("./apikeys.json");
const SCOPE = ["https://www.googleapis.com/auth/drive"];

export const config = {
  api: {
    bodyParser: false,
  },
};

// Function to authorize the Google Drive API
async function authorize() {
  const jwtClient = new google.auth.JWT(
    apikeys.client_email,
    null,
    apikeys.private_key,
    SCOPE
  );

  await jwtClient.authorize();

  return jwtClient;
}

// Multer storage configuration
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });

// API endpoint handler
export default async function handler(req: any, res: any) {
  if (req.method !== "POST") {
    return res.status(405).end(); // Method Not Allowed
  }

  try {
    await new Promise<void>((resolve, reject) => {
      upload.single("image")(req as any, res as any, (err) => {
        if (err) {
          return reject(err); // Reject if multer encounters an error
        }
        resolve();
      });
    });

    const uploadedFile = req.file;

    if (!uploadedFile) {
      return res.status(400).json({ error: "No file uploaded" });
    }

    const authClient = await authorize();
    const result: any = await uploadFile(authClient, uploadedFile);

    // Wait for a short delay (e.g., 2 seconds) before responding
    await new Promise((resolve) => setTimeout(resolve, 2000));

    const previewLink = `https://drive.google.com/uc?id=${result?.data?.id}`;

    res.status(200).json({ previewLink, id: result?.data?.id });
  } catch (error: any) {
    console.error(error);
    res.status(500).json({ error: error?.message ?? "Internal Server Error" });
  }
}

// Function to upload a file to Google Drive
async function uploadFile(authClient: any, uploadedFile: any) {
  return new Promise((resolve, reject) => {
    const drive = google.drive({ version: "v3", auth: authClient });

    const fileMetaData = {
      name: uploadedFile.originalname,
      parents: ["1rE3jBXCsPoABRPCyHhvHBqH9SxzzFzCm"],
    };

    drive.files.create(
      {
        resource: fileMetaData,
        media: {
          mimeType: uploadedFile.mimetype,
          body: stream.PassThrough().end(uploadedFile.buffer),
        },
        fields: "id",
      },
      (error: any, file: any) => {
        if (error) {
          return reject(error); // Reject if there's an error during file creation
        }

        // Set permissions to make the file public
        drive.permissions.create(
          {
            fileId: file.data.id,
            requestBody: {
              role: "reader",
              type: "anyone",
            },
          },
          (err: any) => {
            if (err) {
              console.error(err);
              return reject(err); // Reject if there's an error setting permissions
            }
            resolve(file);
          }
        );
      }
    );
  });
}

```