Mantık işlevleri, kısa ömürlü, izole Node.js süreçlerinde çalışır — bir çalıştırma tamamlandıktan sonra, bellekte tutulan hiçbir şey kalıcı olmaz. Çalıştırmalar arasında bir şeyi hatırlamanız gerektiğinde (maliyetli bir API yanıtını önbelleğe almak, artımlı eşitlemeler için bir imleç saklamak, işleri ertelemek ya da durumu bir işlevden diğerine aktarmak için), bunu çalışma alanı veritabanında kalıcı hale getirin.
Bunun için özel bir depolama ilkeline ihtiyacınız yok: key alanı ve value alanı olan küçük bir teknik nesne, çalışma alanıyla sınırlı, kalıcı bir anahtar-değer deposu sağlar ve kayıtlar için zaten kullandığınız aynı typed API client üzerinden sorgulanabilir.
┌─────────────────┐ set(key, value) ┌──────────────────────────┐
│ Logic function │ ───────────────────▶ │ "KV Store" object │
│ (your handler) │ ◀─────────────────── │ key (unique) │ value │
└─────────────────┘ get(key) └──────────────────────────┘
Depo nesnesini tanımlayın
İki alanı olan özel bir nesne tanımlayın — key (benzersiz bir TEXT) ve value (her türlü JSON-serileştirilebilir yükü saklayabilmeniz için bir RAW_JSON). Tam defineObject referansı için Objects bölümüne bakın.
src/objects/kv-store.object.ts
import { defineObject, FieldType } from 'twenty-sdk/define';
export const KV_STORE_UNIVERSAL_IDENTIFIER =
'2f1c8a90-3b6d-4e2a-9c47-7d0e5a1b9f33';
export const KV_STORE_KEY_FIELD_UNIVERSAL_IDENTIFIER =
'4a7e2d11-9c83-4f60-b5a2-1e6c8d0f4b21';
export const KV_STORE_VALUE_FIELD_UNIVERSAL_IDENTIFIER =
'8b3f6c02-5d19-47ae-9f31-2c4a7e0b6d58';
export default defineObject({
universalIdentifier: KV_STORE_UNIVERSAL_IDENTIFIER,
nameSingular: 'kvStore',
namePlural: 'kvStores',
labelSingular: 'KV Store',
labelPlural: 'KV Store',
description: 'Key-value storage for logic functions',
icon: 'IconDatabase',
fields: [
{
universalIdentifier: KV_STORE_KEY_FIELD_UNIVERSAL_IDENTIFIER,
name: 'key',
type: FieldType.TEXT,
label: 'Key',
description: 'Unique lookup key',
icon: 'IconKey',
},
{
universalIdentifier: KV_STORE_VALUE_FIELD_UNIVERSAL_IDENTIFIER,
name: 'value',
type: FieldType.RAW_JSON,
label: 'Value',
description: 'Stored JSON payload',
icon: 'IconJson',
},
],
});
Anahtar benzersizliğini zorunlu kılın
Aynı anahtarın asla iki satıra sahip olmaması için key üzerinde benzersiz bir indeks ekleyin. Bu, benzersizlik için önerilen ilkeldir — bkz. Data → Unique indexes.
src/indexes/kv-store-key.index.ts
import { defineIndex } from 'twenty-sdk/define';
import {
KV_STORE_UNIVERSAL_IDENTIFIER,
KV_STORE_KEY_FIELD_UNIVERSAL_IDENTIFIER,
} from '../objects/kv-store.object';
export default defineIndex({
universalIdentifier: 'c0d4e8f2-6a1b-4c93-8e57-3f9a2d0b7e14',
objectUniversalIdentifier: KV_STORE_UNIVERSAL_IDENTIFIER,
isUnique: true,
fields: [
{
universalIdentifier: 'c0d4e8f2-6a1b-4c93-8e57-3f9a2d0b7e15',
fieldUniversalIdentifier: KV_STORE_KEY_FIELD_UNIVERSAL_IDENTIFIER,
},
],
});
Bir mantık işlevinden okuma ve yazma
Nesneyi, geri kalan kodunuzun get, set ve del ile bir anahtar-değer API’si gibi okunmasını sağlamak için birkaç küçük yardımcı işlevin arkasına alın. Bunlar, çalışma alanı şemanızdan üretilen ve tamamen kvStore nesnesine göre türlendirilmiş CoreApiClient kullanır.
src/logic-functions/handlers/kv-store.ts
import { CoreApiClient } from 'twenty-client-sdk/core';
import { isDefined } from 'twenty-sdk/utils';
const client = new CoreApiClient();
// Look up a single row by its key.
const findByKey = async (key: string) => {
const { kvStores } = await client.query({
kvStores: {
__args: { filter: { key: { eq: key } }, first: 1 },
edges: { node: { id: true, value: true } },
},
});
return kvStores.edges[0]?.node;
};
// Read a value. Returns undefined when the key is missing.
export const get = async <TValue>(key: string): Promise<TValue | undefined> => {
const row = await findByKey(key);
return isDefined(row) ? (row.value as TValue) : undefined;
};
// Write a value. Creates the row on first write, updates it afterwards (upsert).
export const set = async (key: string, value: unknown): Promise<void> => {
const existing = await findByKey(key);
if (isDefined(existing)) {
await client.mutation({
updateKvStore: {
__args: { id: existing.id, data: { value } },
id: true,
},
});
return;
}
await client.mutation({
createKvStore: {
__args: { data: { key, value } },
id: true,
},
});
};
// Delete a value. No-op when the key is missing.
export const del = async (key: string): Promise<void> => {
const existing = await findByKey(key);
if (isDefined(existing)) {
await client.mutation({
deleteKvStore: { __args: { id: existing.id }, id: true },
});
}
};
Benzersiz indeks çoğaltmalara karşı koruma sağlar, ancak aynı anda aynı yeni anahtarı yazan iki çalıştırma, yine de arama ve oluşturma arasında yarışabilir. Benzersizlik kısıtlamasında başarısız olan bir oluşturma işlemini “başka biri kazandı” olarak ele alın — hatayı yakalayın ve yeniden okuyun ya da bir güncelleme olarak tekrar deneyin.
Kullanın: maliyetli bir çağrıyı önbelleğe alın
Yaygın bir kullanım, yavaş veya hız sınırına tabi üçüncü taraf yanıtını önbelleğe almak, böylece tekrar eden çalıştırmalar her seferinde maliyet ödemek yerine bunu yeniden kullanır.
src/logic-functions/getExchangeRate.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk/define';
import { get, set } from './handlers/kv-store';
const ONE_HOUR_MS = 60 * 60 * 1000;
type CachedRate = { rate: number; fetchedAt: number };
const handler = async (params: { from: string; to: string }) => {
const cacheKey = `exchange-rate:${params.from}:${params.to}`;
const cached = await get<CachedRate>(cacheKey);
if (cached && Date.now() - cached.fetchedAt < ONE_HOUR_MS) {
return { rate: cached.rate, cached: true };
}
const response = await fetch(
`https://api.example.com/rate?from=${params.from}&to=${params.to}`,
);
const { rate } = (await response.json()) as { rate: number };
await set(cacheKey, { rate, fetchedAt: Date.now() });
return { rate, cached: false };
};
export default defineLogicFunction({
universalIdentifier: 'd9b2f4e6-1c83-4a07-9e52-6b1d3c8a0f47',
name: 'get-exchange-rate',
timeoutSeconds: 10,
handler,
});
Kalıplar ve ipuçları
- Ad alanları. Farklı konuları birbirinden ayırmak ve toplu aramaları kolaylaştırmak için anahtarlara ön ek ekleyin —
sync-cursor:linear, cache:exchange-rate:USD:EUR, lock:nightly-report. Bir ad alanının tamamını listelemek veya temizlemek için key: { like: 'cache:%' } ile filtreleyin.
- Sona erme (TTL). Depoda yerleşik bir sona erme özelliği yoktur. Okuma sırasında kontrol etmek için
value içinde bir zaman damgası saklayın (önbellek örneğinde olduğu gibi) veya bir DATE_TIME alanı ekleyip, düzenli aralıklarla cron-triggered function içinden eski satırları temizleyin.
- Ne saklanmalı.
RAW_JSON, sayılar, dizeler, diziler ve nesneler gibi JSON-serileştirilebilir herhangi bir değeri tutar. Kayıtları küçük tutun; bu, koordinasyon ve önbelleğe alma içindir, büyük blob’lar veya dosyalar için değil. Dosyalar için bir FILES alanı ve uploadFile kullanın.
- Görünürlük ve izinler. Satırlar, diğer kayıtlar gibi çalışma alanı veritabanında yaşar; bu nedenle API üzerinden sorgulanabilirler ve uygulamanızın role yapılandırmasına uyarlar. Depoyu ana arayüzün dışında tutmak için, onu navigation menu dışında bırakın.
- Bir kayda kapsamlamak. Genel anahtarlar yerine kayıt başına duruma mı ihtiyacınız var? Kimliği anahtarın içine kodlamak yerine, depo nesnesinden hedef nesneye bir relation ekleyin.
Bu bir kuraldır, ayrı bir özellik değildir — “KV Store”, sizin tanımladığınız ve standart API ile sorguladığınız normal bir özel nesnedir. Bu da, uygulamanızın verilerinin geri kalanıyla aynı eşitleme, izinler ve araç setinden yararlanması anlamına gelir.