Node.js'te dosyaya yazmak için node:fs/promises modülündeki writeFile() fonksiyonunu kullanırsınız: await writeFile('mesaj.txt', 'merhaba') dosya yoksa oluşturur, varsa içeriğini tamamen değiştirir. Mevcut içeriğin sonuna eklemek istiyorsanız appendFile() ya da flag: 'a' kullanırsınız. Dosya zaten varsa yazmayı reddetmek için flag: 'wx', çok sayıda küçük yazma için ise fs.createWriteStream() doğru araçtır.
Bu yazıda writeFile, appendFile ve senkron karşılıklarını; dosya açma bayraklarını (flag), veri türü hatalarını, üst klasör yokken alınan ENOENT hatasını, aynı dosyaya eşzamanlı yazmanın neden güvensiz olduğunu, dosyayı değiştirmeden belirli bir konuma yazmayı ve belgelerin "atomik yazma" konusunda ne söyleyip ne söylemediğini Node.js v26.13.0 ile denenmiş örneklerle anlatıyoruz. Okuma tarafı için dosya okuma yazımıza bakabilirsiniz.
writeFile: dosyayı oluştur ya da değiştir
fsPromises.writeFile(file, data[, options]) verilen veriyi dosyaya yazar ve dosya zaten varsa onu değiştirir. Promise başarıyla tamamlandığında undefined değeriyle çözülür. Varsayılan seçenekler encoding: 'utf8', mode: 0o666 ve flag: 'w' şeklindedir. 'w' bayrağı dosyayı yazmak için açar, yoksa oluşturur, varsa sıfırlar (truncate). Bu yüzden ikinci yazma ilk içeriği tamamen siler:
import { writeFile, readFile } from 'node:fs/promises';
await writeFile('mesaj.txt', 'ilk içerik');
await writeFile('mesaj.txt', 'yeni');
console.log(await readFile('mesaj.txt', 'utf8'));
// çıktı: yeniBelgelere göre data bir string, Buffer, TypedArray, DataView olabilir; Promise biçiminde ayrıca Iterable ve AsyncIterable nesneleri de kabul edilir. data bir Buffer ise encoding seçeneği yok sayılır. mode seçeneği yalnızca yeni oluşturulan dosyayı etkiler; var olan dosyanın izinlerini değiştirmez. İzinleri değiştirmek için chmod ailesi kullanılır ve belge, Windows'ta yalnızca yazma izninin değiştirilebildiğini ayrıca belirtir.
Callback biçimi fs.writeFile(file, data[, options], callback) ve senkron biçim fs.writeFileSync(file, data[, options]) aynı mantıkla çalışır. Callback parametresi zorunludur; verilmezse çalışma anında TypeError fırlatılır. Senkron biçim undefined döndürür ve hatayı fırlatır.
appendFile: sonuna ekleme
appendFile() veriyi dosyanın sonuna ekler ve dosya yoksa oluşturur. Varsayılan bayrağı 'a''dır. Basit log dosyaları için kullanışlıdır. Aşağıdaki örnek önce rm ile eski dosyayı (varsa) siliyor, ardından üç satır ekliyor; force: true sayesinde dosya yoksa hata alınmıyor:
import { appendFile, readFile, rm } from 'node:fs/promises';
await rm('uygulama.log', { force: true });
for (const olay of ['başladı', 'istek geldi', 'bitti']) {
await appendFile('uygulama.log', `${olay}\n`);
}
console.log((await readFile('uygulama.log', 'utf8')).trimEnd());
// çıktı: başladı
// çıktı: istek geldi
// çıktı: bittiSatır sonunu (\n) kendiniz eklemelisiniz; appendFile araya hiçbir şey koymaz. Her çağrı dosyayı açar, yazar ve kapatır. Saniyede yüzlerce satır yazan bir uygulamada bu gereksiz bir yüktür; o durumda bir yazma stream'i açık tutmak daha uygundur (aşağıda göreceğiz). Senkron karşılığı appendFileSync()'tir. path parametresi olarak ekleme kipinde açılmış bir dosya tanımlayıcısı da verebilirsiniz; bu durumda belgeye göre tanımlayıcı otomatik kapatılmaz, kapatmak sizin sorumluluğunuzdadır.
Dosya açma bayrakları (flag)

flag seçeneği dosyanın hangi kipte açılacağını belirler. Belgelerdeki tam listeden yazma ile ilgili en sık kullanılanlar şunlardır:
| Bayrak | Ne yapar | Dosya yoksa | Dosya varsa |
|---|---|---|---|
| w | Yazmak için açar (writeFile varsayılanı) | Oluşturur | İçeriği siler |
| wx | w gibi ama dışlayıcı | Oluşturur | Hata verir (EEXIST) |
| w+ | Okuma ve yazma | Oluşturur | İçeriği siler |
| a | Sonuna ekleme (appendFile varsayılanı) | Oluşturur | Sonuna ekler |
| ax | a gibi ama dışlayıcı | Oluşturur | Hata verir |
| a+ | Okuma ve sonuna ekleme | Oluşturur | Sonuna ekler |
| r+ | Okuma ve yazma, sıfırlamaz | Hata verir | İçeriği korur |
Belge birkaç platform ayrıntısına dikkat çeker. Linux'ta dosya ekleme kipinde açıldıysa konumlu yazmalar çalışmaz; çekirdek konum parametresini yok sayar ve veriyi her zaman sona ekler. Windows'ta var olan gizli bir dosyayı 'w' bayrağıyla açmak EPERM ile başarısız olur; bu tür dosyalar 'r+' ile açılabilir. Dışlayıcı 'x' bayrağının ağ dosya sistemlerinde çalışmayabileceği de not edilmiştir.
wx ile 'varsa yazma'
Bir dosyanın üzerine yanlışlıkla yazılmasını istemiyorsanız 'wx' bayrağını kullanın. İşletim sistemi dosyayı oluşturmayı tek adımda dener; dosya zaten varsa EEXIST hatası alırsınız. Önce existsSync ile kontrol edip sonra yazmaktan farkı, iki adım arasında başka bir sürecin araya girememesidir:
import { writeFile } from 'node:fs/promises';
await writeFile('kilit.txt', 'süreç 1', { flag: 'wx' });
try {
await writeFile('kilit.txt', 'süreç 2', { flag: 'wx' });
} catch (err) {
console.log(err.code, err.syscall);
}
// çıktı: EEXIST openBu örnek ilk çalıştırmada yukarıdaki çıktıyı verir. Betiği ikinci kez çalıştırırsanız kilit.txt zaten var olduğu için ilk writeFile çağrısı da hata fırlatır. Belgeler, "önce kontrol et sonra yaz" yerine doğrudan 'wx' ile açıp EEXIST hatasını ele almayı önerilen desen olarak gösterir; bu konu fs.stat ve dosya var mı kontrolü yazımızda daha ayrıntılı.
Veri türü hataları: nesne yazmak

v14.0.0'dan beri writeFile desteklenmeyen girdileri string'e çevirmeye çalışmaz. Bir JavaScript nesnesini doğrudan verirseniz [object Object] yazılmaz, ERR_INVALID_ARG_TYPE hatası alırsınız. Belgedeki değişiklik kaydına göre kendi toString fonksiyonu olan bir nesne vermek de v19.0.0'dan beri desteklenmiyor. Nesneyi önce JSON.stringify ile metne çevirmelisiniz:
import { writeFile, readFile } from 'node:fs/promises';
const ayar = { port: 3000, ad: 'api' };
try {
await writeFile('ayar.json', ayar);
} catch (err) {
console.log(err.name, err.code);
console.log(err.message);
}
await writeFile('ayar.json', JSON.stringify(ayar, null, 2));
console.log(await readFile('ayar.json', 'utf8'));
// çıktı: TypeError ERR_INVALID_ARG_TYPE
// çıktı: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of Object
// çıktı: {
// çıktı: "port": 3000,
// çıktı: "ad": "api"
// çıktı: }Aynı kural sayılar için de geçerlidir. Senkron biçimle sayı yazmaya çalışmak da aynı kodu verir; sayıyı String(42) ile çevirmeniz gerekir. Buffer ise sorunsuz kabul edilir:
import { writeFileSync, appendFileSync, readFileSync } from 'node:fs';
writeFileSync('sayac.txt', '0');
appendFileSync('sayac.txt', '1');
appendFileSync('sayac.txt', Buffer.from('2'));
console.log(readFileSync('sayac.txt', 'utf8'));
try {
writeFileSync('sayac.txt', 42);
} catch (err) {
console.log(err.code);
}
// çıktı: 012
// çıktı: ERR_INVALID_ARG_TYPEHata kodlarının (ERR_*) genel mantığı ve en sık görülenleri Node.js hata kodları yazımızda anlatılıyor. JSON.stringify'ın üçüncü parametresi (girinti) ve dikkat edilmesi gereken değer türleri için JSON.parse ve stringify yazımıza bakabilirsiniz.
Üst klasör yoksa: ENOENT
writeFile dosyayı oluşturur ama eksik klasörleri oluşturmaz. Yoldaki bir klasör yoksa open sistem çağrısı ENOENT ile başarısız olur. Çözüm, önce mkdir ile klasörü recursive: true seçeneğiyle oluşturmaktır; bu seçenek klasör zaten varsa hata vermez:
import { writeFile, mkdir } from 'node:fs/promises';
try {
await writeFile('rapor/2026/kasim.txt', 'veri');
} catch (err) {
console.log(err.code, err.syscall);
}
await mkdir('rapor/2026', { recursive: true });
await writeFile('rapor/2026/kasim.txt', 'veri');
console.log('yazıldı');
// çıktı: ENOENT open
// çıktı: yazıldıKlasör oluşturma, listeleme ve silme işlemlerinin tamamı mkdir, readdir ve rm yazımızda ele alınıyor.
Aynı dosyaya eşzamanlı yazma ve createWriteStream
Belge açık bir uyarı içerir: aynı dosya üzerinde, öncekinin bitmesini beklemeden birden fazla kez writeFile çağırmak güvenli değildir. fs/promises işlemleri libuv iş parçacığı havuzunda çalışır, birbirleriyle senkronize edilmez ve aynı dosyada eşzamanlı değişiklik veri bozulmasına yol açabilir. writeFile kendisi de tek bir işlem değildir; verdiğiniz veriyi içeride birden fazla write çağrısıyla yazan bir kolaylık fonksiyonudur.
Sürekli yazma gereken durumlar için belge fs.createWriteStream() önerir. Stream dosyayı bir kez açar, yazmaları sıraya koyar ve siz end() çağırana kadar açık tutar. 'finish' olayı tüm verinin işletim sistemine iletildiğini bildirir:
import { createWriteStream } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { once } from 'node:events';
const akis = createWriteStream('kayit.log');
for (let i = 1; i <= 3; i++) {
akis.write(`satır ${i}\n`);
}
akis.end('son\n');
await once(akis, 'finish');
console.log(akis.bytesWritten);
console.log((await readFile('kayit.log', 'utf8')).trimEnd());
// çıktı: 31
// çıktı: satır 1
// çıktı: satır 2
// çıktı: satır 3
// çıktı: sonbytesWritten değeri 31'dir: her satır N\n dokuz bayt tutar (ı iki bayttır), üçü 27 eder, son\n da dört bayt ekler. createWriteStream varsayılan olarak flags: 'w' ile açar; mevcut dosyanın sonuna eklemek için { flags: 'a' } verin. Dikkat: burada seçeneğin adı flag değil flags'tır. events.once yardımcı fonksiyonu EventEmitter yazımızda, write() dönüş değerine göre yavaşlamak (backpressure) ise pipeline ve backpressure yazımızda anlatılıyor.
Dosyayı değiştirmeden belirli konuma yazmak
Belge şunu hatırlatır: bir dosyayı baştan yazmak yerine yerinde değiştirmek istiyorsanız varsayılan 'w' yerine 'r+' bayrağı gerekir, çünkü 'w' dosyayı açar açmaz sıfırlar. fsPromises.open() ile bir FileHandle alıp write(string, position) çağırırsanız veri yalnızca o konumdaki baytların üzerine yazılır:
import { writeFile, open, readFile } from 'node:fs/promises';
await writeFile('kod.txt', 'ABCDEFGH');
const fh = await open('kod.txt', 'r+');
try {
await fh.write('xy', 2);
} finally {
await fh.close();
}
console.log(await readFile('kod.txt', 'utf8'));
// çıktı: ABxyEFGHKonum 0'dan sayılır; 2. bayttan itibaren CD yerine xy yazıldı. FileHandle mutlaka kapatılmalıdır; finally bloğu bunu hata olsa bile garanti eder. Dosyayı belirli bir uzunluğa kısaltmak için filehandle.truncate(len) kullanılır. FileHandle ve dosya tanımlayıcıları fs/promises, callback ve sync farkı yazımızda ayrıntılı.
Iterable ve async generator ile yazmak
fsPromises.writeFile() v15.14.0 ve v14.18.0'dan beri data olarak Iterable ve AsyncIterable nesneleri de kabul eder. Bu sayede veriyi parça parça üreten bir async generator fonksiyonunu doğrudan dosyaya yazabilirsiniz; tüm içeriği önce tek bir string'de birleştirmeniz gerekmez:
import { writeFile, readFile } from 'node:fs/promises';
async function* satirUret() {
yield 'id,ad\n';
for (const [id, ad] of [[1, 'Ayşe'], [2, 'Mehmet']]) {
yield `${id},${ad}\n`;
}
}
await writeFile('liste.csv', satirUret());
console.log((await readFile('liste.csv', 'utf8')).trimEnd());
// çıktı: id,ad
// çıktı: 1,Ayşe
// çıktı: 2,MehmetCallback ve senkron writeFile bu türleri kabul etmez; yalnızca string, Buffer, TypedArray ve DataView alırlar. Generator ve iterator kavramı için generator ve iterator yazımıza bakabilirsiniz.
flush seçeneği ve 'atomik yazma' hakkında
v21.0.0 ve v20.10.0 ile writeFile ailesine flush seçeneği eklendi. Belgeye göre tüm veri başarıyla yazıldığında ve flush: true verilmişse veri fs.fsync() (Promise biçiminde filehandle.sync()) ile diske aktarılır. Varsayılan değer false'tur. appendFile ve createWriteStream için de aynı adlı seçenek vardır.
Node.js belgeleri "atomik yazma" adında bir API ya da garanti tanımlamaz. Belgelerde bu konuya değen iki bilgi vardır: fs.rename(), newPath zaten varsa onun üzerine yazar (hedefte bir klasör varsa hata verir) ve copyFile() için kopyalamanın atomikliği konusunda hiçbir garanti verilmediği açıkça yazılıdır. Yaygın bir yaklaşım veriyi önce geçici bir dosyaya yazıp sonra asıl adla değiştirmektir:
import { writeFile, rename, readFile } from 'node:fs/promises';
await writeFile('veri.json', '{"surum":1}');
const gecici = 'veri.json.tmp';
await writeFile(gecici, '{"surum":2}', { flush: true });
await rename(gecici, 'veri.json');
console.log(await readFile('veri.json', 'utf8'));
// çıktı: {"surum":2}Bu desen, okuyan tarafın yarım yazılmış bir dosya görme olasılığını azaltmak için kullanılır. Ancak yeniden adlandırmanın atomik olup olmadığı Node.js'in değil işletim sisteminin ve dosya sisteminin konusudur; belge bu ayrıntı için POSIX rename(2) belgesine yönlendirir. Geçici dosyayı hedefle aynı klasörde tutmak ve ağ dosya sistemlerinde davranışı kendi ortamınızda doğrulamak gerekir.
Yazmayı iptal etmek
writeFile seçeneklerine bir AbortSignal verilebilir. Belge iptalin "en iyi çaba" (best effort) olduğunu ve verinin bir kısmının yine de yazılmış olabileceğini vurgular; işletim sistemine gitmiş istekler durdurulmaz, yalnızca writeFile'ın iç arabelleğe alma işlemi kesilir. Bu yüzden iptal edilen bir yazmadan sonra dosyanın içeriğine güvenmemeli, gerekirse dosyayı silip baştan yazmalısınız. İptal mekanizmasının genel kullanımı için AbortController yazımıza bakın.
Sık yapılan hatalar

- appendFile yerine writeFile kullanmak:
writeFilevarsayılan'w'bayrağıyla dosyayı sıfırlar; log satırlarınız her seferinde silinir. - Nesneyi doğrudan yazmak:
ERR_INVALID_ARG_TYPEalırsınız.JSON.stringifyile metne çevirin. - Await etmeden art arda yazmak: aynı dosyaya bekleme olmadan birden fazla
writeFileçağırmak belgeye göre güvenli değildir. Ya her çağrıyıawaitedin ya da tek bir yazma stream'i kullanın. - Klasörün var olduğunu varsaymak:
writeFileeksik klasörleri oluşturmaz; öncemkdir(..., { recursive: true })çağırın. - createWriteStream'de flag yazmak: stream seçeneğinin adı
flags'tır; belgedeflagdiye bir stream seçeneği yoktur, bu yüzden dosya varsayılan'w'ile açılır. - Satır sonunu unutmak:
appendFileotomatik satır sonu eklemez; her kaydın sonuna\nkoyun. - Sunucu kodunda writeFileSync kullanmak: yazma bitene kadar tüm istekler bekler; istek işleyicilerinde Promise biçimini tercih edin.
Sık Sorulan Sorular
writeFile dosya yoksa oluşturur mu?
Evet. Varsayılan 'w' bayrağı dosya yoksa oluşturur, varsa içeriğini siler. Ancak yoldaki klasörleri oluşturmaz; klasör eksikse ENOENT alırsınız.
Dosyanın sonuna nasıl ekleme yaparım?
appendFile(yol, veri) kullanın ya da writeFile(yol, veri, { flag: 'a' }) yazın. Sık ekleme yapıyorsanız createWriteStream(yol, { flags: 'a' }) ile açık bir stream tutmak daha verimlidir.
Var olan dosyanın üzerine yazılmasını nasıl engellerim?
flag: 'wx' kullanın. Dosya varsa işlem EEXIST koduyla başarısız olur. Bu kontrol işletim sistemi tarafından tek adımda yapıldığı için önce varlık kontrolü yapıp sonra yazmaktan daha güvenlidir.
writeFile ile createWriteStream arasında nasıl seçim yaparım?
Elinizde tamamı hazır, makul boyutta bir veri varsa writeFile en sade yoldur. Veri parça parça geliyorsa, çok sayıda küçük yazma yapacaksanız ya da büyük bir kaynağı dosyaya aktarıyorsanız createWriteStream kullanın; belge de performansa duyarlı kod için stream'i önerir.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Node.js öğrenme rehberi · Dosya okuma · Klasör işlemleri · Stream nedir · Üç API biçimi · Node.js nedir