Skip to content
Snippets Groups Projects
Unverified Commit 537228e1 authored by Jeff Bruemmer's avatar Jeff Bruemmer Committed by GitHub
Browse files

docs - SDK readme (#49253)

parent 54ec54b7
No related branches found
No related tags found
No related merge requests found
......@@ -10,6 +10,94 @@ title: Embedded analytics SDK - authentication
Notes on handling authentication when working with the SDK.
## Authenticating people from your server
The SDK requires an endpoint in your app's backend that will sign someone into your Metabase and return a token. The SDK will use that token to authenticate calls to Metabase.
The SDK will call this endpoint to get a new token, or to refresh an existing token that's about to expire.
## Example code for generating a token
This example sets up an endpoint in an app, `/sso/metabase`, that creates a token using the shared secret to authenticate calls to Metabase.
```typescript
const express = require("express");
const cors = require("cors");
const session = require("express-session")
const jwt = require("jsonwebtoken");
const fetch = require("node-fetch");
async function metabaseAuthHandler(req, res) {
const { user } = req.session;
if (!user) {
return res.status(401).json({
status: "error",
message: "not authenticated",
});
}
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 response = await fetch(ssoUrl, { method: "GET" });
const token = await response.json();
return res.status(200).json(token);
} catch (error) {
if (error instanceof Error) {
res.status(401).json({
status: "error",
message: "authentication failed",
error: error.message,
});
}
}
}
const app = express();
// Middleware
// If your FE application is on a different domain from your BE, you need to enable CORS
// by setting Access-Control-Allow-Credentials to true and Access-Control-Allow-Origin
// to your FE application URL.
//
// Limitation: We currently only support setting one origin in Authorized Origins in Metabase for CORS.
app.use(
cors({
credentials: true,
}),
);
app.use(
session({
secret: SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: { secure: false },
}),
);
app.use(express.json());
// routes
app.get("/sso/metabase", metabaseAuthHandler);
app.listen(PORT, () => {
console.log(`API running at http://localhost:${PORT}`);
});
```
## Getting Metabase authentication status
You can query the Metabase authentication status using the `useMetabaseAuthStatus` hook. This is useful if you want to completely hide Metabase components when the user is not authenticated.
......@@ -55,36 +143,27 @@ async function fetchRequestToken(url) {
const config = { fetchRequestToken };
```
## Reloading Metabase components
## Authenticating locally with API keys
In case you need to reload a Metabase component, for example, your users modify your application data and that data is used to render a question in Metabase. If you embed this question and want to force Metabase to reload the question to show the latest data, you can do so by using the `key` prop to force a component to reload.
> The Embedded analytics SDK only supports JWT authentication in production. Authentication with API keys is only supported for local development and evaluation purposes.
```typescript
// Inside your application component
const [data, setData] = useState({});
// This is used to force reloading Metabase components
const [counter, setCounter] = useState(0);
// This ensures we only change the `data` reference when it's actually changed
const handleDataChange = newData => {
setData(prevData => {
if (isEqual(prevData, newData)) {
return prevData;
}
For developing locally to try out the SDK, you can authenticate using an API key.
return newData;
});
};
First, create an [API key](../../people-and-groups/api-keys.md).
useEffect(() => {
/**
* When you set `data` as the `useEffect` hook's dependency, it will trigger the effect
* and increment the counter which is used in a Metabase component's `key` prop, forcing it to reload.
*/
if (data) {
setCounter(counter => counter + 1);
}
}, [data]);
Then you can then use the API key to authenticate with Metabase in your application. All you need to do is include your API key in the config object using the key: `apiKey`.
```typescript
const metabaseConfig = {
...
apiKey: "YOUR_API_KEY"
...
};
return <InteractiveQuestion key={counter} questionId={yourQuestionId} />;
export default function App() {
return (
<MetabaseProvider config={metabaseConfig} className="optional-class">
Hello World!
</MetabaseProvider>
);
```
---
title: Embedded analytics SDK - config
---
{% include beta-blockquote.html %}
{% include plans-blockquote.html feature="Embedded analytics SDK" %}
# Embedded analytics SDK - config
To use the SDK in your app, you need to import the `MetabaseProvider` component and provide it with a `config` object, like so:
```typescript
const config = defineEmbeddingSdkConfig({
metabaseInstanceUrl: "https://metabase.example.com", // Required: Your Metabase instance URL
jwtProviderUri: "https://app.example.com/sso/metabase", // Required: An endpoint in your app that returns signs the user in and delivers a token
});
export default function App() {
return (
<MetabaseProvider config={config} theme={theme} className="optional-class">
Hello World!
</MetabaseProvider>
);
}
```
You can also pass additional objects to `MetabaseProvider`:
- `config` (Required). Includes information about your Metabase.
- `theme` (Optional) See [Appearance](./appearance.md).
- `pluginsConfig` (Optional). See [Plugins](./plugins.md).
- `eventHandlers` (Optional). See [Global event handlers](#global-event-handlers).
## Example `config` object passed to `MetabaseProvider`
```typescript
import React from "react";
import {
MetabaseProvider,
defineEmbeddingSdkConfig,
defineEmbeddingSdkTheme,
} from "@metabase/embedding-sdk-react";
// Configuration
const config = defineEmbeddingSdkConfig({
metabaseInstanceUrl: "https://metabase.example.com", // Required: Your Metabase instance URL
jwtProviderUri: "https://app.example.com/sso/metabase", // Required: An endpoint in your app that returns signs the user in and delivers a token
});
// See the "Customizing appearance" section for more information
const theme = defineEmbeddingSdkTheme({
// Optional: Specify a font to use from the set of fonts supported by Metabase
fontFamily: "Lato",
// Optional: Match your application's color scheme
colors: {
brand: "#9B5966",
"text-primary": "#4C5773",
"text-secondary": "#696E7B",
"text-tertiary": "#949AAB",
},
});
export default function App() {
return (
<MetabaseProvider config={config} theme={theme} className="optional-class">
Hello World!
</MetabaseProvider>
);
}
```
## Global event handlers
`MetabaseProvider` also supports `eventHandlers`.
Currently, we support:
- `onDashboardLoad?: (dashboard: Dashboard | null) => void;`. Triggers when a dashboard loads with all visible cards and their content
- `onDashboardLoadWithoutCards?: (dashboard: Dashboard | null) => void;`. Triggers after a dashboard loads, but without its cards (at this stage only the dashboard title, tabs, and cards grid are rendered, but the contents of the cards have yet to load.
```typescript
const handleDashboardLoad: SdkDashboardLoadEvent = dashboard => {
/* do whatever you need to do - e.g. send analytics events, show notifications */
};
const eventHandlers = {
onDashboardLoad: handleDashboardLoad,
onDashboardLoadWithoutCards: handleDashboardLoad,
};
return (
<MetabaseProvider config={config} eventHandlers={eventHandlers}>
{children}
</MetabaseProvider>
);
```
## Reloading Metabase components
In case you need to reload a Metabase component, for example, your users modify your application data and that data is used to render a question in Metabase. If you embed this question and want to force Metabase to reload the question to show the latest data, you can do so by using the `key` prop to force a component to reload.
```typescript
// Inside your application component
const [data, setData] = useState({});
// This is used to force reloading Metabase components
const [counter, setCounter] = useState(0);
// This ensures we only change the `data` reference when it's actually changed
const handleDataChange = newData => {
setData(prevData => {
if (isEqual(prevData, newData)) {
return prevData;
}
return newData;
});
};
useEffect(() => {
/**
* When you set `data` as the `useEffect` hook's dependency, it will trigger the effect
* and increment the counter which is used in a Metabase component's `key` prop, forcing it to reload.
*/
if (data) {
setCounter(counter => counter + 1);
}
}, [data]);
return <InteractiveQuestion key={counter} questionId={yourQuestionId} />;
```
......@@ -18,28 +18,42 @@ To give you and idea of what's possible with the SDK, we've put together example
![Pug and play example app built with embedding SDK](../images/pug-and-play.png)
# Embedded analytics SDK prerequisites
## Embedded analytics SDK prerequisites
- React application. The SDK is tested to work with React 18 or higher, though it may work with earlier versions.
- [Metabase Pro or Enterprise subscription or free trial](https://www.metabase.com/pricing/).
- Metabase version 1.50 or higher.
- Metabase version 1.51 or higher.
- [Node.js 18.x LTS](https://nodejs.org/en) or higher.
## Embedded analytics SDK on NPM
Check out the Metabase Embedded analytics SDK on NPM: [metaba.se/sdk](https://metaba.se/sdk).
## Quickstart with CLI
If you have Node and Docker installed, you can change into your React application and run:
```sh
npx @metabase/embedding-sdk-react@latest start
```
Only works locally, and you don't need a license key (but you can use one to demo more features).
See more about the [CLI quickstart](./quickstart-cli.md).
We also have a [quickstart with a sample app that uses JWT](./quickstart.md).
## Installation
You'll need to enable the SDK in your Metabase, and install the SDK as a dependency in your app.
### In Metabase
Enable the Embedded Analytics SDK by going to **Admin settings > Settings > Embedding**. Toggle on the SDK, and hit **Configure**. Enter the origins for your website or app where you want to allow SDK embedding, separated by a space. Localhost is automatically included.
Enable the Embedded analytics SDK by going to **Admin settings > Settings > Embedding**. Toggle on the SDK, and hit **Configure**. Enter the origins for your website or app where you want to allow SDK embedding, separated by a space. Localhost is automatically included.
### In your React application
You can install the Embedded Analytics SDK for React via npm:
You can install the Embedded analytics SDK for React via npm:
```bash
npm install @metabase/embedding-sdk-react@51-stable
......@@ -53,7 +67,8 @@ yarn add @metabase/embedding-sdk-react@51-stable
## Developing with the embedded analytics SDK
- [Quickstart](./quickstart.md)
- [Quickstart with sample app and JWT](./quickstart.md)
- [Quickstart with CLI and your data](./quickstart-cli.md)
- [Questions](./questions.md)
- [Dashboards](./dashboards.md)
- [Collections](./collections.md)
......@@ -61,20 +76,27 @@ yarn add @metabase/embedding-sdk-react@51-stable
- [Plugins](./plugins.md)
- [Versioning](./version.md)
- [Authentication](./authentication.md)
- [Config](./config.md)
- [Notes on Next.js](./next-js.md)
## Embedding SDK source code
You can find the [embedding SDK source code in the Metabase repo](https://github.com/metabase/metabase/tree/master/enterprise/frontend/src/embedding-sdk).
## Changelog
[View changelog](https://github.com/metabase/metabase/blob/master/enterprise/frontend/src/embedding-sdk/CHANGELOG.md)
## SDK limitations
- Unsupported features:
- Verified content
- Official collections
- Subscriptions
- Alerts
- The Metabase Embedding SDK doesn't support server-side rendering (SSR).
- You can't embed multiple interactive dashboards on the same application page. If you need to embed multiple dashboards on the same application page, you can embed static dashboards.
The SDK doesn't support:
- Verified content
- Official collections
- Subscriptions
- Alerts
- Server-side rendering (SSR).
- Multiple _interactive_ dashboards on the same application page. If you need to embed multiple dashboards on the same application page, you can embed static dashboards.
## Feedback
......
---
title: Embedded analytics SDK - Using the SDK with Next.js
---
# Embedded analytics SDK - Using the SDK with Next.js
Some notes on using the Embedded analytics SDK with [Next.js](https://nextjs.org/).
## Using App Router
Create a component that imports the `MetabaseProvider` and mark it as a React Client component with "use client".
```typescript
"use client";
import { MetabaseProvider, StaticQuestion, defineEmbeddingSdkConfig } from "@metabase/embedding-sdk-react";
const config = defineEmbeddingSdkConfig({
//...
}); // Your Metabase SDK configuration
export default function MetabaseComponents() {
return (
<MetabaseProvider config={config}>
<StaticQuestion questionId={QUESTION_ID} />
</MetabaseProvider>
);
```
You must use the default export. A named export isn't supported with this setup, and it won't work.
Then, import this component in your page:
```typescript
// page.tsx
const MetabaseComponentsNoSsr = dynamic(
() => import("@/components/MetabaseComponents"),
{
ssr: false,
},
);
export default function HomePage() {
return (
<>
<MetabaseComponentsNoSsr />
</>
);
}
```
To repeat: if you export the component as a named export, it won't work with Next.js. You must use a default export. For example, this _won't_ work:
```typescript
const DynamicAnalytics = dynamic(
() =>
import("@/components/MetabaseComponents").then(
module => module.MetabaseComponents,
),
{
ssr: false,
},
);
```
## Handling authentication
If you authenticate with Metabase using JWT, you can create a Route handler that signs people in to Metabase.
Create a new `route.ts` file in your `app/*` directory, for example `app/sso/metabase/route.ts` that corresponds to an endpoint at /sso/metabase.
```typescript
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,
},
);
}
}
}
```
Pass this `config` to `MetabaseProvider`
```typescript
import { defineEmbeddingSdkConfig } from "@metabase/embedding-sdk-react";
const config = defineEmbeddingSdkConfig({
metabaseInstanceUrl: "https://metabase.example.com", // Required: Your Metabase instance URL
jwtProviderUri: "/sso/metabase", // Required: An endpoint in your app that signs people in and returns a token.
});
```
## Using pages router
This works almost the same as the App Router, but you don't need to mark the component that imports the Metabase SDK components as a React Client component.
If you authenticate with Metabase using JWT, you can create an API route that signs people in to Metabase.
Create a new `metabase.ts` file in your `pages/api/*` directory, for example `pages/api/sso/metabase.ts` that corresponds to an endpoint at /api/sso/metabase.
```typescript
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,
});
}
}
}
```
And pass this `config` to `MetabaseProvider`
```ts
import { defineEmbeddingSdkConfig } from "@metabase/embedding-sdk-react";
const config = defineEmbeddingSdkConfig({
metabaseInstanceUrl: "https://metabase.example.com", // Required: Your Metabase instance URL
jwtProviderUri: "/api/sso/metabase", // Required: An endpoint in your app that returns signs the user in and delivers a token
});
```
......@@ -84,7 +84,7 @@ export default function App() {
## Customizing interactive questions
By default, the Embedded Analytics SDK provides a default layout for interactive questions that allows you to view your questions, apply filters and aggregations, and access functionality within the query builder.
By default, the Embedded analytics SDK provides a default layout for interactive questions that allows you to view your questions, apply filters and aggregations, and access functionality within the query builder.
Here's an example of using the `InteractiveQuestion` component with its default layout:
......
---
title: Embedded analytics SDK - CLI quickstart
---
# Embedded analytics SDK - CLI quickstart
{% include beta-blockquote.html %}
We built a single command to spin up a Metabase and help you get an embedded dashboard in your app. This setup won't work in production; it's only intended for you to quickly try out the SDK on your local machine.
## Prerequisites
- Docker
- [Node.js 18.x LTS](https://nodejs.org/en) or higher.
- License (Optional - only if you want to try out multi-tenancy).
- Database (you can connect to your app's database).
## The quickstart command
Change into your React application and run:
```sh
npx @metabase/embedding-sdk-react@latest start
```
## Script overview
The script will walk you through the setup. There are a fair number of pieces to put together, so here's an overview of what the command does.
1. Checks that you're installing the SDK in a React application.
2. Looks for, or installs, the Embedded analytics SDK.
3. Spins up a Metabase on Docker. This takes a bit. To see the Docker container's status, use the `docker ps` command. Or use the time to reflect on good choices you've made recently.
4. Asks you for an email address to create the first admin account in Metabase.
5. Generates a new API Key. The script will build a mock Express server that will use this key to authenticate its requests to your Metabase.
6. Prompts you to connect to a database. Pick your database's engine. You'll need the database's host, port, username, and password (if Postgres, you can also use an auth provider).
7. Connects to the database, and prompts you to select tables from your database to embed. Pick 1-3 tables. If you want to see multi-tenancy in action, pick a table with user IDs in it. Metabase will X-ray these tables to create a dashboard to embed.
8. (Optional): If you have a Pro/EE license, the script can set up permissions. To get a license, sign up for a [free trial of self-hosted Metabase Pro](https://www.metabase.com/pricing/).
9. (Optional): If you set up multi-tenancy, the script asks you for the column used to sandbox the table (e.g., a user ID column). Metabase will sandbox data based on the values in that column.
10. Generates example React components files in "./components/metabase" in your React app (you may need to move these into your `src` directory).
11. Asks you where it (the script) should save the mock Express server (default: `./mock-server`). It'll install the mock server's dependencies with `npm install`.
12. Prompts you to start the mock server in another terminal session. Change into the mock server's directory and run:
```sh
npm run start
```
Once the mock server is running, go back to the script's terminal session and press <Enter> to continue.
13. Prompts you to add the following `import` in your client app:
```sh
import { AnalyticsPage } from "././components/metabase";
```
Make sure the `from` path is valid (depending on your app, you may need to move the components to a new directory).
14. Prompts you to add the `<AnalyticsPage />` component to your page.
15. Start your app, and view the page where you added the `<AnalyticsPage />` component, and you should see an embedded dashboard.
Your Metabase should be running at `http://localhost:3366`. You can find your login credentials at `METABASE_LOGIN.json`.
## Further reading
- [Quickstart with sample app and JWT](./quickstart.md)
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment