嵌入式分析 SDK - 将 SDK 与 Next.js 结合使用

⚠️ 此功能为 Beta 版。欢迎试用,但请注意,功能可能会发生变化(并且可能无法按预期工作)。

嵌入式分析 SDK 仅在 ProEnterprise 计划(自托管和 Metabase 云)中可用。但是,您可以在本地计算机上使用 API 密钥进行身份验证,无需许可证即可试用 SDK。

关于将嵌入式分析 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>
  );
};

接下来,将一个 index.tsx 文件添加到该 metabase 目录。此文件将包含 use client 指令,并且它将导出 EmbeddingSdkProvider 的禁用 SSR 的延迟加载版本。

"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
});

阅读其他Metabase 版本的文档。