# API İstemcisi (TecofApiClient)

> TecofApiClient başvurusu — kurucu, kimlik başlıkları, sayfa/medya/çeviri/CMS/ortak bileşen/e-ticaret metotları, kullandıkları /api/store ve /api/merchant uçları ve hata modeli.
>
> Kaynak: https://tecof-app-dev.vercel.app/editor-api-client · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20

`TecofApiClient`, editörün ve alanların Tecof backend’iyle konuştuğu istemcidir. `TecofProvider` bir örneğini oluşturup bağlama koyar (`useTecof().apiClient`); sunucu tarafında doğrudan da oluşturabilirsiniz.

```ts
import { TecofApiClient } from "@tecof/theme-editor";

const client = new TecofApiClient(
  process.env.NEXT_PUBLIC_BASE_URL!,             // apiUrl (sondaki / temizlenir)
  process.env.NEXT_PUBLIC_MERCHANT_SECRET_KEY!,  // secretKey → x-secret-key
  process.env.NEXT_PUBLIC_CDN_URL,               // cdnUrl (verilmezse apiUrl)
  undefined,                                     // accessToken (JWT, yazma uçları için)
  process.env.NEXT_PUBLIC_THEME_ID,              // themeId
);

const page = await client.getPublishedPage("anasayfa", "tr");
if (page.success && page.data) console.log(page.data.title);
```

## Kimlik

Her istek `x-secret-key` başlığını taşır. Vitrin anahtarı tarayıcı paketine girdiği için backend yazma uçlarında (sayfa kaydetme, yükleme, ortak bileşen, stil senkronu) ayrıca kullanıcı JWT’si ister: `setAccessToken(token)` ya da kurucunun 4. argümanı ile verilir ve `Authorization` başlığına yazılır. Editörde bu token `TecofEditor`’ün `accessToken` prop’undan gelir.

`themeId` verilirse sayfa okuma, kaydetme ve listeleme istekleri o temaya gider; verilmezse backend mağazanın **aktif** temasına düşer (çok temalı mağazada yanlış temanın sayfaları gelir).

## Metotlar

| Metot | Uç | Açıklama |
| :--- | :--- | :--- |
| `getPage(id, signal?, revisionId?)` | `GET /api/store/editor/:id` | Editör taslağı; `revisionId` ile revizyon anlık görüntüsü |
| `savePage(id, draftData, title?, accessToken?)` | `PUT /api/store/editor/:id` | Taslağı kaydeder (`{ draftData, title?, themeId? }`) |
| `getPublishedPage(slug, locale?)` | `POST /api/store/render` | Yayınlanmış sayfa |
| `getMerchantInfo()` | `GET /api/store/merchant-info` | Diller, varsayılan dil, ürün tipi, Powered by bandı, askı/yapım bayrakları |
| `uploadFile(file, folder?)` | `POST /api/store/upload` | Çok parçalı yükleme (alan adı `files`) |
| `getUploads(page = 1, limit = 50)` | `GET /api/store/uploads` | Medya kütüphanesi |
| `getPages()` | `GET /api/store/pages` | Bağlantı seçici için sayfa listesi (`_id, slug, title, status, metaTitle`) |
| `translate(text, sourceLang, locales, isHtml = false)` | `POST /api/store/translate` | Çoklu dil çevirisi → `[{ code, value }]` |
| `searchStockMedia(query, { provider?, page?, perPage?, orientation? })` | `GET /api/store/stock-media/search` | Stok görsel arama |
| `importStockMedia({ provider, downloadUrl, id?, alt? })` | `POST /api/store/stock-media/import` | Stok görseli kütüphaneye alır |
| `getCmsCollections()` | `POST /api/store/cms/collections` | Koleksiyonlar |
| `getCmsCollectionItems(slug, { page, limit, sort, locale, filters, sortBy, sortDir })` | `POST /api/store/cms/collections/:slug/items` | Canlı öğeler → `{ items, totalData }` |
| `getSharedComponents()` · `createSharedComponent(name, type, props)` · `updateSharedComponent(id, name, type, props)` · `deleteSharedComponent(id)` | `/api/store/shared-components` | Ortak bileşenler |
| `previewApplyStyles(payload)` · `applyStylesToPages(payload)` | `POST /api/store/editor/apply-styles` | Sayfalar arası stil (önizleme / uygulama) |
| `getComponentPreview(domain, componentName)` | `POST /api/store/component-preview` | Bileşen önizleme görseli (blob URL, önbellekli) |
| `getEcommerceCategories(locale)` · `getEcommerceBrands(locale)` · `getEcommerceProducts(…)` | `GET /api/store/ecommerce/*` | Vitrin e-ticaret listeleri |
| `getEcommerceTags` · `getEcommerceAttributes` · `getEcommerceVariantTypes` · `getEcommerceFlashSales` · `getEcommerceCampaigns` · `getEcommerceDiscounts` | `GET /api/merchant/ecommerce/*` | Panel listeleri (JWT + `x-merchant-id`; kimlik `merchant-info`’dan bir kez çözülür) |
| `setAccessToken(token?)` · `getAccessToken()` | — | JWT yönetimi |
| `setMerchantId(id?)` | — | Panel uçları için mağaza kimliğini elle verir |
| `getRealtimeConfig()` | — | Canlı katılım soketi için `{ url, token }` |
| `cdnUrl` | — | Medya adresleri için CDN kökü |

## Hata modeli

Metotların çoğu ağ hatasını fırlatmak yerine `{ success: false, message }` zarfına çevirir; başarılı yanıt `{ success: true, data }` biçimindedir:

```ts
const res = await client.savePage(pageId, draftData);
if (!res.success) {
  console.error(res.message);
}
```

İstisna: `getPage` iptal edilen istekleri ayırt edebilmeniz için `AbortError`’ı **yeniden fırlatır** (yeni yükleme eskisini geçersiz kıldığında eski sonucu yok saymak için):

```ts
const controller = new AbortController();
try {
  const res = await client.getPage(pageId, controller.signal);
} catch (error) {
  if (error instanceof DOMException && error.name === "AbortError") return; // iptal, hata değil
  throw error;
}
```

## Pratik notlar

- Yükleme isteğinde `Content-Type`’ı elle vermeyin; çok parçalı sınırı tarayıcı ekler.
- Backend’in `x-secret-key` ve `Authorization` başlıklarına CORS izni verdiğinden emin olun.
- CDN adresi kuran kodda dosyanın `folder` alanını hesaba katın; klasörsüz `${cdnUrl}/${name}` kapsamlı klasördeki dosyada 404 verir. `TecofPicture` bunu sizin yerinize yapar.
