ข้ามไปยังเนื้อหา

ตั้งค่า Project และ Supabase CLI

Supabase project เริ่มต้นจาก Postgres instance แบบ hosted พร้อม API key สองตัวที่ระดับความน่าเชื่อถือต่างกันมาก และ Supabase CLI คือสิ่งที่ทำให้คุณ link folder บนเครื่องเข้ากับ project นั้น แล้วรันทั้ง stack บนเครื่องตัวเองด้วย Docker ได้

คุณสร้าง hosted project ใหม่ได้จาก dashboard ของ Supabase หรือ shortcut database.new เบื้องหลัง Supabase จะ provision Postgres instance ของคุณเอง, API URL และ API key สองตัว

  • anon key — public ใช้ใน client-side code ได้ปลอดภัย (browser bundle, mobile app) request ที่ใช้ key นี้ต้องผ่าน Row Level Security
  • service_role key — secret, full-access, bypass Row Level Security ทั้งหมด ใช้เฉพาะกับ server-side code ที่เชื่อถือได้เท่านั้น
// Server-side only — e.g. an API route, a cron job, an Edge Function.
// process.env.SUPABASE_SERVICE_ROLE_KEY must never be bundled into client code.
import { createClient } from '@supabase/supabase-js';
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);

CLI คือวิธีจัดการ project เป็น code แทนที่จะทำผ่าน dashboard อย่างเดียว

Terminal window
# Scaffold a local supabase/ config directory in your project
npx supabase init
# Authenticate the CLI with your Supabase account
supabase login
# Connect this local project folder to a specific hosted project
supabase link --project-ref <project-ref>

supabase link คือคำสั่งที่บอกว่า folder supabase/ บนเครื่องคุณตรงกับ hosted project ตัวไหน — db push หรือ db diff ครั้งต่อ ๆ ไปจะเล็งไปที่ project ที่ link ไว้นี้เสมอ

Terminal window
supabase start

คำสั่งนี้จะสตาร์ท Supabase stack ทั้งชุดบนเครื่องตัวเองผ่าน Docker — Postgres, Studio, Auth, Storage, Realtime และอื่น ๆ รันเป็น container บนเครื่องคุณทั้งหมด นั่นแปลว่าคุณ develop และ test schema, RLS policy และ client code ได้แบบ offline เต็มรูปแบบ โดยไม่มีความเสี่ยงต่อ hosted project เลย ก่อนจะรัน supabase db push ขึ้นของจริง

รันคำสั่งนี้ (หรือถ้ารันอยู่แล้วก็ supabase status) แล้ว CLI จะ print URL และ key ทุกตัวที่คุณต้องใช้ — นี่คือ output จริงที่ตัดให้สั้นลงจาก local stack ของคอร์สนี้เอง:

$ supabase status --workdir tools/probe -o env
DB_URL="postgresql://postgres:[email protected]:55322/postgres"
API_URL="http://127.0.0.1:55321"
REST_URL="http://127.0.0.1:55321/rest/v1"
GRAPHQL_URL="http://127.0.0.1:55321/graphql/v1"
MCP_URL="http://127.0.0.1:55321/mcp"
STUDIO_URL="http://127.0.0.1:55323"
ANON_KEY="eyJhbGciOiJIUzI1NiIs...I0"
SERVICE_ROLE_KEY="eyJhbGciOiJIUzI1NiIs...IU"

ทุกตัวนี้คือ endpoint local จริงที่เรียกใช้ได้ทันทีที่ supabase start เสร็จ — DB_URL คือ connection ตรงเข้า Postgres, API_URL คือประตูหน้าของ Kong จากบทที่แล้ว และ MCP_URL คือหัวข้อของส่วนที่เหลือของ section นี้

MCP (Model Context Protocol) คือ standard ที่ AI coding tool (Claude Code, Cursor และตัวอื่น) ใช้เรียก tool กับระบบภายนอก แทนที่จะอ่าน/เขียนไฟล์อย่างเดียว Supabase มี MCP server ที่เป็นทางการให้ AI agent ตรวจ schema, รัน SQL, เช็ค advisor และ generate type ได้โดยตรง — โดยไม่ต้อง copy-paste schema dump ใส่ chat เอง ตั้งแต่ CLI เวอร์ชันหลัง ๆ supabase start จะรัน MCP server local ให้อัตโนมัติ เข้าถึงได้ที่ MCP_URL ที่ print ไว้ข้างบน ไม่ต้อง setup เพิ่ม ไม่ต้องมี process npx @supabase/mcp-server-supabase แยกให้จัดการเอง

พิสูจน์ว่ามันคือ MCP server จริง ไม่ใช่ URL เฉย ๆ — JSON-RPC initialize handshake แบบดิบ ยิงตรงด้วย curl:

$ curl -s -X POST http://127.0.0.1:55321/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"fnd-probe","version":"0.1.0"}}}'
{"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{}},"serverInfo":{"name":"supabase","title":"Supabase","version":"0.11.0"},"instructions":"Here are guidelines for using Supabase tools effectively:\n\n- Before making schema changes, use `list_tables`...\n..."},"jsonrpc":"2.0","id":1}

MCP session จริง, ชื่อและ version ของ server จริง แถมยังมี guidance ที่ server เขียนเองให้ agent ที่เรียกอยู่ใน instructions ต่อด้วยเรียก tools/list จะเห็นเลยว่า server local ตัวนี้ทำอะไรได้บ้าง:

$ curl -s -X POST http://127.0.0.1:55321/mcp \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
search_docs, list_tables, list_extensions, list_migrations, apply_migration,
execute_sql, query_logs, get_advisors, get_project_url, get_publishable_keys,
generate_typescript_types

AI coding tool ต่อเข้ามาแบบเดียวกับ MCP client ทั่วไป — สำหรับ local stack แค่ชี้ไป URL ข้างบนตรง ๆ ไม่ต้องมี access token เลย เพราะไม่มี hosted project หรือ PAT อยู่ในสมการ:

{
"mcpServers": {
"supabase-local": {
"url": "http://127.0.0.1:55321/mcp"
}
}
}

พอต่อแล้ว agent เรียก execute_sql รัน SQL อะไรก็ได้ นี่ไม่ใช่เรื่องสมมติ — พิสูจน์ตรงกับ local stack นี้เลย:

$ curl -s -X POST http://127.0.0.1:55321/mcp \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"execute_sql","arguments":{"query":"select current_user, session_user;"}}}'
{"current_user":"postgres","session_user":"postgres"}

current_user คือ postgres — database superuser ไม่ใช่แค่ service_role local MCP server ต่อเข้า Postgres โดยตรง ด้วยสิทธิ์ระดับ admin เต็ม: อ่านหรือเขียน table ไหนก็ได้ไม่สนใจ RLS, รัน DDL อะไรก็ได้ และดู schema ไหนก็ได้ รวมถึง auth กับ storage เอกสาร MCP อย่างเป็นทางการ พูดตรง ๆ ว่า “Connecting an LLM to your Supabase projects carries security risks” และเรียก prompt injection ว่าเป็น “the primary attack vector unique to LLMs” — data ที่ query คืนมา (bio field ของ user, เนื้อหา support ticket) อาจมี text ที่ agent ที่ไม่ระวังพอตีความเป็นคำสั่งใหม่ เอกสารเดียวกันแนะนำให้เปิด parameter read_only=true ไว้และเปิด manual tool-call approval เพราะเหตุผลนี้เป๊ะ ๆ และคู่มือ self-hosting MCP ก็พูดชัดว่า endpoint นี้ไม่ควรเข้าถึงได้จาก internet เปิด — เข้าผ่าน VPN หรือ SSH tunnel เท่านั้น local ความเสี่ยงต่ำกว่า (เป็น dev database ทิ้งได้ของตัวเอง) แต่พอ pattern เดียวกันนี้ไปชี้ที่ project จริง “ให้ MCP URL กับ AI agent” ก็เท่ากับ “ให้ AI agent มี psql session แบบไม่จำกัดในฐานะเจ้าของ database”

AI assistant ถูกขอให้ “wire up the Supabase MCP server for this project” แล้วสร้าง config นี้ออกมา:

🐛 หา bug ในโค้ดนี้

src/mcp-config-check.ts
// An AI assistant's suggested MCP client config, pasted into a shared repo README
const mcpConfig = {
mcpServers: {
supabase: {
command: 'npx',
args: [
'-y',
'@supabase/mcp-server-supabase@latest',
'--access-token',
'sbp_<your-personal-access-token>',
],
},
},
};
export {};
เฉลย

บั๊กไม่ได้อยู่ที่ shape ของ JSON — แต่คือ personal access token จริงที่ยังใช้งานได้ถูก commit ตรง ๆ ลงใน config ตัวอย่างในที่ repo ที่แชร์กัน แบบเดียวกับที่ AI assistant จะยินดี paste มันกลับมาให้ถ้าเคยเห็น token นั้นมาก่อนใน conversation แล้วถูกขอให้ “write out the config” PAT หนึ่งตัว scope ครอบทั้ง account โดย default (ทุก project เว้นแต่ --project-ref จะจำกัดไว้ ตามตาราง tool-permission ของเอกสาร Supabase เอง) ดังนั้น token ที่หลุดไม่ใช่แค่ปัญหาระดับ “project นี้” — แต่เป็น “attacker list, แก้ หรือลบ project ไหนก็ได้ใน account นี้” ทางแก้มีสองส่วน: อย่าให้ token ตัวจริงหลุดลงไฟล์ที่เข้า version control เด็ดขาด (ใช้ environment variable แทน — "--access-token", process.env.SUPABASE_PAT ใน MCP client ที่รองรับ env expansion หรือกลไก secret ของ client เอง) และถือว่า token ไหนก็ตามที่ AI assistant เคยเห็นใน conversation แล้วว่า compromised แล้ว — rotate จาก dashboard แบบเดียวกับที่ทำหลัง secret หลุดโดยไม่ตั้งใจกรณีอื่น ๆ

MCP server ไม่ใช่วิธีใหม่ที่ Supabase ใช้รัน SQL — มันคือ translation layer ที่ให้ AI agent เรียก operation เดียวกับที่คุณรันเองด้วยมือได้ (psql, supabase db push, Studio SQL editor) ผ่าน structured tool-call protocol แทนที่คุณจะ copy-paste เอง ทุก capability ที่มันเปิดให้ map ตรงกับสิ่งที่คอร์สนี้สอนตรง ๆ อยู่แล้ว: execute_sql คือ psql -c, apply_migration คือ supabase migration new บวก db push, get_advisors คือ panel Advisors ของ Studio มองมันเป็น “อีกวิธีรันคำสั่งจากคอร์สนี้ แค่มี LLM เป็นคนกดคีย์บอร์ดแทน” คือ mental model ที่ถูกต้อง — และเป็นมุมมองที่ถูกต้องสำหรับตัดสินว่าจะให้ความไว้ใจมันมากแค่ไหน

  • MCP server ให้ AI agent ตรวจและแก้ schema ได้โดยไม่ต้องเขียน SQL ทุกคำสั่งเอง — แลกมาด้วยการยกสิทธิ์ระดับเดียวกับที่ human ที่เข้าถึง database เต็มรูปแบบมี พิสูจน์ข้างบน: execute_sql รันในฐานะ Postgres superuser local ไม่ใช่ role ที่จำกัด
  • personal access token แบบ hosted scope ได้แค่ project เดียว (--project-ref) และตั้ง read-only ได้ (--read-only) — แลกมาด้วยต้องจำตั้ง flag พวกนี้เอง default ที่ไม่ scope และ read-write คือแบบที่อันตรายกว่า และเป็นแบบที่ตัวอย่าง copy-paste ทุกอันใช้เป็น default
  • setup local เป็นศูนย์ (ไม่มี PAT, ไม่มี npx wrapper) ทำให้ลองง่ายมาก — แลกมาด้วย access control local ก็เป็นศูนย์เหมือนกัน: ไม่มี flag ไหนให้ scope กับ MCP_URL เลย มีแค่ “ชี้ agent ไปที่ database local หรือไม่ชี้”
  • “MCP server ให้ AI agent อ่านข้อมูลได้อย่างเดียว แก้ไขอะไรไม่ได้” ถูกหักล้างด้วย tool list เอง: apply_migration กับ execute_sql เขียนได้ทั้งคู่ get_advisors, list_tables และ search_docs คือตัวที่ read-only จริง — อันตรายข้างบนพูดถึงตัวที่ไม่ใช่ read-only โดยเฉพาะ
  • “service_role คือสิทธิ์สูงสุดที่ AI agent จะเข้าถึงได้” พิสูจน์ข้างบนแล้วว่า local MCP server รันในฐานะ postgres ซึ่งคือ database superuser จริง — สิทธิ์สูงกว่า service_role อีกขั้น เพราะ service_role ยังเป็นแค่ Postgres role ที่ผูกกับ grant ปกติ (service_role แค่ไม่ถูก RLS บังคับ)
  • “MCP ปลอดภัยตราบใดที่ AI model เองน่าเชื่อถือ” ความเสี่ยงที่เอกสารระบุคือ prompt injection จาก data ที่ query คืนมา ไม่ใช่จากเจตนาของ model เอง — model ที่ทำงานถูกต้องก็ยังถูกชักจูงได้ด้วย text ที่ฝังอยู่ใน row ที่มันถูกขอให้อ่าน
flowchart LR
  subgraph local["Local machine (supabase start)"]
    lpg[("Postgres")]
    lstudio["Studio"]
    lauth["Auth"]
    lstorage["Storage"]
  end
  subgraph hosted["Hosted project"]
    hpg[("Postgres")]
    hapi["API URL + anon/service_role keys"]
  end
  local -- "supabase link --project-ref" --> hosted
  local -- "supabase db push" --> hosted
Local Docker-based stack ที่ link เข้ากับ hosted project
anon key กับ service_role key ต่างกันตรงไหนหลัก ๆ
ทำไม service_role key ห้ามหลุดไปถึง client-side code
supabase start ทำอะไร
supabase link --project-ref <ref> ทำอะไร
curl จริงเข้า MCP server ของ local stack นี้ รัน execute_sql ด้วย select current_user; แล้วได้ postgres กลับมา สิ่งนี้พิสูจน์อะไรเกี่ยวกับอันตรายของการเปิด MCP_URL