Галоўная / Артыкулы / Supabase GraphQL Direct протыва вашага сабскіптаванага Apollo Backend

Supabase GraphQL Direct протыва вашага сабскіптаванага Apollo Backend

Паруцьце pg_graphql, які выкаліваюцца няпраздзе з Supabase, з адзінам Apollo серверам — аутантыкацыя, RLS, падпіскі, а таксама калі выграе гібрыдны шлях Edge Function.

1853 слоў

GraphQL — это язык запитаў, а не среда выконання. Яго можна выклікваць з колькох месцаў:

  • Вбудованы pg_graphql у Supabase — схема, створаная на базе PostgreSQL, хоставаная Supabase, практычна без налаштоўкаў
  • Спецыяльны бэкенд — Node, Python чытаў Go, які запускае Apollo Server, Hasura, Pothos чытаў падобныя рашэнняя, якія належаць вам і якія вы самі розмешчаеце

У будзь-якам случае фронтэнд можа выкарыстоўваць Apollo Client. Зменяецца толькі месца, дзе знаходзится схема GraphQL, і тое, хто яю контролюе.

Система Vision Expert System выбірае падход через Supabase:

// src/client/supabase-grphql-apollo.client.js
const httpLink = new HttpLink({
  uri: `${SUPABASE_URL}/graphql/v1`,  // ← Supabase's built-in endpoint
});

Няма процэса Express, няма Apollo Server, няма ручна напісаных рашэнняў, няма дадатковага розмешчання. API-інтерфейс — це схема базы дадзеных.

Эскізы архітектуры

Падход А: Чыраз Supabase

Браузер співае безпосередна з Supabase GraphQL. Аутэнтыкацыя адбываецца за дапамогою JWT у Supabase. Безпека на рэвеніі строк — гэта механізм контролю.

Падчынны B: Адзінольны бэкенд

Браузер выступае ў зв’язку з вашым серверам GraphQL. Разьяснювачы выконваюць запиты да Supabase (або іншых джэрел) за дапамогою адмінскага SDK чыта SQL. Вы самі ствараеце типы і поля.

Інтуіцыя па прымэру

Прямой режым оптымаўна падходзіць для викорыстання функцый CRUD без неабяжнасці хоставаць API. Адзінольны режым оптымаўна падходзіць для спецыяльных полей, з’ѐеднанняў калькольнасця служб і падпісаў у стыле GraphQL. Тэксты кліента Apollo аднаковыя; розныя толькі права на кераванне схемай і правіламі.

Praktыка викорыстання Supabase-direct

Як гэта працюе

Пад час запуску pg_graphql аналізуе базу дадзеных PostgreSQL і стварае цэлысткі GraphQL API. Табелі ператвараюцца на типы Калекцый з функцыйямі CRUD, прыстосаванымі для з’ѐеднанняў у стыле Relay.

Канфігурацыя кліента Apollo (гэты проект)

// Two required headers — no custom backend token scheme
const authLink = setContext((_, { headers }) => ({
  headers: {
    ...headers,
    apikey: SUPABASE_ANON_KEY,       // identifies the Supabase project
    Authorization: `Bearer ${accessToken}`, // Supabase JWT for RLS
    "Content-Type": "application/json",
  },
}));
const httpLink = new HttpLink({
  uri: `${SUPABASE_URL}/graphql/v1`, // Supabase endpoint — no server to host
});

Формат запитаў

Калекцыі павінны адпавядаць шаблону з’ѐеднання:

query LoadStaff {
  staffCollection {        # ← always "Collection"
    edges {                # ← always "edges"
      node {               # ← always "node"
        id
        first_name
        role {             # ← relations via FK are auto-resolved
          role_name
        }
      }
    }
  }
}

Формат мутацый

mutation AddStaff($firstName: String!, $email: String!) {
  insertIntostaffCollection(         # ← prefix is always "insertInto"
    objects: { first_name: $firstName, email: $email }
  ) {
    records {
      id
    }
  }
}

Безпека ў базе дадзеных

Палітры RLS адначыя, хто можа бачыць калікве. «Бэкенд» — это PostgreSQL:

-- Only active staff can be read
CREATE POLICY "select active staff"
ON vision_expert.staff
FOR SELECT
USING (is_active = true AND auth_user_id = auth.uid());

Нават спецыяльна створаная запита GraphQL не можа абійсці RLS; фільтрацыя выконвуецца ўнутры Postgres.

Меры, якія існуюць пад час працы без посередніка

  1. Для спецыяльных поль, якіх нэмаў у базе дадзеных, патрэбны функцыяй калікве адзор вію.
  2. Для вырахоўваных/віртуальных поль патрэбны вырахоўваныя столбцы калікве адзор вію.
  3. Удоўжненне адзінага рэзалвера за дапамою запыту REST ад трэцьяго сторонніка є складным без Edge Functions.
  4. Багатыяпрыемнікавыя рабочыя процесы з адворачэнням змян выконвуецца ў функцыях базы дадзеных калікве на фронтэндзе.
  5. Назвы паўтараюцца за правіламі pg_graphql — знакі падкрэсла застаюцца, таму імёні на кшталт head_officeCollection застаюцца такімі.

Практычны выкарыстоўванне аддзельнага бэкенду

Як гэта працюе

Вы запускаеце Apollo Server (альбо аднакласныя сэрвісы), ствараеце схему і пісваеце рэзалверы, якія вызываюць Supabase admin, Prisma або SQL.

Канфігурацыя кліента Apollo

// Separate backend — your own auth token, your own endpoint
const authLink = setContext((_, { headers }) => ({
  headers: {
    ...headers,
    Authorization: `Bearer ${yourCustomJwt}`,
    "Content-Type": "application/json",
  },
}));
const httpLink = new HttpLink({
  uri: "https://api.yourapp.com/graphql",  // your server
});

Адзінаковая схема

# You define every type by hand
type Staff {
  id: ID!
  firstName: String!
  lastName: String!
  fullName: String!          # computed field — not in DB
  role: Role!
  branch: Branch!
  subordinates: [Staff!]!   # custom logic
}
type Query {
  staff(id: ID!): Staff
  staffByBranch(branchId: ID!): [Staff!]!
}type Mutation {
  addStaff(input: AddStaffInput!): StaffPayload!
  deactivateStaff(id: ID!): Boolean!
}

Рэзалверы

// server/resolvers/staffResolvers.js
const Query = {
  staffByBranch: async (_, { branchId }, { supabaseAdmin, currentUser }) => {
    // Business logic: only managers can list their own branch
    if (currentUser.role !== "manager" && currentUser.branchId !== branchId) {
      throw new ForbiddenError("Access denied");
    }
    // Fetch from Supabase using admin SDK (bypasses RLS)
    const { data, error } = await supabaseAdmin
      .from("staff")
      .select("*, branch(*), role(*)")
      .eq("branch_id", branchId);    return data;
  },
};const Staff = {
  // Computed field not in DB
  fullName: (parent) => `${parent.first_name} ${parent.last_name}`,
};

Шляхі SDK для адміністрацыі абходзяць RLS, таму автарызацыя павінна быць реалізаваная ў коде рэзалвера з строгім сяброўствам.

Дэтальны аналіз: што на самай працы разлічваецца

1. Прабег аутантыкацыі

Supabase-direct:

User logs in → supabase.auth.signInWithPassword()
             → Supabase returns JWT
             → JWT stored in session
             → Apollo authLink reads JWT from supabase.auth.getSession()
             → JWT sent in Authorization header with every GraphQL request
             → Supabase validates JWT and enforces RLS
// Token is read dynamically from the Supabase session
let accessToken = null;
supabase.auth.getSession().then(({ data: { session } }) => {
  accessToken = session?.access_token ?? null;
});
supabase.auth.onAuthStateChange((_event, session) => {
  accessToken = session?.access_token ?? null;
});

Адзінаковы бэкенд:

User logs in → POST /auth/login (your endpoint) or Supabase Auth
             → Your server returns a custom JWT
             → Frontend stores JWT
             → Apollo authLink reads JWT from localStorage / context
             → JWT sent to your server
             → Your server validates JWT in middleware
             → Resolver runs with the validated user context

У прямам режыме не патрэбна адменстрацыя токенаў — JWT ад Supabase выступае як доказ заходжання і кранты для базы дадзеных. Адзінаковы бэкенд додае ўзлёт, але дазволяе формаваць тэрміны дзейснасці і параметры заяваў.

2. Паўязка Apollo link

Supabase-direct:

// Two specific headers, one endpoint
const link = offlineMutationLink
  .concat(authLink)   // adds apikey + Bearer
  .concat(httpLink);  // → supabase.co/graphql/v1

Адзінаковы бэкенд:

// One standard auth header, your endpoint
const link = authLink    // adds your token
  .concat(httpLink);     // → api.yourapp.com/graphql

Тая ж сама ідея setContext + HttpLink. Разліки заключаюцца ў URL і тым, якія заголовкі є обав’язковымі.

3. Запиты і зміны

Supabase-direct — імены, які генеруюцца, є фіксаванымі:

# Table: staff → Query: staffCollection, Mutation: insertIntostaffCollection
query {
  staffCollection(filter: { is_active: { eq: true } }) {
    edges { node { id first_name } }
  }
}

Адзельны бэкенд — вы самі называеце все:

# You chose the name "activeStaff"
query {
  activeStaff {
    id
    fullName          # computed in resolver
    avatarUrl         # fetched from a CDN API in resolver
  }
}

Адзін створаны схема можа спроставіць вялікі структуры, з’еднаць службы і вырахаваць похідныя поля ў аднай вядомасі. У режыме Direct вяртаецца тое, што ўтримваюць таблицы.

4. Дзе знаходзяцца правіла бізнесу

Supabase-direct:

Business logic →  PostgreSQL functions, triggers, and RLS policies
                  OR handled in the React frontend (as in this project)

Прыклад — пераконтроль наявнасці дублікатав NIC/электронной пашты у фронтендзе перад дадзенням:

// Frontend handles the business rule: check for dup NIC/email before inserting
const { data: dupData } = await checkDuplicate({ variables: { nic, email } });
const nicExists = dupData?.nicCheck?.edges?.length > 0;
if (nicExists) {
  alert("A staff member with this NIC already exists.");
  return;
}
await addStaff({ variables: { ... } });

Адзельны бэкенд:

// Backend resolver handles the business rule
const addStaff = async (_, { input }, { db }) => {
  const existing = await db.staff.findFirst({
    where: { OR: [{ nic: input.nic }, { email: input.email }] }
  });
  if (existing) throw new UserInputError("NIC or email already exists");
  // Atomic transaction: create auth user + insert staff profile
  return await db.$transaction([
    supabaseAdmin.auth.admin.createUser({ email: input.email }),
    db.staff.create({ data: input }),
  ]);
};

Правіла бэкенду залишаюцца централізаванымі для кожнаго користувальця (веб, мобайль, партнеры). Правіла толькі для фронтенду розсіююцца і легка працягваюцца.

5. Модэль безпекі

У прымаму режыме сложней паспаліць настройкі, які моглы б спрычыніць нехтаўны вывар падаў, таму што Postgres контролюе доступ. Аддзельны бэкенд ёсць болей гнучкі, але больш залежыць ад рэтельнага выбору средстваў для разв’язвання прычын.

6. Падпісвання і рэальны час

Supabase-direct выкарыстоўвае каналы Supabase Realtime, а не падпісвання GraphQL:

// Supabase Realtime — separate from GraphQL
const channel = supabase
  .channel("staff-changes")
  .on("postgres_changes", { event: "*", schema: "vision_expert", table: "staff" },
    (payload) => { refetch(); }
  )
  .subscribe();

Гэты падход аддзельны ад Apollo; кліент GraphQL сам по сабе не отрымае паведамленняў у рэальны час.

Аддзельны бэкенд можа запростаць справжнія падпісвання GraphQL через WebSocket:

subscription {
  staffAdded {
    id
    first_name
    role { role_name }
  }
}
// Apollo Client with WebSocket link
import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
const wsLink = new GraphQLWsLink(
  createClient({ url: "wss://api.yourapp.com/graphql" })
);

7. Падтрымка паўнай самастойнасці

Обе стороны могу архіваваць змены, якія выкалічваюцца паўнай самастойнасцю, через Apollo Link. Vision Expert System робіць гэта ў прымаму режыме, выкарыстоўваючы RxDB як локальную канцэльеру.

Падход у сутнасі той самы:

Offline mutation → ApolloLink intercepts → stores in local DB → syncs on reconnect

Адкантэнацыя токена пасля перыякнуцья зазвычай ўсё простае, калі вы самі кантролюеце жыцёвы цикл токена. У Supabase вам неабходна паўтарна перакананасць, што JWT застаецца чынным, прытымкі практычна не трэба.

Калі падходзіць Supabase-direct

Выбірайте яго, калі:

  • Работа з дадзенням майже выключна CRUD і адбываецца за дапамою табелей
  • Вы хочаце запускань без адпаведнага хоста API
  • Команда мала, а глыбіна бэкенду обмежана
  • Логіка падходзіць пад функцыі PostgreSQL і RLS
  • Існуе фактычна адны корыстнік фронтэнда
  • Вы хочаце мінімальныя витраты на хостынг для шара API
  • Запиты залишаюцца сфокусаванымі на табелях без з’еднанняў межа служб

Гэта падходзіць для Vision Expert System: внутраняго інструмента з калькама ролей, усі даны ў Supabase, RLS па ролях і відгалужэннях, адны фронтэнд.

Калі патрэбны околачы бэкенд

Дадзіце яго, калі:

  • Вам патрэбны вычыслены/вяртуальныя поля, якіх няма ў БД
  • Адпаведны адказ GraphQL павінен агрэгаваць даны з Supabase, а таксама з сервісамі платежаў, CRM чы іншымі
  • Багатоэтапныя транзакцыі павінны заставацца атамарнымі ўсередзіне розных таблиц і сервісаў
  • Зовнішнія разработчыкі будуць выкарыстоўваць публічны API
  • Вам патрэбны падпісы GraphQL як спосаб прыманення данных у рэальны час
  • Бізнес-правілы занадта складныя для реалізаціі за дапамогою SQL
  • GraphQL становіцься шлюзам для федэрацыі ў структуре мікросервісаў
  • Вам патрэбны ліміты частоты запытоў на рэвэле, пераверкі чы журналы аудыту на рэвэле
  • Сгенераваныя імены pg_graphql є непрыемнымі і не можна іх выправіць

Гібрыдны падход

Багатыя продакшн-проекты выкарыстоўваюць аба падходы разам:

  • Прямы GraphQL для звычных запытоў на чытанне і запіс
  • Supabase Edge Functions для тых 10% сцэнарыяў, дзе ён не падходзіць: адмінскі SDK (безпечны спосаб айханаў RLS), запыты да сторонніх сервісаў, багатоэтапныя процесы
Frontend Apollo Client
    │
    ├── /graphql/v1  →  Supabase pg_graphql  (standard CRUD)
    │
    └── /functions/complex-operation  →  Supabase Edge Function (business logic)
                                          → calls Supabase admin SDK internally

Вы застаўляеце шырокі спектар трафіку на режым працы без адстаўкі, які забезпечвае высокую швальнасць і мінімальную падтрымку, а гнучкасць, прыемную для бэкенду, застаўляеце там, дзе яна практычна неабходная — без неабяжнай наладкі цэлага сервера, які працавае ўвесь час для всего.