Skip to content

React Frontend

Setup

  1. Add <script src="https://apis.google.com/js/api.js"></script> to frontend/index.html:

    tsx
    ...
    <div id="root"></div>
    <script src="https://apis.google.com/js/api.js"></script>
    <script type="module" src="/src/main.tsx"></script>
    ...
  2. Install typescript dependencies:

    Each google service has a separate package with the typescript declarations:

    • drive: @types/gapi.client.drive-v3
    • calendar: @types/gapi.client.calendar-v3
    • sheets: @types/gapi.client.sheets-v4
    • gmail: @types/gapi.client.gmail-v1
    • docs: @types/gapi.client.docs-v1

    For example, for drive use

    sh
    npm install --save-dev @types/gapi @types/gapi.client @types/gapi.client.drive-v3

    Add "gapi", "gapi.client" and the service specific packages to the types in frontend/tsconfig.app.json:

    json
    {
       "compilerOptions": {
         "types": [... "gapi", "gapi.client", "@types/gapi.client.drive-v3", ...],
       }
    }

Gapi Integration

  1. Create a WithGapi component:
WithGapi.tsx
tsx
import { fetchWithAuth } from "@lib/fetch/fetchWithAuth";
import { buildQueryString } from "@lib/utils";
import { type ReactNode, useEffect, useState } from "react";

export function WithGapi(props: { children: ReactNode }) {
  const [loaded, setLoaded] = useState<
    { status: "loading" | "loaded" } | { status: "error"; error: string }
  >({
    status: "loading",
  });
  useEffect(() => {
    (async () => {
      try {
        const scopes = [
          "https://www.googleapis.com/auth/calendar.events",
          "https://www.googleapis.com/auth/drive.file",
          "https://www.googleapis.com/auth/spreadsheets",
          "https://www.googleapis.com/auth/gmail.send",
          "https://www.googleapis.com/auth/gmail.readonly",
          "https://www.googleapis.com/auth/documents",
        ].join(" ");
        const apis = [
          { name: "drive", version: "v3" },
          { name: "calendar", version: "v3" },
          { name: "sheets", version: "v4" },
          { name: "gmail", version: "v1" },
          { name: "docs", version: "v1" },
        ];
        const tokenUrl =
          "/api/gw/platform/token/" +
          buildQueryString({
            name: "google",
            scopes: scopes,
          });

        const token = await fetchWithAuth<string>(tokenUrl);

        gapi.load("client", async () => {
          await gapi.client.init({});

          gapi.client.setToken({ access_token: token });

          await Promise.all(
            apis.map((api) => gapi.client.load(api.name, api.version)),
          );
          setLoaded({ status: "loaded" });
        });
      } catch (e) {
        setLoaded({ status: "error", error: "" + e });
      }
    })();
  }, []);

  if (loaded.status === "loaded") return <>{props.children}</>;
  else if (loaded.status === "error")
    return <div> Error loading GAPI: {loaded.error} </div>;
  else return <div> Loading GAPI... </div>;
}

This example can access the Google Drive, Calendar, Sheets, Mail and Docs APIs. The WithGapi component can also be parameterized by modifying the useEffect() function. That way, scopes and clients can be created dynamically.

  1. Wrap your base google component with the WithGapi component in App.tsx.
tsx
return (
  <WithGapi>
    <Container>...</Container>
  </WithGapi>
);

Usage

Now React components can call Google APIs directly. For example:

GoogleCalendarDemo.tsx
tsx
const response = await gapi.client.calendar.events.list({
  calendarId,
  timeMin: now.toISOString(),
  timeMax: max.toISOString(),
  singleEvents: true,
  orderBy: "startTime",
});