Node.js'te bir dosyayı okumanın en kısa yolu node:fs/promises modülündeki readFile() fonksiyonudur: await readFile('notlar.txt', 'utf8') dosyanın tüm içeriğini bir string olarak döndürür. Kodlama (encoding) vermezseniz sonuç string değil, ham baytları tutan bir Buffer olur. Aynı işin callback alan fs.readFile() ve olay döngüsünü bekleten fs.readFileSync() biçimleri de vardır; üçü de dosyanın tamamını belleğe alır.
Bu yazıda üç biçimin nasıl kullanıldığını, kodlama seçimini, dosya yolunun nereye göre çözüldüğünü, ENOENT ve EISDIR gibi hataları, büyük dosyalarda neden stream'e geçmeniz gerektiğini, satır satır okumayı, okumayı iptal etmeyi ve v26.4.0 ile gelen buffer seçeneğini Node.js v26.13.0 üzerinde çalıştırılmış örneklerle anlatıyoruz. Dosyaya yazmak için writeFile ve appendFile yazımıza, üç API biçiminin ayrıntılı karşılaştırması için fs/promises, callback ve sync farkı yazımıza bakabilirsiniz.
readFile için üç biçim

node:fs modülündeki her dosya sistemi işleminin senkron, callback tabanlı ve Promise tabanlı bir biçimi bulunur. Okuma için bunlar fs.readFileSync(), fs.readFile() ve fsPromises.readFile() fonksiyonlarıdır. Promise biçimini import { readFile } from 'node:fs/promises' ile, diğer ikisini import { readFile, readFileSync } from 'node:fs' ile alırsınız. CommonJS kullanan projelerde aynı modüller require('node:fs/promises') ve require('node:fs') ile yüklenir.
Üç biçimin imzası neredeyse aynıdır: ilk parametre dosya yolu, ikinci parametre seçenekler nesnesi ya da doğrudan kodlama adıdır. Seçenekler arasında encoding (varsayılan null), flag (varsayılan 'r'), signal ve buffer bulunur. Senkron biçimde signal yoktur; iptal edilecek bir arka plan işi olmadığı için bu mantıklıdır. Fark, sonucun size nasıl ulaştığındadır: Promise biçimi bir Promise döndürür, callback biçimi sonucu (err, data) parametreleriyle bir fonksiyona verir, senkron biçim değeri doğrudan döndürür ve hatayı fırlatır.
Kodlama: Buffer mı, string mi?
Belgelere göre encoding belirtilmezse veri bir Buffer nesnesi olarak gelir; belirtilirse string olur. Metin dosyası okurken en sık yapılan hata kodlamayı unutup ekrana <Buffer ...> basılmasıdır. Aşağıdaki örnekte aynı dosya önce kodlamasız, sonra 'utf8' ile okunuyor:
import { readFile } from 'node:fs/promises';
const ham = await readFile('notlar.txt');
console.log(ham);
console.log(ham.length);
const metin = await readFile('notlar.txt', 'utf8');
console.log(metin.length);
console.log(metin.split('\n')[1]);
// çıktı: <Buffer 4d 65 72 68 61 62 61 20 4e 6f 64 65 2e 6a 73 0a c4 b0 6b 69 6e 63 69 20 73 61 74 c4 b1 72 3a 20 c4 9f c3 bc c5 9f c4 b1 c3 b6 c3 a7 0a c3 9c c3 a7 c3 ... 13 more bytes>
// çıktı: 63
// çıktı: 50
// çıktı: İkinci satır: ğüşıöçDikkat edin: Buffer uzunluğu 63, string uzunluğu 50. Aradaki fark Türkçe karakterlerden gelir; İ, ğ, ş gibi harfler UTF-8'de iki bayt tutar ama string'de tek karakter sayılır. Bu yüzden dosya boyutu (bayt) ile metin uzunluğunu (karakter) karıştırmamak gerekir. İkili dosyalarda (resim, PDF, sıkıştırılmış arşiv) kodlama vermez, Buffer ile çalışırsınız. Buffer'ın ayrıntıları için Buffer kullanımı yazımıza göz atabilirsiniz.
İkinci parametreyi string olarak vermek ('utf8') ile nesne olarak vermek ({ encoding: 'utf8' }) aynı sonucu verir. Belge, seçenekler bir string ise bunun kodlamayı belirttiğini açıkça söyler. Nesne biçimi, aynı çağrıda signal ya da flag da vermeniz gerektiğinde işe yarar.
Callback ve senkron biçim
Callback biçiminde fonksiyonun son parametresi bir tamamlanma fonksiyonudur. Node.js'in callback kuralı gereği ilk parametre her zaman hata için ayrılmıştır; işlem başarılıysa bu değer null ya da undefined olur. Senkron biçim ise hatayı doğrudan fırlattığı için try...catch ile kullanılır:
import { readFile, readFileSync } from 'node:fs';
readFile('notlar.txt', 'utf8', (err, data) => {
if (err) {
console.error('okunamadı:', err.message);
return;
}
console.log('callback:', data.split('\n')[0]);
});
const s = readFileSync('notlar.txt', { encoding: 'utf8' });
console.log('sync:', s.split('\n')[0]);
// çıktı: sync: Merhaba Node.js
// çıktı: callback: Merhaba Node.jsKodda callback çağrısı yukarıda olduğu halde önce sync: satırı basılır. Çünkü callback biçimi işi arka plana bırakır ve kodun devam etmesine izin verir; senkron çağrı ise dosya okunana kadar olay döngüsünü ve sonraki JavaScript kodunu durdurur. Bu davranışın arkasındaki olay döngüsü için nextTick ve setImmediate yazımıza, callback deseninin dil tarafı için JavaScript callback yazımıza bakabilirsiniz.
Senkron okuma her zaman kötü değildir. Uygulama açılırken bir kez okunan küçük bir ayar dosyası ya da tek seferlik bir komut satırı betiği için readFileSync kodu sadeleştirir. Sorun, bir HTTP sunucusunun her isteğinde senkron okuma yapmaktır: okuma süresince sunucu başka hiçbir isteğe cevap veremez. Olay döngüsünü bloklamanın etkileri Node.js performans optimizasyonu yazımızda ele alınıyor.
Dosya yolu nereye göre çözülür?
String olarak verdiğiniz göreli yollar, betiğin bulunduğu klasöre göre değil, process.cwd() ile dönen çalışma dizinine göre çözülür. Yani node betikler/oku.mjs komutunu proje kökünden çalıştırırsanız readFile('ayar.json') kökteki ayar.json dosyasını arar. Betiğin yanındaki dosyayı okumak için belgelerdeki örnekte olduğu gibi new URL('./ayar.json', import.meta.url) kullanabilirsiniz; fs fonksiyonlarının çoğu file: protokollü bir URL nesnesini yol olarak kabul eder:
import { readFile } from 'node:fs/promises';
const yol = new URL('./ayar.json', import.meta.url);
const ayar = JSON.parse(await readFile(yol, 'utf8'));
console.log(ayar);
console.log(ayar.port + 1);
// çıktı: { port: 3000, debug: true }
// çıktı: 3001readFile JSON'u kendisi ayrıştırmaz; size metni verir, JSON.parse ile nesneye siz çevirirsiniz. Ayrıştırmanın kuralları ve hata durumları için JSON.parse ve stringify yazımıza bakabilirsiniz. ESM'de __dirname olmadığı için import.meta.dirname ve import.meta.filename seçeneklerini __dirname ve import.meta yazımızda, yolları birleştirmeyi path modülü yazımızda, file: URL'lerini ise URL ve URLSearchParams yazımızda anlatıyoruz.
Hatalar: ENOENT, EISDIR ve diğerleri

Okuma başarısız olduğunda gelen hata bir sistem hatasıdır. Nesnenin code alanı hatanın türünü, syscall alanı başarısız olan sistem çağrısını taşır. Kodunuzda mesaj metnine değil code değerine bakmalısınız; mesaj insanlara yöneliktir. Aşağıdaki örnekte olmayan bir dosya ve bir klasör okunmaya çalışılıyor (çıktılardaki tam yollar kısaltılmıştır):
import { readFile } from 'node:fs/promises';
try {
await readFile('yok.txt', 'utf8');
} catch (err) {
console.log(err.name);
console.log(err.code);
console.log(err.syscall);
console.log(err.message);
}
try {
await readFile('klasor');
} catch (err) {
console.log(err.code, err.message);
}
// çıktı: Error
// çıktı: ENOENT
// çıktı: open
// çıktı: ENOENT: no such file or directory, open '/home/kullanici/proje/yok.txt'
// çıktı: EISDIR EISDIR: illegal operation on a directory, read '/home/kullanici/proje/klasor'ENOENT yoldaki bir parçanın bulunmadığını söyler; çoğu zaman yanlış çalışma dizini ya da yazım hatası anlamına gelir. Belgeye göre yol bir klasörse davranış platforma bağlıdır: macOS, Linux ve Windows'ta hata döner, FreeBSD'de ise klasör içeriğinin bir temsili döndürülür. Sık görülen diğer kodlar EACCES (izin yok) ve EMFILE (aynı anda çok fazla açık dosya) olur. Bu kodların listesi ve anlamları için ENOENT, EADDRINUSE ve sistem hataları yazımıza bakabilirsiniz.
Büyük dosyalar: readFile neden yetmez?
Learn rehberi açıkça belirtir: fs.readFile(), fs.readFileSync() ve fsPromises.readFile() verinin tamamını belleğe aldıktan sonra size döner. Birkaç kilobaytlık ayar dosyasında bu sorun değildir; yüzlerce megabaytlık bir log dosyasında ise bellek kullanımı ve süre ciddi biçimde artar. Hata belgesi ayrıca fs.readFile() için 2 GiB üst sınır olduğunu, bu sınırı aşan okumada ERR_FS_FILE_TOO_LARGE hatası alındığını ve bunun Buffer değil iç bir G/Ç kısıtı olduğunu söyler.
API belgesindeki performans notuna göre fs.readFile() dosyayı parça parça okur ve her parça arasında olay döngüsüne fırsat tanır. Normal dosyalarda her okuma 512 KiB işler; boyutu belirlenemeyen dosyalarda (örneğin bir pipe) her okuma 64 KiB yükler. Bu, aynı libuv iş parçacığı havuzunu kullanan diğer işlere daha az etki eder ama tüm dosyanın okunması daha uzun sürer. En hızlı okuma gereken uygulamalar için belge fs.read() ile okumayı uygulamanın kendisinin yönetmesini önerir.
Büyük dosyalar için önerilen yol fs.createReadStream() ile stream kullanmaktır. Stream dosyayı küçük parçalar (chunk) halinde verir; bellekte aynı anda yalnızca birkaç parça bulunur. Varsayılan highWaterMark değeri 64 * 1024 bayttır. Aşağıdaki örnekte etkisini görmek için bu değeri 16 bayta indiriyoruz ve start/end ile dosyanın yalnızca ilk yedi baytını okuyoruz:
import { createReadStream } from 'node:fs';
const akis = createReadStream('notlar.txt', { encoding: 'utf8', highWaterMark: 16 });
let parca = 0;
for await (const chunk of akis) {
parca++;
}
console.log('parça sayısı:', parca);
const aralik = createReadStream('notlar.txt', { start: 0, end: 6, encoding: 'utf8' });
for await (const chunk of aralik) console.log(JSON.stringify(chunk));
// çıktı: parça sayısı: 4
// çıktı: "Merhaba"63 baytlık dosya 16 baytlık parçalarla dört okumada bitti. start ve end değerleri her ikisi de dahil olacak şekilde 0'dan sayılır; bu yüzden 0-6 aralığı yedi bayt verir. Stream'i for await...of ile tüketmek en okunaklı yoldur; stream'in olayları, türleri ve geri basınç (backpressure) konusu stream nedir yazımızda ve pipeline ve backpressure yazımızda anlatılıyor.
Satır satır okuma: filehandle.readLines()
CSV ya da log dosyalarını satır satır işlemek çok sık ihtiyaçtır. fsPromises.open() ile açtığınız bir FileHandle üzerinde readLines() çağırırsanız Node.js sizin için bir readline arayüzü kurar ve dosyayı stream olarak okur. Satır sonları otomatik ayrılır, bellekte dosyanın tamamı tutulmaz:
import { open } from 'node:fs/promises';
const dosya = await open('notlar.txt');
let no = 0;
for await (const satir of dosya.readLines()) {
no++;
console.log(no, satir);
}
// çıktı: 1 Merhaba Node.js
// çıktı: 2 İkinci satır: ğüşıöç
// çıktı: 3 Üçüncü satırreadLines() seçenekleri createReadStream() ile aynıdır ve autoClose varsayılan olarak true olduğu için okuma bitince dosya kapanır. Kullanıcıdan terminalde girdi almak ya da readline modülünü doğrudan kullanmak için readline ile kullanıcı girdisi yazımıza bakın.
Okumayı iptal etmek: AbortSignal
readFile seçeneklerine bir AbortSignal verebilirsiniz. Sinyal tetiklendiğinde Promise AbortError ile reddedilir. Belgenin önemli bir notu var: iptal, işletim sistemine gitmiş tek tek okuma isteklerini durdurmaz; yalnızca readFile'ın kendi içinde yaptığı arabelleğe almayı keser.
import { readFile } from 'node:fs/promises';
const ac = new AbortController();
const soz = readFile('notlar.txt', { signal: ac.signal });
ac.abort();
try {
await soz;
} catch (err) {
console.log(err.name, err.code);
}
// çıktı: AbortError ABORT_ERRZaman aşımı için AbortSignal.timeout(ms), birden fazla sinyali birleştirmek için AbortSignal.any() kullanılabilir. Bu araçları fs, timers ve fetch ile birlikte AbortController ile iptal ve zaman aşımı yazımızda anlatıyoruz.
v26.4.0 ile gelen buffer seçeneği
Belgelerdeki YAML kaydına göre fsPromises.readFile(), fs.readFile(), fs.readFileSync() ve filehandle.readFile() fonksiyonlarına v26.4.0 ile buffer seçeneği eklendi. Bu seçenekle okumanın yapılacağı tamponu siz verirsiniz ya da dosya boyutunu alıp tampon döndüren bir fonksiyon geçersiniz. Kodlama verilmezse dönen Buffer, verdiğiniz tampon üzerinde yalnızca okunan baytları gösteren bir görünümdür. Tampon dosyaya yetmezse işlem hata ile biter:
import { Buffer } from 'node:buffer';
import { readFile } from 'node:fs/promises';
const tampon = Buffer.alloc(1024);
const icerik = await readFile('notlar.txt', { buffer: tampon });
console.log(icerik.length, icerik.buffer === tampon.buffer);
try {
await readFile('notlar.txt', { buffer: Buffer.alloc(4) });
} catch (err) {
console.log(err.name, err.code);
console.log(err.message);
}
// çıktı: 63 true
// çıktı: TypeError ERR_INVALID_ARG_VALUE
// çıktı: The property 'options.buffer.byteLength' is smaller than the file size of 63 bytes. Received 4Bu seçenek, çok sayıda küçük dosyayı art arda okurken her seferinde yeni bellek ayırmak istemediğiniz durumlar için düşünülmüştür. Eski sürümler bu seçeneği tanımaz; projeniz v26.4.0 altındaki bir sürümde çalışacaksa kullanmayın.
Kodlama ve satır sonu tuzakları
Dosyayı yanlış kodlamayla okursanız hata almazsınız; Node.js baytları istediğiniz kodlamaya göre yorumlar ve bozuk görünen bir metin üretir. Aşağıda UTF-8 olarak kaydedilmiş Türkçe metin 'latin1' ile okunuyor:
import { readFile } from 'node:fs/promises';
const yanlis = await readFile('notlar.txt', 'latin1');
console.log(yanlis.split('\n')[1].slice(0, 7));
console.log(yanlis.includes('ş'));
const dogru = await readFile('notlar.txt', 'utf8');
console.log(dogru.split('\n')[1]);
// çıktı: İkinci
// çıktı: false
// çıktı: İkinci satır: ğüşıöçEkranda İ gibi karakterler görüyorsanız dosya büyük olasılıkla UTF-8'dir ama siz başka bir kodlamayla okuyorsunuz. İkinci tuzak satır sonlarıdır: Windows'ta oluşturulan dosyalarda satırlar \r\n ile biter. split('\n') kullanırsanız her satırın sonunda görünmeyen bir \r kalır. Düzenli ifade ile iki biçimi birden ayırmak daha güvenlidir:
import { readFile } from 'node:fs/promises';
const veri = await readFile('notlar.txt', 'utf8');
const satirlar = veri.split(/\r?\n/).filter((s) => s !== '');
console.log(satirlar.length);
console.log(satirlar.at(-1));
// çıktı: 3
// çıktı: Üçüncü satırHangi okuma yöntemi ne zaman?

| Yöntem | Sonuç | Bellek | Uygun olduğu yer |
|---|---|---|---|
| fsPromises.readFile | Promise ile tüm içerik | Dosyanın tamamı | Async kodda küçük ve orta dosyalar |
| fs.readFile | Callback ile tüm içerik | Dosyanın tamamı | Callback tabanlı eski kod, en düşük ek yük |
| fs.readFileSync | Doğrudan değer, hata fırlatır | Dosyanın tamamı | Başlangıçta bir kez okunan ayarlar, betikler |
| fs.createReadStream | Parça parça (chunk) | Birkaç parça | Büyük dosyalar, HTTP yanıtına aktarma |
| filehandle.readLines | Satır satır | Birkaç parça | CSV, log gibi satır tabanlı dosyalar |
Belge, en yüksek performans (çalışma süresi ve bellek ayırma açısından) gerektiğinde callback biçiminin Promise biçimine tercih edilebileceğini söyler. Günlük uygulama kodunda ise okunabilirlik nedeniyle çoğu ekip fs/promises ve async/await kullanır; async/await yazımız bu sözdizimini anlatıyor.
Sık yapılan hatalar
- Kodlamayı unutmak:
encodingvermeyince string yerineBuffergelir veJSON.parsegibi işlemler beklenmedik sonuç verir. Metin dosyalarında'utf8'verin. - Göreli yolu betiğe göre sanmak: göreli yollar
process.cwd()ile çözülür. Betiği başka bir klasörden çalıştırıncaENOENTalırsınız. Betiğe göre yol içinimport.meta.urlya daimport.meta.dirnamekullanın. - Sunucu isteğinde readFileSync kullanmak: her istekte olay döngüsü durur. İstek işleyicilerinde Promise ya da stream biçimini seçin.
- Büyük dosyayı tek seferde okumak: bellek tüketimi dosya boyutu kadar artar, 2 GiB üzerinde
ERR_FS_FILE_TOO_LARGEalınır.createReadStreamya dareadLines()kullanın. - Hata mesajını karşılaştırmak:
err.message.includes('no such file')yerineerr.code === 'ENOENT'kontrol edin. - Önce varlık kontrolü yapmak:
existsSyncardındanreadFilebir yarış durumu yaratır. Doğrudan okuyup hatayı ele alın. - Callback içinde hatayı yok saymak:
if (err)kontrolü olmadandatakullanırsanız hata durumundadatatanımsız olur.
Sık Sorulan Sorular
readFile ile readFileSync arasındaki fark nedir?
İkisi de dosyanın tamamını okur. readFile işi arka planda yapar ve sonuç hazır olunca callback'i çağırır; bu sırada diğer kodlar çalışmaya devam eder. readFileSync ise okuma bitene kadar olay döngüsünü durdurur, değeri doğrudan döndürür ve hatayı fırlatır.
Dosyayı okurken neden <Buffer ...> görüyorum?
Kodlama belirtmediğiniz için Node.js ham baytları Buffer olarak döndürür. readFile(yol, 'utf8') yazarsanız string alırsınız. Elinizdeki Buffer'ı sonradan buf.toString('utf8') ile de çevirebilirsiniz.
Çok büyük bir dosyayı nasıl okurum?
fs.createReadStream() ile parça parça okuyun ve for await...of ile tüketin. Satır tabanlı dosyalarda filehandle.readLines() daha pratiktir. Veriyi başka bir hedefe (dosya, sıkıştırma, HTTP yanıtı) aktaracaksanız stream.pipeline kullanın.
Aynı klasördeki bir dosyayı nasıl güvenle okurum?
ESM'de new URL('./dosya.txt', import.meta.url) ya da path.join(import.meta.dirname, 'dosya.txt') kullanın. CommonJS'te aynı iş path.join(__dirname, 'dosya.txt') ile yapılır. Böylece betiği hangi klasörden çalıştırırsanız çalıştırın doğru dosya okunur.
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 yazma · fs/promises, callback ve sync farkı · Stream nedir · readline ile girdi · Node.js nedir