n8n ile Döngü ve Veri İşleme: Loop Over Items Kullanımı
Veri işleme konusuna gelince, n8n kullanıcılarının en çok takıldığı yerlerden biri şüphesiz döngüler. “Listemdeki her müşteriye nasıl mail atarım?”, “API’den gelen 500 kaydı tek tek nasıl işlerim?” soruları n8n community’sinde her gün onlarca kez karşıma çıkıyor. Bu yazıda bu soruların cevabını, kendi iş akışlarımdan örneklerle anlatacağım.
n8n’de Döngü Mantığını Anlamak
n8n, temelden itibaren liste tabanlı düşünülmüş bir araç. Yani bir node çalıştığında çıktısı tek bir obje değil, bir items dizisi üretiyor. Bu yaklaşım, klasik programlamadaki for döngüsünden farklı bir paradigma sunuyor.
Klasik bir Python döngüsünü düşünelim:
# Python'da klasik yaklaşım
for user in users:
send_email(user.email, user.name)
n8n’de ise veri zaten liste olarak akıyor. Bir HTTP Request node’u 200 kayıt döndürdüğünde, bir sonraki node bu 200 kaydı otomatik olarak ayrı ayrı işliyor. Buna n8n dünyasında “implicit iteration” deniyor. Yani siz hiçbir şey yapmadan, her node zaten bir döngü gibi davranıyor.
Ama işler her zaman bu kadar basit gitmiyor. Bazı senaryolarda explicit döngü kurmanız gerekiyor. İşte o noktada Loop Over Items node’u devreye giriyor.
Loop Over Items Node’u Ne Zaman Kullanılır?
Önce şunu netleştirelim: Eğer amacınız sadece “her kayıt için şu node’u çalıştır” ise, Loop Over Items’a gerek yok. n8n zaten bunu yapıyor. Peki ne zaman lazım?
- Rate limiting uygulamanız gereken durumlarda (API’ye saniyede maksimum 5 istek gönderebiliyorsunuz)
- Her iteration arasında bekleme süresi eklemek istediğinizde
- Bir önceki iterasyonun sonucunu bir sonrakine taşımanız gerektiğinde (state management)
- İşlemleri batch’ler halinde gruplandırmak istediğinizde
- Karmaşık alt iş akışları içinde kontrollü döngü kurmanız gerektiğinde
Şimdi bunları birer birer gerçek dünya senaryolarıyla açıklayalım.
Senaryo 1: Veritabanından Gelen Kayıtları İşlemek
Diyelim ki PostgreSQL’den 1000 müşteri kaydı çektiniz ve her biri için harici bir CRM API’sine çağrı yapmanız gerekiyor. CRM API’niz dakikada 60 isteği sınırlıyor.
İlk adımda veritabanından veriyi çekiyoruz:
# PostgreSQL node sorgusu
SELECT id, email, first_name, last_name, company
FROM customers
WHERE sync_status = 'pending'
AND created_at > NOW() - INTERVAL '24 hours'
ORDER BY created_at ASC
LIMIT 1000;
Sonra Loop Over Items node’u ekliyoruz. Node’un temel konfigürasyonu şöyle:
# Loop Over Items node ayarları (JSON gösterimi)
{
"batchSize": 1,
"options": {}
}
Her iterasyonda şu Code node’u çalışıyor:
# n8n Code node - JavaScript
const item = $input.item.json;
// CRM için payload hazırla
const payload = {
external_id: item.id,
email: item.email,
name: `${item.first_name} ${item.last_name}`,
organization: item.company,
source: 'postgresql_sync'
};
return {
json: payload
};
Rate limit sorununu çözmek için Loop Over Items’dan sonra bir Wait node’u ekliyoruz. Wait node’unu 1 saniye bekleyecek şekilde ayarlıyoruz. Bu sayede dakikada maksimum 60 istek gönderilmiş oluyor.
Senaryo 2: Toplu E-posta Gönderimi ile Batch İşleme
Bu senaryo benim en çok kullandığım pattern’lerden biri. Diyelim ki bir SaaS ürününüz var ve her gün aktif olmayan kullanıcılara özelleştirilmiş e-posta atmak istiyorsunuz.
Önce kullanıcıları çekiyoruz, sonra Loop Over Items ile her birini işliyoruz:
# Code node - Kullanıcı bazlı e-posta içeriği üretimi
const user = $input.item.json;
// Kullanıcının son aktivitesine göre farklı mesaj
let emailSubject = '';
let emailBody = '';
const daysSinceLogin = Math.floor(
(Date.now() - new Date(user.last_login).getTime()) / (1000 * 60 * 60 * 24)
);
if (daysSinceLogin >= 30) {
emailSubject = `${user.first_name}, sizi özledik!`;
emailBody = `30 gün önce son kez giriş yaptınız. Yeni özelliklerimizi keşfetmek ister misiniz?`;
} else if (daysSinceLogin >= 14) {
emailSubject = `${user.first_name}, yeni özellikler sizi bekliyor`;
emailBody = `2 haftadır görünmüyorsunuz. İşte bu sürede eklediğimiz şeyler...`;
} else {
emailSubject = `Haftalık özet: ${user.first_name}`;
emailBody = `Bu hafta hesabınızdaki aktiviteler...`;
}
return {
json: {
to: user.email,
subject: emailSubject,
body: emailBody,
userId: user.id,
daysSinceLogin
}
};
Bu örnekte her kullanıcı için dinamik içerik üretiliyor. Loop’un sonunda da bir Code node ile istatistik topluyoruz.
Senaryo 3: Nested Loop – İç İçe Döngüler
İşte burada işler biraz karmaşıklaşıyor. Birden fazla müşteriniz var ve her müşterinin birden fazla projesi var. Her proje için durum raporu oluşturmanız gerekiyor.
Bu durumda iç içe Loop Over Items kullanımı gerekiyor. Dış döngü müşteriler için, iç döngü projeler için:
# Dış döngü - Müşteri verisi normalizasyonu
const customer = $input.item.json;
// Her müşterinin projelerini ayrı item'lara böl
const projectItems = customer.projects.map(project => ({
json: {
customerId: customer.id,
customerName: customer.name,
projectId: project.id,
projectName: project.name,
projectStatus: project.status,
deadline: project.deadline
}
}));
return projectItems;
Bu Code node’u tek bir müşteri kaydını birden fazla proje kaydına dönüştürüyor. Buna n8n’de “item splitting” deniyor ve döngü mantığının temel taşlarından biri.
Senaryo 4: Önceki İterasyondan Veri Taşımak
Loop Over Items’ın en güçlü özelliklerinden biri, iterasyonlar arası veri taşıyabilmesidir. Klasik programlamadaki “accumulator” pattern’ını burada uygulayabilirsiniz.
Diyelim ki 100 farklı URL’den fiyat verisi çekiyorsunuz ve en düşük fiyatı bulmak istiyorsunuz:
# Accumulator pattern - her iterasyonda en düşük fiyatı takip et
const currentItem = $input.item.json;
// Loop'un static data'sını al (persistent state)
const staticData = $node["Loop Over Items"].context;
// İlk iterasyonda başlat
if (!staticData.minPrice) {
staticData.minPrice = Infinity;
staticData.minPriceItem = null;
staticData.processedCount = 0;
}
// Mevcut fiyatla karşılaştır
if (currentItem.price < staticData.minPrice) {
staticData.minPrice = currentItem.price;
staticData.minPriceItem = currentItem;
}
staticData.processedCount++;
return {
json: {
currentItem,
runningMinPrice: staticData.minPrice,
processedSoFar: staticData.processedCount
}
};
Bu pattern’ı kullandığımda genellikle loop’un çıkışında bir merge node ile de final sonucu topluyorum.
Hata Yönetimi ve Retry Mekanizması
Production ortamında döngüler çalıştırırken hata yönetimi kritik. n8n’de Loop Over Items içindeki bir hata tüm döngüyü durdurabiliyor. Bunu önlemek için her node’a “Continue on Fail” ayarını açabilirsiniz, ama daha gelişmiş bir yaklaşım hataları loglamak ve retry mekanizması kurmak.
# Try-catch ile hata yönetimi
const item = $input.item.json;
let result = null;
let errorOccurred = false;
let errorMessage = '';
try {
// Asıl işlemi burada yap
// Örneğin API çağrısının sonucu
result = {
id: item.id,
processed: true,
timestamp: new Date().toISOString()
};
} catch (error) {
errorOccurred = true;
errorMessage = error.message;
// Hata detayını bir log tablosuna yazabilirsiniz
// ya da Slack/Teams'e bildirim gönderebilirsiniz
}
return {
json: {
originalItem: item,
result,
error: errorOccurred,
errorMessage,
retryNeeded: errorOccurred && !item._retryCount
}
};
Başarısız olan kayıtları filtreleyip tekrar işlemek için iş akışının sonunda bir IF node’u ekleyebilirsiniz. retryNeeded alanı true olan kayıtlar için ayrı bir sub-workflow tetikleyebilirsiniz.
Batch Processing: Toplu İşlem Optimizasyonu
Bazı durumlarda tek tek işlem yapmak yerine kayıtları gruplara ayırıp toplu işlemek daha verimli. Örneğin bir veritabanına tek tek INSERT atmak yerine 100’lü batch’ler halinde INSERT atmak.
Loop Over Items node’undaki Batch Size parametresi tam da bunun için var. Batch Size’ı 1’den fazla yaparsanız, her iterasyona birden fazla item geliyor:
# Batch insert için SQL hazırlama
const items = $input.all();
// Gelen batch'i VALUES formatına çevir
const values = items.map(item => {
const d = item.json;
return `('${d.id}', '${d.email}', '${d.name}', NOW())`;
}).join(', ');
const sql = `
INSERT INTO processed_users (id, email, name, processed_at)
VALUES ${values}
ON CONFLICT (id) DO UPDATE
SET processed_at = NOW()
`;
return {
json: {
query: sql,
batchSize: items.length
}
};
Batch Size’ı 100 yaparak her 100 kayıt için tek bir INSERT atıyoruz. Bu yaklaşım 1000 kayıt için 10 sorgu anlamına geliyor, 1000 sorgu değil. Performans farkı özellikle büyük veri setlerinde çok belirgin.
Gerçek Dünya: Shopify Siparişlerini İşleme
Şimdi tüm bu kavramları bir araya getiren gerçek bir senaryo inceleyelim. Bir e-ticaret müşterim için kurduğum iş akışı:
- Her 15 dakikada Shopify API’sinden yeni siparişleri çek
- Her sipariş için stok kontrolü yap
- Stokta varsa kargoyu oluştur, yoksa tedarikçiye otomatik sipariş ver
- Müşteriye durum SMS’i at
# Shopify sipariş normalizasyonu
const order = $input.item.json;
// Sipariş satırlarını düzleştir
const lineItems = order.line_items.map(item => ({
json: {
orderId: order.id,
orderNumber: order.order_number,
customerEmail: order.email,
customerPhone: order.phone,
sku: item.sku,
productTitle: item.title,
quantity: item.quantity,
variantId: item.variant_id,
price: parseFloat(item.price),
requiresShipping: item.requires_shipping
}
}));
// Sadece kargo gerektiren ürünleri döndür
return lineItems.filter(item => item.json.requiresShipping);
Bu code node, her siparişi satırlarına bölüyor. Bir sipariş 3 ürün içeriyorsa 3 ayrı item üretiyor. Sonraki Loop Over Items her ürün için stok kontrolü yapıyor.
# Stok kontrol sonucuna göre aksiyon belirleme
const item = $input.item.json;
const stockLevel = $('Stok Kontrol API').item.json.available_quantity;
let action = '';
let priority = 0;
if (stockLevel >= item.quantity) {
action = 'create_shipment';
priority = 1;
} else if (stockLevel > 0) {
action = 'partial_fulfill';
priority = 2;
} else {
action = 'reorder_from_supplier';
priority = 3;
}
return {
json: {
...item,
stockLevel,
action,
priority,
checkedAt: new Date().toISOString()
}
};
Bu pattern’ı kurduğumda müşterimin operasyon ekibinin manuel işlem yükü yaklaşık %70 azaldı. Haftada 200-300 sipariş için tek tek kontrol yapılıyordu, artık sadece exception case’ler manuel müdahale gerektiriyor.
Performance İpuçları
n8n’de büyük veri setleriyle çalışırken dikkat etmeniz gereken birkaç nokta var:
- Memory kullanımı: n8n her item’ı bellekte tutuyor. 10.000’den fazla kaydı aynı anda işlemeye çalışırsanız container’ınız OOM hatası verebilir. Bu yüzden veritabanı sorgularınıza
LIMITveOFFSETekleyerek pagination yapın - Execution timeout: n8n’in varsayılan execution timeout’u 1 saattir ama self-hosted kurulumda bu değiştirilebilir. Uzun süren döngüler için
N8N_EXECUTION_TIMEOUTenvironment variable’ını artırın - Paralel çalışma: Loop Over Items sequential çalışır yani iterasyonlar sıra sıra ilerler. Paralel işleme istiyorsanız SplitInBatches node’u ile farklı branch’lere gönderebilirsiniz
- Wait node optimizasyonu: Her iterasyonda 1 saniye beklemek 1000 kayıt için 16 dakika demek. Rate limit gerçekten gerekli mi, yoksa bant genişliğiniz buna izin veriyor mu diye API dokümantasyonunu tekrar kontrol edin
# Pagination ile büyük veri setlerini güvenli işleme
// Bu code node'u "offset" değerini yönetir
const staticData = $node["Loop Over Items"].context;
if (!staticData.offset) {
staticData.offset = 0;
}
const batchSize = 100;
const query = `
SELECT * FROM large_table
ORDER BY id
LIMIT ${batchSize}
OFFSET ${staticData.offset}
`;
staticData.offset += batchSize;
return {
json: {
query,
currentOffset: staticData.offset,
batchSize
}
};
Loop’tan Çıkış Koşulları
Bazen döngüyü belirli bir koşul sağlandığında sonlandırmanız gerekir. n8n’de explicit “break” komutu yok ama IF node’u ile benzer davranışı elde edebilirsiniz.
Loop Over Items node’unun çıkış noktaları var: loop çıkışı (devam ediyor) ve done çıkışı (bitti). Eğer loop içinde bir item’ı “done” çıkışına yönlendirirseniz döngü orada son bulmuyor, ama istediğiniz koşulu takip edebiliyorsunuz.
Alternatif olarak, loop içinde bir flag tutup IF node’uyla bir sonraki iterasyona geçişi kontrol edebilirsiniz. Bu yaklaşım, örneğin “ilk başarılı sonucu bul ve dur” senaryolarında işe yarıyor.
Sonuç
n8n’de Loop Over Items, görsel iş akışı otomasyonunun en güçlü silahlarından biri. Ancak her güçlü araç gibi, doğru zamanda doğru şekilde kullanmak kritik önem taşıyor.
Özetle şunu söyleyebilirim: Eğer n8n’in implicit iteration’ı yeterliyse, Loop Over Items’a gerek yok. Ama rate limiting, state management, batch processing veya karmaşık kontrol akışı gerekiyorsa, bu node’u öğrenmek için harcadığınız zaman katkat geri dönüyor.
Shopify örneğinde olduğu gibi, iyi kurulmuş bir döngü mantığı gerçekten operasyonel yükü ciddi ölçüde azaltabiliyor. Production’da birkaç düzine iş akışı yönetiyorum ve bunların büyük çoğunluğunda bir şekilde Loop Over Items veya benzeri bir pattern var.
Son bir tavsiye: Loop Over Items içindeki her işlemi küçük, test edilebilir parçalara bölün. Büyük bir Code node yerine birkaç küçük node zinciri hem debug etmeyi kolaylaştırır hem de n8n’in görsel avantajından tam anlamıyla faydalanmanızı sağlar.
