嵌入式分析 SDK - 將 SDK 與 Next.js 搭配使用

⚠️ 此功能為 Beta 版。歡迎隨意試用,但請注意,內容可能會變更 (且可能無法如預期般運作)。

嵌入式分析 SDK 僅適用於專業版企業版方案 (包括自架託管和 Metabase 雲端)。不過,您可以在本機電腦上試用 SDK,無需授權,只需使用 API 金鑰驗證您的嵌入。

關於將嵌入式分析 SDK 與 Next.js 搭配使用的一些注意事項。SDK 經測試可在 Next.js 14 上運作,但也可能適用於其他版本。

具有伺服器端渲染 (SSR) 或 React 伺服器元件的 SDK 元件

目前,SDK 元件僅支援用戶端渲染。若要將 SDK 元件與伺服器端渲染或 React 伺服器元件搭配使用,您可以選擇使用相容性層或手動包裝元件。

伺服器端渲染 (SSR) 的相容性層 (實驗性)

若要將 SDK 元件與 Next.js 搭配使用,SDK 提供了一個實驗性相容性層,可使用動態匯入包裝所有元件並停用 SSR。為了與應用程式路由器搭配運作,此相容性層使用 use client

若要使用相容性層,請將您的匯入從 @metabase/embedding-sdk-react 變更為 @metabase/embedding-sdk-react/nextjs

請參閱使用此相容性層的 Next.js 應用程式範例

手動包裝元件

如果您想要自訂元件的載入方式,您可以建立自己的包裝函式。

在您的應用程式中,建立一個 metabase 目錄,並在該目錄中新增一個 EmbeddingSdkProvider.tsx 檔案。此檔案將包含具有適當設定的提供者。

"use client";

import {
  defineMetabaseAuthConfig,
  MetabaseProvider,
} from "@metabase/embedding-sdk-react";

const authConfig = defineMetabaseAuthConfig({
  metabaseInstanceUrl: process.env.NEXT_PUBLIC_METABASE_INSTANCE_URL,
  authProviderUri: process.env.NEXT_PUBLIC_METABASE_AUTH_PROVIDER_URI,
});

export const EmbeddingSdkProvider = ({
  children,
}: {
  children: React.ReactNode;
}) => {
  return (
    <MetabaseProvider authConfig={authConfig}>{children}</MetabaseProvider>
  );
};

接下來,在該 metabase 目錄中新增一個 index.tsx 檔案。此檔案將包含 use client 指令,並且將匯出已停用 SSR 的 EmbeddingSdkProvider 延遲載入版本。

"use client";

import dynamic from "next/dynamic";

import React from "react";

// Lazy load the EmbeddingSdkProvider so and let it render children while it's being loaded
export const EmbeddingSdkProviderLazy = ({
  children,
}: {
  children: React.ReactNode;
}) => {
  const EmbeddingSdkProvider = dynamic(
    () =>
      import("./EmbeddingSdkProvider").then(m => {
        return { default: m.EmbeddingSdkProvider };
      }),
    {
      ssr: false,
      loading: () => {
        // render children while loading
        return <div>{children}</div>;
      },
    },
  );

  return <EmbeddingSdkProvider>{children}</EmbeddingSdkProvider>;
};

// Wrap all components that you need like this:

export const StaticQuestion = dynamic(
  () => import("@metabase/embedding-sdk-react").then(m => m.StaticQuestion),
  {
    ssr: false,
    loading: () => {
      return <div>Loading...</div>;
    },
  },
);

export const StaticDashboard = dynamic(
  () => import("@metabase/embedding-sdk-react").then(m => m.StaticDashboard),
  {
    ssr: false,
    loading: () => {
      return <div>Loading...</div>;
    },
  },
);

您現在可以像這樣匯入元件

import { StaticQuestion } from "@/metabase"; // path to the folder created earlier

export default function Home() {
  return <StaticQuestion questionId={123} />;
}

處理身份驗證

應用程式路由器和頁面路由器定義 API 路由的方式不同。如果您想使用 JWT 從伺服器驗證使用者,您可以按照以下指示操作。但如果您想使用 API 金鑰進行本機開發驗證,請參閱「使用 API 金鑰在本機進行驗證」

使用應用程式路由器

您可以建立一個路由處理程式,讓使用者登入 Metabase。

在您的 app/* 目錄中建立一個新的 route.ts 檔案,例如 app/sso/metabase/route.ts,對應於 /sso/metabase 的端點。

import jwt from "jsonwebtoken";

const METABASE_JWT_SHARED_SECRET = process.env.METABASE_JWT_SHARED_SECRET || "";
const METABASE_INSTANCE_URL = process.env.METABASE_INSTANCE_URL || "";

export async function GET() {
  const token = jwt.sign(
    {
      email: user.email,
      first_name: user.firstName,
      last_name: user.lastName,
      groups: [user.group],
      exp: Math.round(Date.now() / 1000) + 60 * 10, // 10 minutes expiration
    },
    // This is the JWT signing secret in your Metabase JWT authentication setting
    METABASE_JWT_SHARED_SECRET,
  );
  const ssoUrl = `${METABASE_INSTANCE_URL}/auth/sso?token=true&jwt=${token}`;

  try {
    const ssoResponse = await fetch(ssoUrl, { method: "GET" });
    const ssoResponseBody = await ssoResponse.json();

    return Response.json(ssoResponseBody);
  } catch (error) {
    if (error instanceof Error) {
      return Response.json(
        {
          status: "error",
          message: "authentication failed",
          error: error.message,
        },
        {
          status: 401,
        },
      );
    }
  }
}

然後,將此 authConfig 傳遞給 MetabaseProvider

import { defineMetabaseAuthConfig } from "@metabase/embedding-sdk-react";
const authConfig = defineMetabaseAuthConfig({
  metabaseInstanceUrl: "https://metabase.example.com", // Required: Your Metabase instance URL
  authProviderUri: "/sso/metabase", // Required: An endpoint in your app that signs the user in and returns a session
});

使用頁面路由器

您可以建立一個 API 路由,讓使用者登入 Metabase。

在您的 pages/api/* 目錄中建立一個新的 metabase.ts 檔案,例如 pages/api/sso/metabase.ts,對應於 /api/sso/metabase 的端點。

import type { NextApiRequest, NextApiResponse } from "next";
import jwt from "jsonwebtoken";

const METABASE_JWT_SHARED_SECRET = process.env.METABASE_JWT_SHARED_SECRET || "";
const METABASE_INSTANCE_URL = process.env.METABASE_INSTANCE_URL || "";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const token = jwt.sign(
    {
      email: user.email,
      first_name: user.firstName,
      last_name: user.lastName,
      groups: [user.group],
      exp: Math.round(Date.now() / 1000) + 60 * 10, // 10 minutes expiration
    },
    // This is the JWT signing secret in your Metabase JWT authentication setting
    METABASE_JWT_SHARED_SECRET,
  );
  const ssoUrl = `${METABASE_INSTANCE_URL}/auth/sso?token=true&jwt=${token}`;

  try {
    const ssoResponse = await fetch(ssoUrl, { method: "GET" });
    const ssoResponseBody = await ssoResponse.json();

    res.status(200).json(ssoResponseBody);
  } catch (error) {
    if (error instanceof Error) {
      res.status(401).json({
        status: "error",
        message: "authentication failed",
        error: error.message,
      });
    }
  }
}

然後,將此 authConfig 傳遞給 MetabaseProvider

import { defineMetabaseAuthConfig } from "@metabase/embedding-sdk-react/nextjs";
const authConfig = defineMetabaseAuthConfig({
  metabaseInstanceUrl: "https://metabase.example.com", // Required: Your Metabase instance URL
  authProviderUri: "/api/sso/metabase", // Required: An endpoint in your app that signs the user in and returns a session
});

閱讀其他版本的說明文件