Přejít na hlavní obsah

Veřejné prostředky (složka public/)

Složka public/ v kořenu vaší aplikace obsahuje statické soubory — obrázky, ikony, písma a další prostředky, které vaše aplikace potřebuje za běhu. Tyto soubory jsou automaticky zahrnuty do buildů, synchronizovány během vývojového režimu a nahrávány na server. Soubory umístěné v public/ jsou:
  • Veřejně přístupné — po synchronizaci na server jsou prostředky dostupné na veřejné URL. K přístupu k nim není potřeba žádná autentizace.
  • Dostupné ve frontendových komponentách — použijte URL prostředků k zobrazení obrázků, ikon či jiných médií uvnitř komponent Reactu.
  • Dostupné v logických funkcích — odkazujte na URL prostředků v e-mailech, odpovědích API či jiné serverové logice.
  • Používány pro metadata Marketplace — pole logoUrl a screenshots v defineApplication() odkazují na soubory z této složky (např. public/logo.png). Tyto se zobrazují v Marketplace, když je vaše aplikace zveřejněna.
  • Automaticky synchronizované ve vývojovém režimu — když v public/ přidáte, aktualizujete nebo smažete soubor, je automaticky synchronizován na server. Není potřeba restart.
  • Zahrnuté do buildůyarn twenty build zabalí všechny veřejné prostředky do distribučního výstupu.

Přístup k veřejným prostředkům pomocí getPublicAssetUrl

K získání plné URL souboru ve vaší složce public/ použijte pomocnou funkci getPublicAssetUrl z twenty-sdk. Funguje jak v logických funkcích, tak ve frontendových komponentách. V logické funkci:
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,
});
Ve frontendové komponentě:
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" />;
});
Argument path je relativní ke složce public/ vaší aplikace. Jak getPublicAssetUrl('logo.png'), tak getPublicAssetUrl('public/logo.png') se vyhodnotí na stejnou URL — předpona public/ je, je-li přítomna, automaticky odstraněna.

Používání balíčků npm

Ve své aplikaci můžete nainstalovat a používat libovolný balíček npm. Logické funkce i frontendové komponenty se bundlují pomocí esbuild, který vloží všechny závislosti přímo do výstupu — za běhu nejsou potřeba žádné node_modules.

Instalace balíčku

yarn add axios
Poté jej importujte ve svém kódu:
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,
});
Stejně to funguje i pro frontendové komponenty:
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,
});

Jak funguje bundlování

Krok sestavení používá esbuild k vytvoření jediného samostatného souboru pro každou logickou funkci a každou frontendovou komponentu. Všechny importované balíčky jsou vloženy přímo do bundlu. Logické funkce běží v prostředí Node.js. Vestavěné moduly Node (fs, path, crypto, http atd.) jsou k dispozici a není je třeba instalovat. Frontendové komponenty běží ve Web Workeru. Vestavěné moduly Node nejsou k dispozici — pouze prohlížečová API a balíčky npm, které fungují v prohlížečovém prostředí. V obou prostředích jsou jako předpřipravené moduly k dispozici twenty-client-sdk/core a twenty-client-sdk/metadata — nejsou součástí bundlu, ale server je za běhu načítá.

Testování vaší aplikace

SDK poskytuje programová rozhraní, která vám umožní z testovacího kódu aplikaci sestavit, nasadit, nainstalovat a odinstalovat. V kombinaci s Vitest a typovanými klienty API můžete psát integrační testy, které ověří, že vaše aplikace funguje end-to-end proti reálnému serveru Twenty.

Nastavení

Vygenerovaná aplikace již obsahuje Vitest. Pokud to nastavujete ručně, nainstalujte závislosti:
yarn add -D vitest vite-tsconfig-paths
Vytvořte vitest.config.ts v kořeni vaší aplikace:
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',
    },
  },
});
Vytvořte soubor nastavení, který před spuštěním testů ověří dostupnost serveru:
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),
  );
});

Programová rozhraní SDK

Subcesta twenty-sdk/cli exportuje funkce, které můžete volat přímo z testovacího kódu:
FunkcePopis
appBuildSestaví aplikaci a volitelně zabalí tarball
appDeployNahraje tarball na server
appInstallNainstaluje aplikaci do aktivního pracovního prostoru
appUninstallOdinstaluje aplikaci z aktivního pracovního prostoru
Každá funkce vrací objekt výsledku se success: boolean a buď data, nebo error.

Psání integračního testu

Zde je kompletní příklad, který aplikaci sestaví, nasadí a nainstaluje a poté ověří, že se objeví v pracovním prostoru:
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();
  });
});

Spuštění testů

Ujistěte se, že běží váš lokální server Twenty, a poté:
yarn test
Nebo v režimu watch během vývoje:
yarn test:watch

Kontrola typů

Kontrolu typů můžete spustit i na vaší aplikaci bez spuštění testů:
yarn twenty typecheck
Spustí se tsc --noEmit a nahlásí se případné chyby typů.

Referenční dokumentace CLI

Kromě dev, build, add a typecheck poskytuje CLI příkazy pro spouštění funkcí, zobrazení logů a správu instalací aplikací.

Spouštění funkcí (yarn twenty exec)

Spusťte logickou funkci ručně bez vyvolání přes HTTP, cron nebo databázovou událost:
# 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

Zobrazení logů funkcí (yarn twenty logs)

Streamujte výstupní logy běhu logických funkcí vaší aplikace:
# 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
To je jiné než yarn twenty server logs, které zobrazují logy kontejneru Docker. yarn twenty logs zobrazuje logy spuštění funkcí vaší aplikace ze serveru Twenty.

Odinstalace aplikace (yarn twenty uninstall)

Odeberte svou aplikaci z aktivního pracovního prostoru:
yarn twenty uninstall

# Skip the confirmation prompt
yarn twenty uninstall --yes

Správa vzdálených serverů

Remote je server Twenty, ke kterému se vaše aplikace připojuje. Během nastavení jej generátor kostry automaticky vytvoří. Můžete kdykoli přidat další vzdálené servery nebo mezi nimi přepínat.
# 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>
Vaše přihlašovací údaje jsou uloženy v ~/.twenty/config.json.

CI s GitHub Actions

Generátor kostry vytvoří připravený k použití workflow GitHub Actions v .github/workflows/ci.yml. Automaticky spouští integrační testy při každém pushi do main a u pull requestů. Workflow:
  1. Načte váš kód (checkout).
  2. Spustí dočasný server Twenty pomocí akce twentyhq/twenty/.github/actions/spawn-twenty-docker-image
  3. Nainstaluje závislosti pomocí yarn install --immutable
  4. Spustí yarn test s proměnnými TWENTY_API_URL a TWENTY_API_KEY vloženými z výstupů akce
.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 }}
Není potřeba konfigurovat žádné secrets — akce spawn-twenty-docker-image spustí efemérní server Twenty přímo v runneru a vypíše podrobnosti připojení. Secret GITHUB_TOKEN je poskytován GitHubem automaticky. Chcete-li připnout konkrétní verzi Twenty místo latest, změňte proměnnou prostředí TWENTY_VERSION na začátku workflow.