Skip to content

Ders 10: Deployment ve Produksiyon

  • Uygulamayı build alma ve optimizasyon
  • Deploy seçenekleri (Vercel, Netlify, Docker)
  • Environment variables yönetimi
  • Hata ayıklama ve monitoring
  • Performans optimizasyonu
  • Analytics entegrasyonu
  • Güvenlik best practice’leri

TanStack Start, Vite kullanarak optimize edilmiş production build’leri oluşturur.

Terminal window
# Development build
npm run dev
# Production build
npm run build
# Production build preview
npm run preview
# Type checking
npm run type-check
vite.config.ts
import { defineConfig } from 'vite'
import { tanstackStartConfig } from '@tanstack/start/config'
export default defineConfig({
...tanstackStartConfig(),
// Build ayarları
build: {
// Chunk boyutu sınırı (kb)
chunkSizeWarningLimit: 1000,
// Source map
sourcemap: process.env.NODE_ENV === 'development',
// Minify
minify: 'terser',
// CSS code split
cssCodeSplit: true,
// Target browser
target: 'es2015',
},
// Server ayarları
ssr: {
// SSR için external modüller
noExternal: ['@tanstack/start'],
},
})

Vercel, TanStack Start için en iyi deploy deneyimini sunar.

Terminal window
# Vercel CLI yükle
npm install -g vercel
# Proje dizinine git
cd my-tanstack-app
# Deploy
vercel
vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": ".tanstack/output",
"devCommand": "npm run dev",
"installCommand": "npm install",
"framework": "vite",
"regions": ["iad1"],
"functions": {
"app/**/*.{ts,tsx}": {
"memory": 1024,
"maxDuration": 10
}
},
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
}
]
}
]
}
Terminal window
# Vercel dashboard veya CLI ile
vercel env add SESSION_SECRET production
vercel env add DATABASE_URL production
vercel env add API_KEY production

Netlify, ücretsiz deploy için harika bir seçenektir.

netlify.toml
[build]
command = "npm run build"
publish = ".tanstack/output"
[build.environment]
NODE_VERSION = "20"
[[plugins]]
package = "@netlify/plugin-lighthouse"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
X-XSS-Protection = "1; mode=block"
Referrer-Policy = "strict-origin-when-cross-origin"

Docker ile containerized deploy yapabilirsiniz.

# Dockerfile
FROM node:20-alpine AS base
# Dependencies
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# Builder
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Build
RUN npm run build
# Runner
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 tanstack
COPY --from=builder /app/.tanstack/output ./
COPY --from=builder /app/package.json ./
RUN chown -R tanstack:nodejs /app
USER tanstack
EXPOSE 3000
CMD ["npm", "run", "start"]
docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
- SESSION_SECRET=${SESSION_SECRET}
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app

Kendi sunucunuzda deploy yapmak için:

Terminal window
# PM2 yükle
npm install -g pm2
# Uygulamayı başlat
pm2 start npm --name "tanstack-app" -- start
# Kaydet
pm2 save
# Startup script oluştur
pm2 startup
/etc/nginx/sites-available/tanstack-app
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}

Terminal window
# .env.example (versiyon kontrolüne ekleyin)
NODE_ENV=development
PORT=3000
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
# Session
SESSION_SECRET=your-session-secret-min-32-chars
# API Keys
API_KEY=your-api-key
SMTP_HOST=smtp.example.com
SMTP_PASS=password
# External Services
REDIS_URL=redis://localhost:6379
S3_BUCKET=your-bucket
S3_REGION=us-east-1
# Feature Flags
ENABLE_ANALYTICS=true
ENABLE_ERROR_REPORTING=true
src/lib/config.ts
export const config = {
nodeEnv: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT || '3000', 10),
databaseUrl: process.env.DATABASE_URL!,
sessionSecret: process.env.SESSION_SECRET!,
apiKey: process.env.API_KEY,
// Validation
isProduction: process.env.NODE_ENV === 'production',
isDevelopment: process.env.NODE_ENV === 'development',
}
// Development'ta eksik değişkenleri uyar
if (config.isDevelopment) {
const required = ['DATABASE_URL', 'SESSION_SECRET']
const missing = required.filter((key) => !process.env[key])
if (missing.length > 0) {
console.warn('⚠️ Missing env vars:', missing.join(', '))
}
}
src/start.ts
import { createStart } from '@tanstack/react-start'
import { z } from 'zod'
// Environment şeması
const envSchema = z.object({
DATABASE_URL: z.string().url(),
SESSION_SECRET: z.string().min(32),
API_KEY: z.string().optional(),
})
// Validate at startup
const env = envSchema.parse(process.env)
export const startInstance = createStart(() => ({
// Config'i geç
config: env,
}))

Terminal window
npm install @sentry/react @sentry/tracing
src/sentry.ts
import * as Sentry from '@sentry/react'
import { BrowserTracing } from '@sentry/tracing'
if (process.env.NODE_ENV === 'production') {
Sentry.init({
dsn: process.env.SENTRY_DSN,
integrations: [
new BrowserTracing(),
],
tracesSampleRate: 0.1, // %10 trace
environment: process.env.NODE_ENV,
beforeSend(event) {
// Hassas verileri temizle
if (event.request?.headers) {
delete event.request.headers['cookie']
delete event.request.headers['authorization']
}
return event
},
})
}
// Web Vitals
import { onCLS, onFID, onFCP, onLCP, onTTFB } from 'web-vitals'
export function reportWebVitals() {
onCLS(console.log)
onFID(console.log)
onFCP(console.log)
onLCP(console.log)
onTTFB(console.log)
}
// Kullanım (app entry point)
reportWebVitals()
src/components/MonitoringDashboard.tsx
export function MonitoringDashboard() {
const { data } = useQuery({
queryKey: ['health'],
queryFn: async () => {
const res = await fetch('/api/health')
return res.json()
},
refetchInterval: 30000, // 30 saniyede bir
})
return (
<div>
<h2>System Status</h2>
<div>Uptime: {data?.uptime}</div>
<div>Memory: {data?.memory}</div>
<div>DB Connection: {data?.db ? '' : ''}</div>
</div>
)
}

// Lazy loading
import { lazy } from 'react'
const HeavyComponent = lazy(() => import('./HeavyComponent'))
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
)
}
src/components/OptimizedImage.tsx
export function OptimizedImage({ src, alt, ...props }: any) {
return (
<img
src={src}
alt={alt}
loading="lazy"
decoding="async"
{...props}
style={{
contentVisibility: 'auto',
...props.style,
}}
/>
)
}
// Cache header'ları
export const cacheHeaders = {
// Statik assets - 1 yıl
'/*.js': { maxAge: 31536000, immutable: true },
'/*.css': { maxAge: 31536000, immutable: true },
'/*.png': { maxAge: 31536000, immutable: true },
// HTML - cache yok
'/': { noCache: true },
'/**/*.html': { noCache: true },
}
Terminal window
# Bundle analyzer yükle
npm install -D rollup-plugin-visualizer
# Build ve analyze
npm run build -- --mode analyze
vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
}),
],
})

src/lib/analytics.ts
export const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_ID
export function pageView(path: string) {
if (typeof window !== 'undefined' && (window as any).gtag) {
;(window as any).gtag('config', GA_TRACKING_ID, {
page_path: path,
})
}
}
export function event(action: string, category: string, label?: string, value?: number) {
if (typeof window !== 'undefined' && (window as any).gtag) {
;(window as any).gtag('event', action, {
event_category: category,
event_label: label,
value: value,
})
}
}
// Route değişikliklerini takip et
import { useRouter } from '@tanstack/react-router'
import { useEffect } from 'react'
import { pageView } from '../lib/analytics'
export function useAnalytics() {
const router = useRouter()
useEffect(() => {
const unsubscribe = router.subscribe((event) => {
if (event.type === 'onLoaded') {
pageView(router.state.location.pathname)
}
})
return unsubscribe
}, [router])
}
// Buton tıklama
function BuyButton() {
const handleClick = () => {
event('purchase', 'ecommerce', 'product_123', 99.99)
}
return <button onClick={handleClick}>Satın Al</button>
}

src/middleware/security.ts
export const securityHeaders = createMiddleware()
.server(async ({ next }) => {
const response = await next()
// Güvenlik header'ları ekle
response.headers.set('X-Content-Type-Options', 'nosniff')
response.headers.set('X-Frame-Options', 'DENY')
response.headers.set('X-XSS-Protection', '1; mode=block')
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
response.headers.set('Permissions-Policy', 'geolocation=(), microphone=()')
// CSP
response.headers.set(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;"
)
return response
})
src/lib/rate-limit.ts
import { createMiddleware } from '@tanstack/react-start'
const rateLimiter = new Map<string, { count: number; reset: number }>()
export const rateLimitMiddleware = createMiddleware()
.server(async ({ next }) => {
const ip = headers.get('x-forwarded-for') || 'unknown'
const now = Date.now()
const windowMs = 60 * 1000 // 1 dakika
const maxRequests = 100
const record = rateLimiter.get(ip)
if (!record || now > record.reset) {
rateLimiter.set(ip, { count: 1, reset: now + windowMs })
return next()
}
if (record.count >= maxRequests) {
throw new Error('Rate limit exceeded')
}
record.count++
return next()
})
// XSS koruması
import DOMPurify from 'dompurify'
export function sanitizeHTML(html: string): string {
if (typeof window === 'undefined') {
return html // Server'da işlem yapma
}
return DOMPurify.sanitize(html)
}
// Kullanım
const clean = sanitizeHTML(userInput)

🎨 Pratik Örnek: Production-Ready Uygulama

Section titled “🎨 Pratik Örnek: Production-Ready Uygulama”

Hadi öğrendiklerimizle production-ready bir yapı oluşturalım!

my-app/
├── src/
│ ├── start.ts # Entry point
│ ├── routes/ # Routes
│ ├── lib/
│ │ ├── config.ts # Environment config
│ │ ├── db.ts # Database
│ │ └── auth.ts # Auth
│ ├── middleware/
│ │ ├── security.ts # Security headers
│ │ └── auth.ts # Auth check
│ └── components/
├── public/ # Static assets
├── .env.example
├── Dockerfile
├── docker-compose.yml
├── vercel.json
└── package.json
src/start.ts
import { createStart } from '@tanstack/react-start'
import { securityHeaders } from './middleware/security'
export const startInstance = createStart(() => ({
requestMiddleware: [securityHeaders],
// Global error handling
onError: (error) => {
console.error('[ERROR]', error)
// Production'da monitoring servisine gönder
if (process.env.NODE_ENV === 'production') {
// sendToSentry(error)
}
},
}))
src/routes/api/health.tsx
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/api/health')({
loader: async () => {
const health = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
environment: process.env.NODE_ENV,
}
return new Response(JSON.stringify(health), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
},
})

✅ Ders 10 Özeti - Tüm Müfredatın Özeti

Section titled “✅ Ders 10 Özeti - Tüm Müfredatın Özeti”

Bu derste öğrendiklerimiz:

KonuAçıklama
BuildProduction build oluşturma
VercelÖnerilen deploy platformu
DockerContainerized deploy
Environment varsKonfigürasyon yönetimi
MonitoringHata takibi ve performans
SecurityGüvenlik best practice’leri
DersKonuAna Öğrenmeler
01Giriş ve KurulumProje kurulumu, temel yapı
02Routing - TemelFile-based routing, Link, Outlet
03Routing - OrtaSearch params, loaders, redirects
04Server Functions - GirişcreateServerFn, GET/POST
05Server Functions - OrtaMiddleware, context, session
06State ManagementTanStack Query, mutations
07SSR ve RenderingSSR, SPA, static prerendering
08AuthenticationSession, JWT, OAuth
09Form YönetimiValidasyon, multi-step forms
10DeploymentBuild, deploy, monitoring

Öğrendiklerinizin tamamını kullanarak tam bir e-ticaret uygulaması yapın:

Gereksinimler:

  1. Routing

    • Ana sayfa (SSR)
    • Ürün listesi (SSR)
    • Ürün detay (SSR)
    • Sepet (SPA)
    • Checkout (SPA + auth)
    • Admin panel (SPA + auth)
  2. Server Functions

    • Ürün CRUD
    • Sipariş oluştur
    • Ödeme işlemleri
    • Admin istatistikleri
  3. Authentication

    • Kullanıcı girişi
    • Admin yetkisi
    • Session yönetimi
  4. Forms

    • Ürün ekleme formu (admin)
    • Checkout formu
    • İletişim formu
  5. Deployment

    • Production build
    • Environment config
    • Deploy (Vercel/Docker)

TanStack Start müfredatını tamamladınız! Artık:

✅ Modern, full-stack React uygulamaları geliştirebilirsiniz ✅ Type-safe routing ve server functions kullanabilirsiniz ✅ Authentication ve authorization sistemleri kurabilirsiniz ✅ Production-ready uygulamaları deploy edebilirsiniz

  1. Kendi projenizi oluşturun
  2. TanStack Router’ı derinlemesine inceleyin
  3. TanStack Query ile complex state yönetimi öğrenin
  4. Testing stratejileri araştırın

Herhangi bir sorunuz varsa belirtmekten çekinmeyin!

İyi kodlamalar! 🚀👋