من یک Backend Developer هستم که دوست دارم توی مسیر یادگیری و تجربه رشد کنم. ساختن ، همیشه برام هیجانانگیزه و تلاش میکنم هر روز چیز جدیدی یاد بگیرم 🌱
چگونه با استفاده از Metabase SDK و FastAPI، دسترسی امن و محدود به دادههای داشبوردها ایجاد کردیم؟

در تیم پشتیبانی ، نیاز داشتیم اپراتورها فقط به دادههایی خاص از داشبوردهای متابیس دسترسی داشته باشن — نه بیشتر.
ما نمیخواستیم:
کاربر وارد محیط متابیس بشه،
چیزی از کوئریها ببینه،
یا حتی به لینک یا iframe داشبوردها دسترسی داشته باشه.
🎯 راهحل نهایی: React + Metabase SDK + FastAPI Proxy
در نهایت، این معماری رو پیادهسازی کردیم:
در سمت فرانتاند، از Metabase Embedding SDK استفاده کردیم.
این SDK امکان رندر مستقیم داشبورد داخل کامپوننتهای ریاکت رو میده.
اما برای امنیت بیشتر، جلوی ارتباط مستقیم SDK با متابیس رو گرفتیم و یک لایه پروکسی با FastAPI بینشون قرار دادیم.
جریان کار به این شکله:
کاربر وارد پنل پشتیبانی میشه (احراز هویت با JWT داخلی ما).
کاربر تنها داشبوردهای رو میبینه که با توجه به رول/پرمیشنهاش در سیستم تعریف شدن.
وقتی SDK میخواد دادهی یک داشبورد رو بگیره، درخواست رو به جای متابیس، به سرویس پروکسی ما میفرسته.
در پروکسی (FastAPI):
JWT اعتبارسنجی میشه.
پرمیشن کاربر برای این داشبورد چک میشه.
در صورت تایید، درخواست به API متابیس فوروارد میشه.
پاسخ متابیس بهصورت JSON دریافت میشه و به SDK برگشت داده میشه.
SDK داده رو گرفته و خودش داخل ریاکت رندر میکنه .
چرا SDK رو بهجای embed URL انتخاب کردیم؟
کنترل کامل روی ظاهر و تعامل در ریاکت
بدون استفاده از iframe و محدودیتهایش
امکان شخصیسازی request ها و header ها
خروجی JSON آماده برای ویژوالسازی دلخواه
نکات امنیتی مهم
ارتباط مستقیم بین کاربر و متابیس کاملاً قطع شده.
فقط پروکسی ما به API متابیس دسترسی داره.
سطح دسترسی هر کاربر قبل از ارسال request به متابیس، در سرویس ما بررسی میشه.
دادهی خام، کوئریها یا اطلاعات متا به کاربر داده نمیشه؛ فقط خروجی پاکشده و کنترلشده از متابیس.
نمونه کد سمپل پروکسی سرویس :
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse, Response
import httpx
import json
from urllib.parse import urlencode
app = FastAPI()
METABASE_URL = "https://your-metabase-url.com" # ← آدرس متابیس
METABASE_API_KEY = "your-api-key" # ← کلید API متابیس
@app.api_route("/{path:path}", methods=["GET", "POST", "OPTIONS"])
async def proxy_metabase(path: str, request: Request):
# ⛔️ اینجا میتونی دسترسیهای کاربر رو بررسی کنی
# --------------------------------------------------------------------
# مثال:
# user = await get_current_user(request)
# if not user.has_permission(path):
# raise HTTPException(status_code=403, detail="Permission denied")
# --------------------------------------------------------------------
# ساخت آدرس کامل متابیس
query_string = urlencode(dict(request.query_params))
full_url = f"{METABASE_URL}/{path}"
if query_string:
full_url += f"?{query_string}"
# گرفتن بدنه درخواست در صورت نیاز
body = None
if request.method in {"POST", "PUT", "PATCH"}:
raw = await request.body()
if raw:
body = json.loads(raw.decode("utf-8"))
# ارسال درخواست به متابیس
async with httpx.AsyncClient() as client:
response = await client.request(
method=request.method,
url=full_url,
json=body,
headers={
"x-api-key": METABASE_API_KEY,
"User-Agent": "fastapi-proxy"
}
)
# بازگرداندن پاسخ
content_type = response.headers.get("content-type", "")
if "application/json" in content_type:
return JSONResponse(content=response.json(), status_code=response.status_code)
return Response(
content=response.content,
status_code=response.status_code,
media_type=content_type
)
سمت فرانت هم به این شکل عمل کردیم:
📁 ساختار پیشنهادی
src/
├── metabase/
│ ├── MetabaseProvider.jsx
│ ├── Dashboard.jsx
│ └── config.js
✅ config.js (تنظیمات اتصال به متابیس)
import { defineMetabaseAuthConfig, defineMetabaseTheme } from '@metabase/embedding-sdk-react'
const config = defineMetabaseAuthConfig({
preferredAuthMethod: 'jwt',
metabaseInstanceUrl: 'https://proxy-api.example.com', // آدرس متابیس پروکسی شما
// درخواست گرفتن توکن JWT از API بکاند
fetchRequestToken: async () => {
const response = await fetch('https://proxy-api.example.com/auth/sso?preferred_method=jwt', {
method: 'GET',
credentials: 'include', // ← برای ارسال کوکیها
})
return response.json() // باید { token: '...' } برگردونه
},
})
// تم اختیاری (میتونید حذفش کنید)
const theme = defineMetabaseTheme({ colorScheme: 'light' })
export { config, theme }✅ MetabaseProvider.jsx (Provider اصلی برای کانتکست)
import { config, theme } from './config'
import { MetabaseProvider as SDKProvider } from '@metabase/embedding-sdk-react'
function MetabaseProvider({ children }) {
return (
<SDKProvider authConfig={config} theme={theme}>
{children}
</SDKProvider>
)
}
export { MetabaseProvider }
✅ Dashboard.jsx (نمایش داشبورد خاص با پارامتر)
import { useMemo } from 'react'
import { useLocation, useParams } from 'react-router-dom'
import { StaticDashboard, MetabaseProvider } from '@metabase/embedding-sdk-react'
function objectifyQuery(search) {
const query = new URLSearchParams(search)
const obj = {}
for (const [key, value] of query.entries()) {
obj[key] = value
}
return obj
}
function DashboardPage() {
const { id } = useParams() // مثلاً /dashboard/12
const { search } = useLocation()
const queryParams = useMemo(() => objectifyQuery(search), [search])
return (
<div style={{ height: '100vh' }}>
<MetabaseProvider>
<StaticDashboard
dashboardId={+id}
initialParameters={queryParams}
withTitle
/>
</MetabaseProvider>
</div>
)
}
export default DashboardPage
نتیجهگیری
اگر به دنبال پیادهسازی یک سیستم امن و حرفهای برای نمایش داشبوردهای متابیس هستید که:
دسترسیها و پرمیشنها را به صورت دقیق و مبتنی بر نقش کاربران مدیریت کند،
بدون استفاده از iframe و با حفظ یکپارچگی تجربه کاربری در فرانتاند ریاکت باشد،
کوئریها و لینکهای حساس متابیس را از دید کاربران نهایی مخفی نگه دارد،
و قابلیت توسعه و سفارشیسازی آسان را داشته باشد،
ترکیب استفاده از بسته @metabase/embedding-sdk همراه با یک سرویس پروکسی امن مبتنی بر FastAPI، راهکاری ایدهآل، مقیاسپذیر و قابل اعتماد خواهد بود که به راحتی میتوانید آن را مطابق نیازهای سازمان خود توسعه دهید و بهینهسازی کنید.
نمونه داشبورد پیاده سازی شده :

مساله تشخیص محصولات باسلام؛ یک تجربه عملی از بهکارگیری LLM ها
چطور نرمافزار باسلام را برای تبلیغات تلویزیونی مقیاسپذیر کردیم؟
از نیاز تا MVP : ساخت اکستنشن تبدیل گفتار به متن فارسی برای پشتیبانی باسلام