Event-Driven, Non-Blocking Mimari
V8 motoru üzerinde çalışan Node.js, I/O yoğun iş yüklerinde olağanüstü performans sergiler. Tek iş parçacıklı event loop modeliyle binlerce eşzamanlı bağlantıyı sorunsuz yönetir. TypeScript ile birleştiğinde tip güvenliği, büyük ölçekli projelerde hız ve güvenilirliği bir arada sunar.
- Non-blocking I/O ile düşük bellek ayak izi ve yüksek throughput
- TypeScript ile end-to-end type safety — runtime hatalarını sıfıra yakın tutuyoruz
- Worker Threads ile CPU yoğun işler izole edilmiş thread'lerde çalışır
- npm ekosistemi — 2 milyondan fazla paket, her ihtiyaç için hazır çözüm
- Monorepo desteği (Turborepo / Nx) ile büyük ekiplerde ölçeklenebilir yapı
- Docker + PM2 cluster mode ile sıfır downtime deployment
Gerçek Proje Mimarisi
import express, { Application, Request, Response, NextFunction } from 'express';
import helmet from 'helmet';
import cors from 'cors';
import rateLimit from 'express-rate-limit';
import compression from 'compression';
import { pinoHttp } from 'pino-http';
import { logger } from './utils/logger';
import { errorHandler } from './middlewares/errorHandler';
import { authMiddleware } from './middlewares/auth';
import authRouter from './routes/auth.routes';
import ordersRouter from './routes/orders.routes';
import { env } from './config/env';
// ─── Request Logger ────────────────────────────────────────────
const requestLogger = pinoHttp({
logger,
customLogLevel: (_req, res) => (res.statusCode >= 500 ? 'error' : 'info'),
serializers: {
req: (req) => ({ method: req.method, url: req.url, id: req.id }),
res: (res) => ({ statusCode: res.statusCode }),
},
});
// ─── Rate Limiter ──────────────────────────────────────────────
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 dakika
max: 200,
standardHeaders: true,
legacyHeaders: false,
message: { success: false, error: 'Çok fazla istek, lütfen bekleyin.' },
skip: (req) => req.ip === '127.0.0.1',
});
// ─── App Factory ───────────────────────────────────────────────
export function createApp(): Application {
const app = express();
// Güvenlik & sıkıştırma
app.use(helmet({ contentSecurityPolicy: env.NODE_ENV === 'production' }));
app.use(compression());
app.use(cors({ origin: env.ALLOWED_ORIGINS, credentials: true }));
// Parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Logging & rate limiting
app.use(requestLogger);
app.use('/api', apiLimiter);
// Sağlık kontrolü (auth gerektirmez)
app.get('/health', (_req: Request, res: Response) => {
res.json({ status: 'ok', uptime: process.uptime(), timestamp: Date.now() });
});
// Rotalar
app.use('/api/v1/auth', authRouter);
app.use('/api/v1/orders', authMiddleware, ordersRouter);
// 404 handler
app.use((_req: Request, res: Response) => {
res.status(404).json({ success: false, error: 'Endpoint bulunamadı.' });
});
// Merkezi hata yönetimi (en sona)
app.use(errorHandler);
return app;
}
import { Server, Socket } from 'socket.io';
import { MessageService } from '../services/message.service';
import { RoomService } from '../services/room.service';
import { logger } from '../utils/logger';
interface JoinPayload { roomId: string; }
interface SendPayload { roomId: string; content: string; replyTo?: string; }
interface TypingPayload { roomId: string; }
export function registerChatHandlers(
io: Server,
socket: Socket,
messageService: MessageService,
roomService: RoomService,
): void {
const userId = socket.data.userId as string;
// ─── Odaya Katıl ──────────────────────────────────────────
socket.on('room:join', async ({ roomId }: JoinPayload) => {
const isMember = await roomService.isMember(roomId, userId);
if (!isMember) {
socket.emit('error', { code: 'FORBIDDEN', message: 'Bu odaya erişim izniniz yok.' });
return;
}
await socket.join(roomId);
await roomService.setOnline(roomId, userId);
// Odadaki diğer kullanıcılara bildir
socket.to(roomId).emit('user:joined', { userId, roomId, at: new Date().toISOString() });
logger.info({ userId, roomId }, 'Socket odaya katıldı');
});
// ─── Mesaj Gönder ─────────────────────────────────────────
socket.on('message:send', async (payload: SendPayload, ack) => {
try {
const message = await messageService.create({
roomId: payload.roomId,
senderId: userId,
content: payload.content,
replyTo: payload.replyTo,
});
// Odanın tüm üyelerine yayınla (gönderen dahil)
io.to(payload.roomId).emit('message:new', {
id: message.id,
content: message.content,
senderId: message.senderId,
roomId: message.roomId,
replyTo: message.replyTo,
createdAt: message.createdAt,
});
// ACK ile client'a onay
ack?.({ success: true, messageId: message.id });
} catch (err) {
logger.error({ err, payload }, 'Mesaj gönderilemedi');
ack?.({ success: false, error: 'Mesaj gönderilemedi.' });
}
});
// ─── Yazıyor Göstergesi ───────────────────────────────────
socket.on('typing:start', ({ roomId }: TypingPayload) => {
socket.to(roomId).emit('typing:update', { userId, roomId, isTyping: true });
// 3 sn sonra otomatik kapat
setTimeout(() => {
socket.to(roomId).emit('typing:update', { userId, roomId, isTyping: false });
}, 3000);
});
// ─── Bağlantı Kesme Temizliği ─────────────────────────────
socket.on('disconnect', async (reason) => {
logger.info({ userId, reason }, 'Socket bağlantısı kesildi');
// Kullanıcının bulunduğu tüm odalarda offline yap
const rooms = Array.from(socket.rooms).filter((r) => r !== socket.id);
await Promise.allSettled(
rooms.map((roomId) =>
roomService.setOffline(roomId, userId).then(() =>
io.to(roomId).emit('user:left', { userId, roomId }),
),
),
);
});
}
import { Queue, Worker, Job, QueueEvents } from 'bullmq';
import { redisConnection } from '../config/redis';
import { EmailService } from '../services/email.service';
import { logger } from '../utils/logger';
// ─── Job Tipleri ──────────────────────────────────────────────
export interface WelcomeEmailJob {
type: 'welcome';
userId: string;
email: string;
firstName: string;
}
export interface OrderConfirmEmailJob {
type: 'order_confirm';
orderId: string;
email: string;
items: { name: string; qty: number; price: number }[];
}
export type EmailJob = WelcomeEmailJob | OrderConfirmEmailJob;
// ─── Queue Tanımı ─────────────────────────────────────────────
export const emailQueue = new Queue('email', {
connection: redisConnection,
defaultJobOptions: {
attempts: 3,
backoff: { type: 'exponential', delay: 5000 },
removeOnComplete: { count: 500, age: 24 * 3600 },
removeOnFail: { count: 200, age: 72 * 3600 },
},
});
// ─── Worker ───────────────────────────────────────────────────
const emailWorker = new Worker(
'email',
async (job: Job) => {
const emailService = new EmailService();
await job.updateProgress(10);
switch (job.data.type) {
case 'welcome': {
const { email, firstName } = job.data;
logger.info({ jobId: job.id, email }, 'Hoşgeldin maili gönderiliyor');
await emailService.sendWelcome({ to: email, firstName });
await job.updateProgress(100);
break;
}
case 'order_confirm': {
const { orderId, email, items } = job.data;
logger.info({ jobId: job.id, orderId }, 'Sipariş onay maili gönderiliyor');
await job.updateProgress(30);
const total = items.reduce((s, i) => s + i.qty * i.price, 0);
await emailService.sendOrderConfirmation({ to: email, orderId, items, total });
await job.updateProgress(100);
break;
}
default:
throw new Error(`Bilinmeyen email job tipi`);
}
},
{
connection: redisConnection,
concurrency: 5,
limiter: { max: 50, duration: 60_000 }, // dakikada maks 50 mail
},
);
// ─── Worker Event Dinleyicileri ───────────────────────────────
emailWorker.on('completed', (job) => {
logger.info({ jobId: job.id, type: job.data.type }, 'Email job tamamlandı');
});
emailWorker.on('failed', (job, err) => {
logger.error(
{ jobId: job?.id, attempt: job?.attemptsMade, err: err.message },
'Email job başarısız',
);
});
// ─── Queue Events (dışarıdan izleme) ─────────────────────────
export const emailQueueEvents = new QueueEvents('email', { connection: redisConnection });
// ─── Helper: Job Ekle ─────────────────────────────────────────
export async function enqueueEmail(data: EmailJob, delay = 0) {
return emailQueue.add(data.type, data, { delay });
}
import { PrismaClient, Order, OrderStatus, Prisma } from '@prisma/client';
import { Injectable } from '../di/injectable';
import { PaginatedResult, PaginationParams } from '../types/pagination';
export interface OrderFilters {
userId?: string;
status?: OrderStatus;
fromDate?: Date;
toDate?: Date;
minTotal?: number;
maxTotal?: number;
}
// Prisma include tipi — UI'ye döndürülecek ilişkiler
const orderWithRelations = Prisma.validator()({
include: {
items: { include: { product: { select: { id: true, name: true, sku: true } } } },
user: { select: { id: true, firstName: true, lastName: true, email: true } },
shippingAddress: true,
},
});
type OrderWithRelations = Prisma.OrderGetPayload;
@Injectable()
export class OrderRepository {
constructor(private readonly prisma: PrismaClient) {}
async findMany(
filters: OrderFilters,
pagination: PaginationParams,
): Promise> {
const where: Prisma.OrderWhereInput = {
...(filters.userId && { userId: filters.userId }),
...(filters.status && { status: filters.status }),
...(filters.minTotal !== undefined && {
total: { gte: filters.minTotal },
}),
...(filters.maxTotal !== undefined && {
total: { ...(filters.minTotal !== undefined ? { gte: filters.minTotal } : {}), lte: filters.maxTotal },
}),
...((filters.fromDate || filters.toDate) && {
createdAt: {
...(filters.fromDate && { gte: filters.fromDate }),
...(filters.toDate && { lte: filters.toDate }),
},
}),
deletedAt: null, // soft-delete filtresi
};
const { page, limit } = pagination;
const skip = (page - 1) * limit;
// Tek transaction içinde count + data — tutarlılık garantisi
const [total, orders] = await this.prisma.$transaction([
this.prisma.order.count({ where }),
this.prisma.order.findMany({
where,
...orderWithRelations,
orderBy: { createdAt: 'desc' },
skip,
take: limit,
}),
]);
return {
data: orders,
meta: {
total,
page,
limit,
totalPages: Math.ceil(total / limit),
hasNextPage: page * limit < total,
},
};
}
async findByIdOrThrow(id: string, userId?: string): Promise {
const order = await this.prisma.order.findFirst({
where: { id, ...(userId && { userId }), deletedAt: null },
...orderWithRelations,
});
if (!order) {
throw new Error(`Sipariş bulunamadı: ${id}`);
}
return order;
}
async updateStatus(id: string, status: OrderStatus): Promise {
return this.prisma.order.update({
where: { id },
data: { status, updatedAt: new Date() },
});
}
}
Kullandığımız Paketler
REST API ve middleware zinciri için olgun, battle-tested HTTP sunucu çerçeveleri. Fastify düşük overhead ile yüksek performans sunar.
Tip güvenli, otomatik tamamlama destekli modern TypeScript ORM. Migration, seeding ve Prisma Studio ile tam veri yönetimi.
Redis tabanlı, güvenilir iş kuyruğu. Rate limiter, priority queue, repeatable jobs ve delayed job desteğiyle enterprise düzey arka plan işleme.
Çift yönlü gerçek zamanlı iletişim. Oda yönetimi, namespace, Redis adaptörüyle yatay ölçekleme ve otomatik reconnect desteği.
JWT tabanlı kimlik doğrulama stratejisi. Access + refresh token rotasyonu, cookie-based güvenlik ve çoklu strateji desteği.
TypeScript-first runtime doğrulama kütüphanesi. Schema'dan otomatik tip çıkarımı, request body, query param ve response validasyonu.
Yapılandırılmış JSON log üretimi. Log seviyeleri, dosyaya yazma, Elasticsearch/Datadog entegrasyonu ve async logging desteği.
Cluster mode ile çoklu CPU çekirdeği kullanımı, sıfır downtime reload, cron-based restart ve dahili izleme paneli.
Hangi Projelerde Node.js?
Real-time Chat & Bildirim
Socket.io ve Redis Pub/Sub ile binlerce eşzamanlı kullanıcının mesajlaştığı, anlık bildirim aldığı sistemler.
Microservice Mimarisi
gRPC, NATS veya RabbitMQ üzerinden haberleşen, Docker/Kubernetes ortamında yatay olarak ölçeklenen servis katmanları.
API Gateway
Kimlik doğrulama, rate limiting, request routing ve circuit breaker içeren merkezi giriş noktası servisleri.
WebSocket Sunucusu
Canlı fiyat akışı, borsa verileri, IoT cihaz telemetrisi ve oyun sunucuları için düşük gecikmeli kalıcı bağlantılar.
Yüksek Trafikli REST API
Saniyede binlerce isteği karşılayan, Redis cache katmanlı, CDN entegrasyonlu ve otomatik ölçeklenen API servisleri.
CLI Araçları & Otomasyon
Veri migrasyon scriptleri, CI/CD araçları, cron tabanlı raporlama ve toplu işlem botları.
Node.js ile hızlı ve ölçeklenebilir bir backend mi istiyorsunuz?
Ekibimiz, TypeScript ve Node.js ekosistemiyle enterprise düzeyde backend çözümleri geliştiriyor. Projenizi birlikte konuşalım.
Ücretsiz Danışma Al