Ana içeriğe atla

Genel varlıklar (public/ klasörü)

Uygulamanızın kökündeki public/ klasörü, statik dosyaları barındırır — görseller, simgeler, yazı tipleri veya uygulamanızın çalışma zamanında ihtiyaç duyduğu diğer varlıklar. Bu dosyalar derlemelere otomatik olarak dahil edilir, geliştirme modunda senkronize edilir ve sunucuya yüklenir. public/ içine yerleştirilen dosyalar şunlardır:
  • Herkese açık olarak erişilebilir — sunucuya senkronize edildikten sonra varlıklar genel bir URL’den sunulur. Onlara erişmek için kimlik doğrulama gerekmez.
  • Ön uç bileşenlerinde kullanılabilir — React bileşenlerinizin içinde görseller, simgeler veya herhangi bir medyayı göstermek için varlık URL’lerini kullanın.
  • Mantık işlevlerinde kullanılabilir — e-postalarda, API yanıtlarında veya herhangi bir sunucu tarafı mantıkta varlık URL’lerine referans verin.
  • Pazar yeri üst verileri için kullanılırdefineApplication() içindeki logoUrl ve screenshots alanları bu klasördeki dosyalara referans verir (örn. public/logo.png). Bunlar, uygulamanız yayımlandığında pazar yerinde görüntülenir.
  • Geliştirme modunda otomatik senkronize edilirpublic/ içinde bir dosya eklediğinizde, güncellediğinizde veya sildiğinizde otomatik olarak sunucuya senkronize edilir. Yeniden başlatma gerekmez.
  • Derlemelere dahil ediliryarn twenty build, tüm genel varlıkları dağıtım çıktısına paketler.

getPublicAssetUrl ile genel varlıklara erişme

twenty-sdk içindeki getPublicAssetUrl yardımcı işlevini kullanarak public/ dizininizdeki bir dosyanın tam URL’sini alın. Hem mantık işlevlerinde hem de ön uç bileşenlerinde çalışır. Bir mantık işlevinde:
src/logic-functions/send-invoice.ts
import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define';

const handler = async (): Promise<any> => {
  const logoUrl = getPublicAssetUrl('logo.png');
  const invoiceUrl = getPublicAssetUrl('templates/invoice.png');

  // Fetch the file content (no auth required — public endpoint)
  const response = await fetch(invoiceUrl);
  const buffer = await response.arrayBuffer();

  return { logoUrl, size: buffer.byteLength };
};

export default defineLogicFunction({
  universalIdentifier: 'a1b2c3d4-...',
  name: 'send-invoice',
  description: 'Sends an invoice with the app logo',
  timeoutSeconds: 10,
  handler,
});
Bir ön uç bileşeninde:
src/front-components/company-card.tsx
import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define';

export default defineFrontComponent(() => {
  const logoUrl = getPublicAssetUrl('logo.png');

  return <img src={logoUrl} alt="App logo" />;
});
path bağımsız değişkeni, uygulamanızın public/ klasörüne göre görelidir. Hem getPublicAssetUrl('logo.png') hem de getPublicAssetUrl('public/logo.png') aynı URL’ye çözümlenir — public/ öneki varsa otomatik olarak kaldırılır.

npm paketlerini kullanma

Uygulamanızda herhangi bir npm paketini yükleyip kullanabilirsiniz. Hem mantık işlevleri hem de ön uç bileşenleri, tüm bağımlılıkları çıktıya satır içi olarak ekleyen esbuild ile paketlenir — çalışma zamanında node_modules gerekmez.

Bir paketi yükleme

yarn add axios
Ardından kodunuza içe aktarın:
src/logic-functions/fetch-data.ts
import { defineLogicFunction } from 'twenty-sdk/define';
import axios from 'axios';

const handler = async (): Promise<any> => {
  const { data } = await axios.get('https://api.example.com/data');

  return { data };
};

export default defineLogicFunction({
  universalIdentifier: '...',
  name: 'fetch-data',
  description: 'Fetches data from an external API',
  timeoutSeconds: 10,
  handler,
});
Aynısı ön uç bileşenleri için de geçerlidir:
src/front-components/chart.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
import { format } from 'date-fns';

const DateWidget = () => {
  return <p>Today is {format(new Date(), 'MMMM do, yyyy')}</p>;
};

export default defineFrontComponent({
  universalIdentifier: '...',
  name: 'date-widget',
  component: DateWidget,
});

Paketleme nasıl çalışır

Derleme adımı, her mantık işlevi ve her ön uç bileşeni için tek bir bağımsız dosya üretmek üzere esbuild kullanır. Tüm içe aktarılan paketler pakete satır içi eklenir. Mantık işlevleri, Node.js ortamında çalışır. Node yerleşik modülleri (fs, path, crypto, http vb.) kullanılabilir ve kurulmaları gerekmez. Ön uç bileşenleri, bir Web Worker içinde çalışır. Node’un yerleşik modülleri kullanılamaz — yalnızca tarayıcı ortamında çalışan tarayıcı API’leri ve npm paketleri kullanılabilir. Her iki ortamda da twenty-client-sdk/core ve twenty-client-sdk/metadata önceden sağlanmış modüller olarak mevcuttur — bunlar paketlenmez, ancak çalışma zamanında sunucu tarafından çözülür.

Uygulamanızı test etme

SDK, test kodundan uygulamanızı derlemenize, dağıtmanıza, yüklemenize ve kaldırmanıza olanak tanıyan programatik API’ler sağlar. Tiplenmiş API istemcileriyle birlikte Vitest kullanarak, uygulamanızın gerçek bir Twenty sunucusunda uçtan uca çalıştığını doğrulayan entegrasyon testleri yazabilirsiniz.

Kurulum

İskelet aracıyla oluşturulan uygulama zaten Vitest’i içerir. Manuel kurulum yaparsanız, bağımlılıkları yükleyin:
yarn add -D vitest vite-tsconfig-paths
Uygulamanızın kök dizininde bir vitest.config.ts oluşturun:
vitest.config.ts
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig } from 'vitest/config';

export default defineConfig({
  plugins: [
    tsconfigPaths({
      projects: ['tsconfig.spec.json'],
      ignoreConfigErrors: true,
    }),
  ],
  test: {
    testTimeout: 120_000,
    hookTimeout: 120_000,
    include: ['src/**/*.integration-test.ts'],
    setupFiles: ['src/__tests__/setup-test.ts'],
    env: {
      TWENTY_API_URL: 'http://localhost:2020',
      TWENTY_API_KEY: 'your-api-key',
    },
  },
});
Testler çalışmadan önce sunucuya erişilebildiğini doğrulayan bir kurulum dosyası oluşturun:
src/__tests__/setup-test.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { beforeAll } from 'vitest';

const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');

beforeAll(async () => {
  // Verify the server is running
  const response = await fetch(`${TWENTY_API_URL}/healthz`);

  if (!response.ok) {
    throw new Error(
      `Twenty server is not reachable at ${TWENTY_API_URL}. ` +
        'Start the server before running integration tests.',
    );
  }

  // Write a temporary config for the SDK
  fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });

  fs.writeFileSync(
    path.join(TEST_CONFIG_DIR, 'config.json'),
    JSON.stringify({
      remotes: {
        local: {
          apiUrl: process.env.TWENTY_API_URL,
          apiKey: process.env.TWENTY_API_KEY,
        },
      },
      defaultRemote: 'local',
    }, null, 2),
  );
});

Programatik SDK API’leri

twenty-sdk/cli alt yolu, test kodundan doğrudan çağırabileceğiniz fonksiyonları dışa aktarır:
FonksiyonAçıklama
appBuildUygulamayı derleyin ve isteğe bağlı olarak bir tarball paketleyin
appDeployBir tarball’ı sunucuya yükleyin
appInstallUygulamayı etkin çalışma alanına yükleyin
appUninstallUygulamayı etkin çalışma alanından kaldırın
Her fonksiyon, success: boolean ile birlikte data veya error içeren bir sonuç nesnesi döndürür.

Bir entegrasyon testi yazma

İşte uygulamayı derleyen, dağıtan ve yükleyen; ardından çalışma alanında göründüğünü doğrulayan tam bir örnek:
src/__tests__/app-install.integration-test.ts
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const APP_PATH = process.cwd();

describe('App installation', () => {
  beforeAll(async () => {
    const buildResult = await appBuild({
      appPath: APP_PATH,
      tarball: true,
      onProgress: (message: string) => console.log(`[build] ${message}`),
    });

    if (!buildResult.success) {
      throw new Error(`Build failed: ${buildResult.error?.message}`);
    }

    const deployResult = await appDeploy({
      tarballPath: buildResult.data.tarballPath!,
      onProgress: (message: string) => console.log(`[deploy] ${message}`),
    });

    if (!deployResult.success) {
      throw new Error(`Deploy failed: ${deployResult.error?.message}`);
    }

    const installResult = await appInstall({ appPath: APP_PATH });

    if (!installResult.success) {
      throw new Error(`Install failed: ${installResult.error?.message}`);
    }
  });

  afterAll(async () => {
    await appUninstall({ appPath: APP_PATH });
  });

  it('should find the installed app in the workspace', async () => {
    const metadataClient = new MetadataApiClient();

    const result = await metadataClient.query({
      findManyApplications: {
        id: true,
        name: true,
        universalIdentifier: true,
      },
    });

    const installedApp = result.findManyApplications.find(
      (app: { universalIdentifier: string }) =>
        app.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
    );

    expect(installedApp).toBeDefined();
  });
});

Testleri çalıştırma

Yerel Twenty sunucunuzun çalıştığından emin olun, ardından:
yarn test
Veya geliştirme sırasında izleme modunda:
yarn test:watch

Tip denetimi

Ayrıca testleri çalıştırmadan uygulamanızda tip denetimi çalıştırabilirsiniz:
yarn twenty typecheck
Bu, tsc --noEmit komutunu çalıştırır ve tüm tip hatalarını raporlar.

CLI başvurusu

dev, build, add ve typecheck dışında CLI, fonksiyonları çalıştırma, günlükleri görüntüleme ve uygulama kurulumlarını yönetme komutları sağlar.

Fonksiyonları çalıştırma (yarn twenty exec)

Bir mantık fonksiyonunu HTTP, cron veya veritabanı olayıyla tetiklemeden manuel olarak çalıştırın:
# Execute by function name
yarn twenty exec -n create-new-post-card

# Execute by universalIdentifier
yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf

# Pass a JSON payload
yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}'

# Execute the post-install function
yarn twenty exec --postInstall

Fonksiyon günlüklerini görüntüleme (yarn twenty logs)

Uygulamanızın mantık fonksiyonlarının yürütme günlüklerini akış olarak alın:
# Stream all function logs
yarn twenty logs

# Filter by function name
yarn twenty logs -n create-new-post-card

# Filter by universalIdentifier
yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
Bu, Docker konteyner günlüklerini gösteren yarn twenty server logs komutundan farklıdır. yarn twenty logs, uygulamanızın fonksiyon yürütme günlüklerini Twenty sunucusundan gösterir.

Bir uygulamayı kaldırma (yarn twenty uninstall)

Uygulamanızı etkin çalışma alanından kaldırın:
yarn twenty uninstall

# Skip the confirmation prompt
yarn twenty uninstall --yes

Uzakları yönetme

Bir uzak, uygulamanızın bağlandığı Twenty sunucusudur. Kurulum sırasında iskelet oluşturucu sizin için otomatik olarak bir tane oluşturur. Dilediğiniz zaman daha fazla uzak ekleyebilir veya aralarında geçiş yapabilirsiniz.
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote add

# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote add --local

# Add a remote non-interactively (useful for CI)
yarn twenty remote add --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote

# List all configured remotes
yarn twenty remote list

# Switch the active remote
yarn twenty remote switch <name>
Kimlik bilgileriniz ~/.twenty/config.json içinde saklanır.

GitHub Actions ile CI

İskelet oluşturucu, .github/workflows/ci.yml konumunda kullanıma hazır bir GitHub Actions iş akışı üretir. Entegrasyon testlerinizi main dalına yapılan her itmede ve çekme isteklerinde otomatik olarak çalıştırır. İş akışı:
  1. Kodunuzu çalışma alanına alır
  2. twentyhq/twenty/.github/actions/spawn-twenty-docker-image eylemini kullanarak geçici bir Twenty sunucusu başlatır
  3. yarn install --immutable ile bağımlılıkları kurar
  4. Eylem çıktılarından enjekte edilen TWENTY_API_URL ve TWENTY_API_KEY ile yarn test çalıştırır
.github/workflows/ci.yml
name: CI

on:
  push:
    branches:
      - main
  pull_request: {}

env:
  TWENTY_VERSION: latest

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Spawn Twenty instance
        id: twenty
        uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
        with:
          twenty-version: ${{ env.TWENTY_VERSION }}
          github-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Enable Corepack
        run: corepack enable

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'
          cache: 'yarn'

      - name: Install dependencies
        run: yarn install --immutable

      - name: Run integration tests
        run: yarn test
        env:
          TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
          TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
Herhangi bir gizli değişken yapılandırmanız gerekmez — spawn-twenty-docker-image eylemi, koşucu içinde doğrudan geçici bir Twenty sunucusu başlatır ve bağlantı ayrıntılarını çıktı olarak verir. GITHUB_TOKEN gizli değişkeni GitHub tarafından otomatik olarak sağlanır. latest yerine belirli bir Twenty sürümünü sabitlemek için iş akışının başındaki TWENTY_VERSION ortam değişkenini değiştirin.