API dostawcze GraphQL

Jakie zapytania istnieją, jak działa scoping workspace'u i które odczyty opakowuje SDK, a które piszesz sam.

cmssy serwuje opublikowaną treść przez jeden endpoint GraphQL. SDK opakowuje już typowe odczyty - strony, layouty, konfigurację serwisu, formularze - więc większość aplikacji nigdy nie pisze zapytania. Do reszty (własne modele, rekordy, listowanie stron potomnych) wysyłasz własne przez klienta dostawczego.

Endpoint i scoping

Publiczne odczyty idą na ścieżkę z organizacją:

{apiBase}/public/{orgSlug}/{workspaceSlug}/graphql

apiBase to Twoje apiUrl z obciętym końcowym /graphql - domyślnie https://api.cmssy.io, więc żądania lądują na https://api.cmssy.io/public/{org}/{ws}/graphql. Nadpisuj apiUrl tylko przy self-hoście. org i workspaceSlug pochodzą z Twojej konfiguracji, a SDK składa ścieżkę za Ciebie.

Ponieważ organizacja siedzi w ścieżce, slug workspace'u musi być unikalny tylko w obrębie swojej organizacji.

Dwa sposoby ograniczenia zapytania

Każda operacja jest ograniczona do workspace'u, ale nie wszystkie tak samo. To ten szczegół, na którym ludzie się wykładają:

  • workspaceSlug (String!) - używany przez odczyty stron, layoutów, konfiguracji i formularzy. Helpery SDK podają go automatycznie z Twojej konfiguracji.
  • workspaceId (String!) - używany przez odczyty modeli, rekordów i stron po typie. Wywołaj client.queryScoped(...): gdy Twoje zapytanie deklaruje $workspaceId, a Ty go nie podasz, SDK rozwiąże je z workspaceSlug i wstrzyknąć zarówno zmienną, jak i nagłówek x-workspace-id.
// $workspaceId zostaje uzupełnione za Ciebie
await client.queryScoped(MY_QUERY, { modelSlug: "products", limit: 20 });

Co SDK czyta za Ciebie

Normalnie nigdy tego nie piszesz - createCmssyPage, CmssyServerLayout i rozwiązywanie formularzy stojące za context.forms wysyłają te zapytania wewnętrznie.

  • public.page.get - jedna strona po slugu: { id, blocks, publishedBlocks } plus pola SEO seoTitle, seoDescription, seoKeywords, displayName.
  • public.page.getById - { id, publishedBlocks }.
  • public.page.list - [{ id, slug, updatedAt, publishedAt }].
  • public.page.layouts - [{ position, blocks, settings }].
  • public.siteConfig - nazwa serwisu, język domyślny i włączone języki, funkcje, branding.
  • public.form.get - pola i ustawienia formularza, podawane blokom jako context.forms.
  • public.form.submit - { success, message, submissionId, redirectUrl }.

Helpery, które je opakowują - fetchPage, fetchPages, fetchLayouts, fetchPageMeta, fetchSiteConfig, resolveSiteLocales, resolveForms - żyją pod @cmssy/core/internal i nie są publicznym API. Publiczna powierzchnia to createCmssyClient i graphqlRequest: czegokolwiek createCmssyPage już nie wyrenderuje, pytasz sam.

Te napiszesz sam

Nie mają helpera w SDK. Wyślij je przez client.queryScoped(...).

Rekordy własnych modeli

query PublicModelRecords(
  $workspaceId: String!
  $modelSlug: String!
  $filter: JSON
  $sort: String
  $limit: Int
  $offset: Int
  $populate: [String!]
) {
  public {
    model {
      records(
        workspaceId: $workspaceId
        modelSlug: $modelSlug
        filter: $filter
        sort: $sort
        limit: $limit
        offset: $offset
        populate: $populate
      ) {
        items { id modelId data status createdAt updatedAt }
        total
        hasMore
      }
    }
  }
}

Napisz zapytanie sam i trzymaj je w swoim repo. SDK ma odpowiedniki tych stringów, ale pod @cmssy/core/internal - to podscieżka wewnętrzna, co znaczy, że może się zmienić między wydaniami bez adnotacji o breaking change. Twoje własne zapytanie ma cztery linijki i nigdy Cię nie zaskoczy.

Listowanie stron potomnych

To jest to, co napędza indeks bloga albo drzewo docsów:

query PublicPagesByType(
  $workspaceId: String!
  $parentSlug: String
  $search: String
  $limit: Int
  $offset: Int
) {
  public {
    page {
      byType(
        workspaceId: $workspaceId
        parentSlug: $parentSlug
        search: $search
        limit: $limit
        offset: $offset
      ) {
        items {
          id
          slug
          fullSlug
          publishedAt
          displayName
          seoTitle
          seoDescription
          customFields
          pageType
        }
        total
        hasMore
      }
    }
  }
}

byType przyjmuje też pageType, sortBy i customFieldFilters, a przy prawidłowym previewSecret - includeDrafts.

Wysłanie formularza

mutation SubmitForm($formId: ID!, $input: SubmitFormInput!) {
  public {
    form {
      submit(formId: $formId, input: $input) {
        success
        message
        submissionId
        redirectUrl
      }
    }
  }
}

Przekaż { formId, input: { data } }. SDK niesie ten sam string jako SUBMIT_FORM_MUTATION, ale pod @cmssy/core/internal - trzymaj własną kopię, zamiast go importować.

Autoryzację członków montujesz sam

Mutacje siteMember - login, register, refresh, logout, forgotPassword, resetPassword, verifyEmail - stoją za przepływem logowania członków.

Slim SDK nie dostarcza ani trasy auth, ani czytnika sesji. login i refresh zwracają surowe accessToken i refreshToken, a backend nie ustawia żadnego ciasteczka - sesja należy do Twojej aplikacji. Bezpieczny domyślny wybór to ciasteczko httpOnly zapisywane przez Twój własny route handler. Cały przepływ, od rejestracji po weryfikację e-maila, opisuje autoryzacja członków.

Warto wiedzieć

  • data i filter używają skalara JSON - przekazuj zwykłe obiekty, nie JSON jako string.
  • customFields na stronie to mapa JSON pól własnych danego typu strony.
  • Odczyty zwracają wyłącznie treść opublikowaną, chyba że podano prawidłowy previewSecret. SDK robi to za Ciebie w trybie edycji, używając Twojego draftSecret.

Następne kroki