Serverless Framework'ten AWS CDK'ya Geçiş: Bölüm 2 - CDK Environment'ınızı Kurma
Serverless uygulamalar için CDK projesini yapılandırın, Lambda için TypeScript'i ayarlayın ve Serverless Framework'ten geçişi kolaylaştıran pattern'leri kurun.
Tek bir geliştirici için işe yarayan bir CDK projesi, ekiple buluşunca nadiren ayakta kalır. Çoğu tutorial’daki düz, tek stack’li yapı; birden fazla mühendis aynı anda farklı environment’larda Lambda fonksiyonu deploy etmeye başlayınca çöker. Paylaşılan stack’ler merge conflict üretir, ownership bulanıklaşır ve yeni bir dosyanın nereye gideceği konusunda kimse anlaşamaz.
Çözüm, iş birimlerini birbirinden yalıtan bir proje yapısı: mühendisler paralel ilerlerken Serverless Framework kullanıcılarının zaten bildiği konvansiyonlar korunur. Serverless Framework’ten CDK’ya geçen ekipler için, ilk migration’dan production platformuna kadar ölçeklenen yapı budur.
Seri Navigasyonu:
- Bölüm 1: Neden Geçiş Yapalım?
- Bölüm 2: CDK Environment Kurulumu (bu yazı)
- Bölüm 3: Lambda Fonksiyonları ve API Gateway Migration
- Bölüm 4: Database Kaynakları ve Environment Yönetimi
- Bölüm 5: Authentication, Authorization ve IAM
- Bölüm 6: Migration Stratejileri ve Best Practice’ler
Ölçeklenen Proje Yapısı#
Aşağıdaki düzen, bir Serverless Framework servisiyle aynı büyüklükteki bir CDK uygulamasını yan yana koyuyor.
# Serverless Framework Yapısı
my-service/
├── serverless.yml
├── package.json
├── src/
│ └── handlers/
│ ├── users.js
│ └── products.js
├── resources/
│ └── dynamodb-tables.yml
└── config/
├── dev.yml
└── prod.yml
# CDK Yapısı (ekip ölçeğinde düzen)
my-service/
├── cdk.json # CDK app konfigürasyonu
├── package.json
├── bin/
│ └── my-service.ts # Tek entry point (sadelik için önemli)
├── lib/
│ ├── stacks/ # Domain'e göre stack tanımları
│ │ ├── api-stack.ts # API Gateway + Lambda fonksiyonları
│ │ ├── data-stack.ts # DynamoDB tabloları (stateful)
│ │ └── auth-stack.ts # Cognito + auth logiği
│ ├── constructs/ # Tutarlılık için yeniden kullanılabilir pattern'ler
│ │ ├── production-lambda.ts # Standart Lambda konfigürasyonu
│ │ ├── api-with-auth.ts # Yaygın API pattern'leri
│ │ └── monitored-table.ts # Alarm'lı DynamoDB
│ └── config/ # Environment-spesifik config'ler
│ ├── development.ts
│ ├── staging.ts
│ └── production.ts
├── src/
│ └── handlers/ # Lambda kodu (tanıdık lokasyon)
│ ├── users/ # Domain'e göre gruplandırılmış
│ │ ├── create.ts
│ │ ├── update.ts
│ │ └── list.ts
│ └── products/
│ ├── catalog.ts
│ └── inventory.ts
└── test/
├── unit/ # Handler unit testleri
├── integration/ # API integration testleri
└── infrastructure/ # CDK stack testleri
Mühendisleri birbirinin ayağına dolanmaktan kurtaran şey domain-driven organizasyon: her stack’in tek bir sahibi var ve yeni bir dosyanın nereye gideceği belli.
CDK Projenizi Başlatma#
Önce prerequisite’lerin olduğundan emin olun:
# AWS CDK CLI'yi global olarak yükle
npm install -g aws-cdk@2
# Kurulumu doğrula
cdk --version # 2.x.x göstermeli
# AWS credential'larını configure et (henüz yapılmadıysa)
aws configure
Şimdi projenizi oluşturun:
# Proje dizini oluştur
mkdir my-serverless-api && cd my-serverless-api
# TypeScript ile CDK initialize et
cdk init app --language typescript
# Lambda-spesifik dependency'leri yükle
npm install @types/aws-lambda
# Development araçlarını yükle
npm install --save-dev esbuild @types/node ts-node
Lambda Development için TypeScript Konfigürasyonu#
CDK temel bir tsconfig.json oluşturur. Serverless development için optimize edelim:
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"esModuleInterop": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"inlineSourceMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictPropertyInitialization": false,
"skipLibCheck": true,
"resolveJsonModule": true,
"outDir": "./dist",
"rootDir": "./",
"baseUrl": "./",
"paths": {
"@handlers/*": ["src/handlers/*"],
"@libs/*": ["src/libs/*"],
"@constructs/*": ["lib/constructs/*"]
}
},
"include": [
"bin/**/*",
"lib/**/*",
"src/**/*",
"test/**/*"
],
"exclude": [
"cdk.out",
"node_modules"
]
}
Environment Yapılandırma Yönetimi#
Serverless Framework environment-spesifik konfigürasyon için YAML dosyaları kullanır. TypeScript tabanlı bir eşdeğer oluşturalım:
// lib/config/environment.ts
export interface EnvironmentConfig {
stage: string;
region: string;
account: string;
api: {
throttling: {
rateLimit: number;
burstLimit: number;
};
cors: {
origins: string[];
credentials: boolean;
};
};
lambda: {
memorySize: number;
timeout: number;
reservedConcurrentExecutions?: number;
};
monitoring: {
alarmEmail?: string;
enableXRay: boolean;
logRetentionDays: number;
};
}
// lib/config/stages/dev.ts
export const devConfig: EnvironmentConfig = {
stage: 'dev',
region: 'us-east-1',
account: '123456789012',
api: {
throttling: {
rateLimit: 100,
burstLimit: 200,
},
cors: {
origins: ['http://localhost:3000'],
credentials: true,
},
},
lambda: {
memorySize: 512,
timeout: 30,
},
monitoring: {
enableXRay: true,
logRetentionDays: 7,
},
};
// lib/config/stages/prod.ts
export const prodConfig: EnvironmentConfig = {
stage: 'prod',
region: 'us-east-1',
account: '123456789012',
api: {
throttling: {
rateLimit: 1000,
burstLimit: 2000,
},
cors: {
origins: ['https://myapp.com'],
credentials: true,
},
},
lambda: {
memorySize: 1024,
timeout: 30,
reservedConcurrentExecutions: 100,
},
monitoring: {
alarmEmail: 'alerts@myapp.com',
enableXRay: true,
logRetentionDays: 30,
},
};
// lib/config/index.ts
import { devConfig } from './stages/dev';
import { prodConfig } from './stages/prod';
export function getConfig(stage: string): EnvironmentConfig {
switch (stage) {
case 'dev':
return devConfig;
case 'prod':
return prodConfig;
default:
throw new Error(`Unknown stage: ${stage}`);
}
}
İlk Construct’ınızı Oluşturma#
Construct’lar CDK’nın yapı taşlarıdır. Lambda fonksiyonları için reusable bir pattern oluşturalım:
// lib/constructs/serverless-function.ts
import { Construct } from 'constructs';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { Runtime, Tracing } from 'aws-cdk-lib/aws-lambda';
import { Duration } from 'aws-cdk-lib';
import { EnvironmentConfig } from '../config/environment';
export interface ServerlessFunctionProps {
entry: string;
handler?: string;
environment?: Record<string, string>;
config: EnvironmentConfig;
memorySize?: number;
timeout?: number;
}
export class ServerlessFunction extends NodejsFunction {
constructor(scope: Construct, id: string, props: ServerlessFunctionProps) {
const { config } = props;
super(scope, id, {
runtime: Runtime.NODEJS_20_X, // En yeni özellikler için NODEJS_22_X'i düşünün
handler: props.handler || 'handler',
entry: props.entry,
memorySize: props.memorySize || config.lambda.memorySize,
timeout: Duration.seconds(props.timeout || config.lambda.timeout),
tracing: config.monitoring.enableXRay ? Tracing.ACTIVE : Tracing.DISABLED,
environment: {
NODE_OPTIONS: '--enable-source-maps',
STAGE: config.stage,
...props.environment,
},
bundling: {
minify: config.stage === 'prod',
sourceMap: true,
sourcesContent: false,
target: 'es2022',
keepNames: true,
// AWS SDK v3'ü hariç tut (Lambda runtime'da sağlanıyor)
externalModules: [
'@aws-sdk/*',
],
},
reservedConcurrentExecutions: config.lambda.reservedConcurrentExecutions,
});
}
}
İlk Stack’inizi Kurma#
Şimdi construct’ımızı kullanan bir stack oluşturalım:
// lib/stacks/api-stack.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { RestApi, LambdaIntegration, Cors } from 'aws-cdk-lib/aws-apigateway';
import { ServerlessFunction } from '../constructs/serverless-function';
import { EnvironmentConfig } from '../config/environment';
export interface ApiStackProps extends StackProps {
config: EnvironmentConfig;
}
export class ApiStack extends Stack {
public readonly api: RestApi;
constructor(scope: Construct, id: string, props: ApiStackProps) {
super(scope, id, props);
const { config } = props;
// API Gateway oluştur
this.api = new RestApi(this, 'ServerlessApi', {
restApiName: `my-service-${config.stage}`,
deployOptions: {
stageName: config.stage,
throttlingRateLimit: config.api.throttling.rateLimit,
throttlingBurstLimit: config.api.throttling.burstLimit,
},
defaultCorsPreflightOptions: {
allowOrigins: config.api.cors.origins,
allowCredentials: config.api.cors.credentials,
allowMethods: Cors.ALL_METHODS,
allowHeaders: [
'Content-Type',
'Authorization',
'X-Api-Key',
],
},
});
// Lambda fonksiyonları oluştur
const createUserFn = new ServerlessFunction(this, 'CreateUserFunction', {
entry: 'src/handlers/users.ts',
handler: 'create',
config,
environment: {
// Environment variable'lar Bölüm 4'te eklenecek
},
});
// Route'ları kur
const users = this.api.root.addResource('users');
users.addMethod('POST', new LambdaIntegration(createUserFn));
}
}
CDK App Entry Point#
CDK app entry point’ini konfigürasyon sistemimizi kullanacak şekilde güncelleyin:
// bin/my-service.ts
#!/usr/bin/env node
import 'source-map-support/register';
import { App } from 'aws-cdk-lib';
import { ApiStack } from '../lib/stacks/api-stack';
import { getConfig } from '../lib/config';
const app = new App();
// Context veya environment'tan stage al
const stage = app.node.tryGetContext('stage') || process.env.STAGE || 'dev';
const config = getConfig(stage);
new ApiStack(app, `MyServiceApiStack-${stage}`, {
config,
env: {
account: config.account,
region: config.region,
},
tags: {
Stage: stage,
Service: 'my-service',
ManagedBy: 'cdk',
},
});
İlk Lambda Handler’ınız#
TypeScript kullanarak bir Lambda handler oluşturun:
// src/handlers/users.ts
// RestApi v1 proxy payload'ını gönderir. HttpApi kullanılsaydı v2 gelirdi,
// yani tipler APIGatewayProxyEventV2 / APIGatewayProxyResultV2 olurdu.
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
export const create = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
console.log('Event:', JSON.stringify(event, null, 2));
try {
const body = JSON.parse(event.body || '{}');
// Handler logiği burada (Bölüm 3'te genişletilecek)
return {
statusCode: 201,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'User created successfully',
name: body.name,
stage: process.env.STAGE,
}),
};
} catch (error) {
console.error('Error:', error);
return {
statusCode: 500,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
error: 'Internal server error',
}),
};
}
};
Deployment Komutları#
Bu script’leri package.json’nıza ekleyin:
{
"scripts": {
"build": "tsc",
"watch": "tsc -w",
"cdk": "cdk",
"bootstrap": "cdk bootstrap",
"deploy:dev": "cdk deploy --context stage=dev",
"deploy:prod": "cdk deploy --context stage=prod",
"diff:dev": "cdk diff --context stage=dev",
"diff:prod": "cdk diff --context stage=prod",
"synth": "cdk synth",
"test": "jest",
"test:watch": "jest --watch"
}
}
İlk Deployment#
AWS environment’ınızı bootstrap edin (CDK deployment’ları için AWS hesabınızı gerekli S3 bucket’ları ve IAM roller oluşturarak hazırlayan tek seferlik kurulum):
npm run bootstrap
Development’a deploy edin:
npm run deploy:dev
CDK hangi kaynakları oluşturmayı planladığını gösterecek. İnceleyin ve onaylayın.
Local Development Kurulumu#
Serverless Framework’ün serverless-offline’ının aksine, CDK built-in local API Gateway emülasyonu sağlamıyor. Local development için birkaç seçeneğiniz var:
- SAM CLI Entegrasyonu (Önerilen):
# SAM CLI yükle
brew install aws-sam-cli # macOS
# veya takip edin: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html
# CloudFormation template oluştur
cdk synth --no-staging > template.yaml
# Local API başlat
sam local start-api -t template.yaml
- Doğrudan Handler Testing:
// test/handlers/users.test.ts
import { create } from '../../src/handlers/users';
import { APIGatewayProxyEvent } from 'aws-lambda';
describe('Users Handler', () => {
it('should create a user', async () => {
const event: Partial<APIGatewayProxyEvent> = {
body: JSON.stringify({ name: 'John Doe' }),
};
const result = await create(event as APIGatewayProxyEvent);
expect(result.statusCode).toBe(201);
expect(JSON.parse(result.body)).toHaveProperty('message');
});
});
Temel Farklar#
| Konu | Serverless Framework | CDK |
|---|---|---|
| Konfigürasyon | YAML dosyaları | TypeScript kodu |
| Environment Variable’lar | ${self:provider.stage} | Config objeleri |
| Local Development | serverless-offline | SAM CLI veya testing |
| Deployment | serverless deploy | cdk deploy |
| Kaynak Reference’ları | !Ref veya ${cf:stackName.output} | Doğrudan object reference’ları |
Sonraki Adımlar#
Artık Serverless Framework geleneklerini yansıtan ama CDK’nın type safety ve composability’sini koruyan bir CDK temeliniz var. Lambda fonksiyonlarınız tanıdık yerlerde duruyor; altyapınız ise tip denetiminden geçirip test edebileceğiniz TypeScript kodu.
İlgili okuma: Farklı CDK organizasyon pattern’lerinin (service-based vs domain-based vs feature-based) kapsamlı karşılaştırması için, AWS CDK Kod Organizasyonu: Service-Based vs Domain-Based Architecture Pattern’leri yazısına göz atın.
3. Bölüm’te, şunları içeren Lambda fonksiyonları ve API Gateway konfigürasyonlarını migrate edeceğiz:
- Request/response transformasyon’ları
- API Gateway modelleri ve validator’ları
- Lambda layer’ları ve dependency’leri
- Error handling pattern’leri
- API versioning stratejileri
Bu düzen, servisin sahibi tek bir ekip olduğu sürece işini görür. İki ayrı ekip farklı parçaların sahibi olduğunda, lib/ altına yeni klasörler eklemek yerine stack sınırlarından bölün.
Kaynaklar#
- AWS CDK v2 Geliştirici Rehberi (yeni sekmede açılır) - CDK proje kurulumu, construct’lar ve deployment iş akışları için kapsamlı referans
- AWS CDK projeleri (yeni sekmede açılır) - CDK proje yapısı, stack’ler ve uygulama düzeyinde organizasyon için resmi rehber
- AWS CDK ile başlarken (yeni sekmede açılır) - CDK projesini sıfırdan kurma, bootstrap ve başlatma
- Serverless Framework - AWS ile Kurulum (yeni sekmede açılır) - Yerini alacak YAML kurallarının referansı; yan yana karşılaştırma için faydalı
- TypeScript ile Lambda fonksiyonu oluşturma (yeni sekmede açılır) - NodejsFunction construct ile Lambda’ya TypeScript paketleme ve dağıtım
- AWS SAM nedir? - AWS Serverless Application Model (yeni sekmede açılır) - CDK geliştirmesi sırasında yerel API öykünmesi için SAM CLI entegrasyonu
Serverless Framework'ten AWS CDK'ya Geçiş Rehberi
Serverless Framework'ten AWS CDK'ya tam geçiş sürecini kapsayan 6 bölümlük kapsamlı rehber. Kurulum, uygulama pattern'leri ve best practice'ler dahil.
Bu serideki tüm yazılar
İlgili yazılar
Secrets Manager ve Parameter Store'u karşılaştıran teknik rehber: hangi servisi ne zaman seçmeli ve production implementation pattern'leri.
aws · secrets-management · security +7
AWS CDK, DynamoDB ve Lambda ile production-grade link kısaltıcı kurulumu. Mimari kararlar, proje yapısı ve ölçekte ayakta kalan şema seçimleri.
aws-cdk · lambda · dynamodb +5
Bir kodlama ajanının evi olarak devcontainer, Codespaces ve AWS Lambda MicroVM: her basamak ne katıyor ve ajanı laptoptan çıkarmak ne zaman kazandırıyor.
lambda · claude-code · ai-tools +5
İç servis katmanı kurmadan önce kurup kurmayacağınıza karar verin. Katmanın çağrı başına maliyeti, VPC Lattice'in kazandığı hacim ve direct invoke'un hâlâ kazandığı an.
aws · aws-cdk · lambda +4
Private REST API gRPC’yi yapısal olarak taşıyamaz ve gRPC konuşan her AWS yüzeyi Lambda hedeflerini dışlar. gRPC’den neyi tutmalı, neyi bırakmalı.
aws · aws-cdk · lambda +4