StriveLab
Strony internetowe
Usługi
RealizacjeO mnieBlogPorozmawiajmy
PL
EN
StriveLab
Strony internetowe
Usługi
RealizacjeO mnieBlogPorozmawiajmy
PL
EN

Next.js

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.

React

Połączenie intuicyjności z wydajnością, które zapewnia bezproblemową skalowalność kodu.

Astro

Ultraszybkie projekty, łączące lekkość ze skalowalnością.

Doradztwo produktowe

Połączenie perspektywy produktu, developera i marketingu w jednym miejscu

QA & Automation

Testy automatyczne komponentów i E2E w oparciu o Cypress.

SEO & Performance

Audyt techniczny i optymalizacja pod kątem SEO i GEO.

RealizacjeO mnieBlog
Porozmawiajmy
PL
EN

Nowoczesne strony internetowe dla firm, które myślą odważnie.

Przewiń do góry

Nazwa

StriveLab Maciej Sala

NIP

6772218995

REGON

524008527

E-mail

contact@strivelab.pl

Usługi główne
  • Tworzenie stron internetowych
  • Strony internetowe Next.js
  • Strony internetowe Astro
  • Strony internetowe React
Inne usługi
  • Usługi
  • SEO & Performance Sprint
  • QA & Stabilizacja
  • Konsultacje Product / Delivery
  • Aplikacje webowe Next.js
  • Współpraca ciągła
Strony
  • O mnie
  • Usługi
  • Realizacje
  • Blog

© 2026 StriveLab.pl

Polityka prywatności
Backend

Backend dla frontendowca: cache, deployment i bezpieczeństwo

Trzecia część serii Backend dla frontendowca: Redis, HTTP cache, kolejki, file storage, deployment, CI/CD, monitoring, OWASP, rate limiting i RODO.

OpublikujLinkedInFacebookWyślij
Autor
Maciej Sala
Opublikowano
30 lipca 2025 11:45
Czytanie
15 min czytania
Aktualizacja
1 maja 2026 16:30

W skrócie

  • Cache invalidation jest trudna — cache przyspiesza, ale unieważnianie po zmianach danych wymaga strategii (TTL, event-driven, manualne czyszczenie)
  • Kolejki dla operacji blokujących — wysyłka maili, przetwarzanie plików i powiadomienia powinny trafiać do kolejki, nie blokować requestu
  • Presigned URLs dla plików — klient przesyła pliki bezpośrednio do S3/R2 przez presigned URL; serwer nie musi obsługiwać uploadu
  • CI/CD jako minimum — każdy projekt powinien mieć pipeline który automatycznie testuje i deployuje; ręczny deployment to błędy w piątek
  • Trzy filary obserwowalności — logi (co się stało), metryki (ile/jak szybko) i trace (gdzie się zatrzymało) razem dają pełny obraz
  • Bezpieczeństwo to proces — SQL injection, XSS, CSRF i rate limiting to nie opcje, lecz podstawa każdego projektu

To trzecia część serii Backend dla frontendowca. Po fundamentach API oraz tematach real-time, webhooków i auth zostaje warstwa, która decyduje o tym, czy aplikacja wytrzyma prawdziwe użytkowanie: cache, kolejki, pliki, deployment, monitoring i bezpieczeństwo.

To nie są dodatki „na później”, jeśli projekt ma działać poza lokalnym środowiskiem. Cache potrafi zdjąć ogromną część ruchu z bazy, ale może też zwracać nieaktualne dane. Kolejka ratuje UX przy wolnych operacjach, ale wymaga idempotencji. Deployment bez monitoringu daje tylko wiarę, że wszystko działa.

Krótka odpowiedź: cache'uj tylko te dane, których świeżość rozumiesz, ciężkie operacje przerzucaj do kolejek, pliki trzymaj w object storage, deploy automatyzuj przez CI/CD, a produkcję obserwuj przez logi, metryki i alerty. Bezpieczeństwo traktuj jako stały proces: walidacja, uprawnienia, rate limiting, sekrety, nagłówki i regularne aktualizacje zależności.

Notatka

Cache, kolejki i monitoring nie są ozdobą dla dużych systemów. To mechanizmy, które decydują, czy aplikacja degraduje się przewidywalnie, czy zaczyna losowo zwracać stare dane, duplikować zadania i ukrywać błędy produkcyjne.

1. Cache — przyspieszanie odpowiedzi

Cache nie jest tylko optymalizacją na koniec projektu. To sposób na zdjęcie pracy z bazy, API zewnętrznych i serwera, ale też źródło trudnych błędów, jeśli nie wiesz, kiedy dane stają się nieaktualne.

Redis

Redis to in-memory database często używana jako cache, storage sesji, kolejka albo mechanizm pub/sub. W najprostszym wariancie trzymasz w nim wynik wolnego zapytania przez kilka minut:

Code
// Sprawdź cache
const cached = await redis.get('users:all')
if (cached) return JSON.parse(cached)
 
// Pobierz z bazy
const users = await db.query('SELECT * FROM users')
 
// Zapisz w cache na 5 minut
await redis.set('users:all', JSON.stringify(users), 'EX', 300)
 
return users

W praktyce równie ważna jak samo zapisanie jest strategia unieważniania cache po zmianie danych.

Kiedy cachować?

Cache ma sens tam, gdzie odczyt jest częsty, a zmiana rzadsza albo akceptujesz chwilowo nieświeże dane. Nie cachuj wszystkiego automatycznie, bo każdy cache trzeba potem unieważnić.

  • Dane często czytane, rzadko zmieniane
  • Wolne zapytania do bazy
  • Zewnętrzne API calls
  • Obliczenia / agregacje

Strategie cache

Cache-aside (lazy loading) — najczęstsze:

Code
1. Sprawdź cache
2. Brak? → pobierz z bazy → zapisz do cache → zwróć

Write-through — zapis idzie najpierw do cache, potem do bazy. Cache zawsze świeży.

Write-behind — zapis do cache od razu, do bazy asynchronicznie. Najszybsze, ale ryzyko utraty danych.

Stale-while-revalidate — zwróć stary cache od razu, w tle odśwież.

Cache invalidation — najtrudniejsza rzecz w computer science

"There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton

Problem polega na tym, że cache przyspiesza odczyt, ale oddala Cię od źródła prawdy. Masz trzy podstawowe podejścia:

  1. TTL (expire after N) — proste, przewidywalne, ale dane mogą być nieaktualne przez chwilę.
  2. Event-driven — po UPDATE w bazie wyczyść konkretne klucze (del('user:123')).
  3. Versioning — klucz zawiera wersję (user:123:v5); zmieniasz wersję, stare wpisy zostają, ale nikt ich nie czyta.
Code
// Event-driven invalidation
async function updateUser(id, data) {
  await db.user.update({ where: { id }, data })
  await redis.del(`user:${id}`) // pojedynczy klucz
  await redis.del(`users:list:*`) // pattern (uwaga na wydajność)
}

HTTP cache — drugi (i pierwszy) poziom

Backend Redis to nie wszystko — przeglądarka i CDN cachują na bazie HTTP headerów. Frontendowiec to widzi w Network tabie jako (disk cache) / (memory cache) / from CDN.

Code
Cache-Control: public, max-age=3600, stale-while-revalidate=86400
ETag: "abc123"
Last-Modified: Wed, 15 Apr 2026 10:00:00 GMT
HeaderCo robi
Cache-Control: publicCDN może cachować
Cache-Control: privateTylko przeglądarka usera
Cache-Control: no-storeNie cachuj wcale
Cache-Control: no-cacheCachuj, ale zawsze rewaliduj
max-age=3600Cache świeży przez godzinę
s-maxage=3600TTL tylko dla CDN (override max-age)
stale-while-revalidate=NPo expiry serwuj stary, w tle pobierz świeży
stale-if-error=NPo błędzie serwuj stary (graceful degradation)
ETag: "abc"Hash zawartości — przeglądarka pyta "czy jest nowsze?"

Conditional request:

Code
GET /api/posts/1
If-None-Match: "abc123"

→ 304 Not Modified  (zero bajtów body!)

Trzy poziomy cache

Code
Browser cache  →  CDN (Cloudflare, Fastly)  →  Redis (backend)  →  Database
   100ms              30ms                       2ms                100ms+

Dobra aplikacja cachuje na każdym poziomie, gdzie ma to sens.

Next.js / framework caches

Nowoczesne frameworki mają własne warstwy:

  • Next.js Data Cache — cachuje wyniki fetch() z revalidate
  • Next.js Full Route Cache — całe HTML strony statycznej
  • React cache() — deduplikacja w obrębie jednego renderu
  • unstable_cache — explicit cache z tagami i revalidacją
Code
import { unstable_cache, revalidateTag } from 'next/cache'
 
const getPosts = unstable_cache(async () => db.post.findMany(), ['posts'], {
  revalidate: 3600,
  tags: ['posts'],
})
 
// Po update:
revalidateTag('posts')

2. Kolejki i background jobs

Nie każdy proces powinien kończyć się w czasie jednego requestu. Wysyłka maila, przeliczenie raportu, generowanie PDF-a, synchronizacja CRM czy przetwarzanie płatności mogą trwać za długo albo zależeć od zewnętrznego systemu. Kolejka pozwala szybko odpowiedzieć użytkownikowi, a ciężką pracę wykonać w tle.

Code
// Zamiast:
app.post('/api/send-email', async (req, res) => {
  await sendEmail(req.body) // 2-3 sekundy!
  res.json({ success: true })
})
 
// Lepiej:
app.post('/api/send-email', async (req, res) => {
  await queue.add('send-email', req.body) // Natychmiast
  res.json({ queued: true })
})
 
// Worker przetwarza w tle
queue.process('send-email', async (job) => {
  await sendEmail(job.data)
})

Popularne: BullMQ (Redis), RabbitMQ, AWS SQS, Inngest, Trigger.dev (event-driven, type-safe).

Cron / scheduled jobs

Cron to praca cykliczna: raport nocny, czyszczenie cache, naliczenie subskrypcji, przypomnienia mailowe. Prosty mechanizm, ale w produkcji musi być idempotentny i monitorowany, bo dwa równoległe uruchomienia potrafią narobić szkód.

Code
// node-cron
import cron from 'node-cron'
 
cron.schedule('0 2 * * *', async () => {
  // codziennie o 2:00
  await sendDailyDigest()
})

Reguły dla cron:

  • Idempotency — zadanie może odpalić się dwa razy (deploy, retry)
  • Lock — przy wielu instancjach tylko jedna ma wykonać (Redis lock, advisory lock w Postgres)
  • Monitoring — alert jeśli zadanie się nie wykonało (Healthchecks.io, Better Stack)

W produkcji użyj zarządzanej platformy: Vercel Cron, Cloudflare Cron Triggers, Inngest, GitHub Actions (cron + curl), Kubernetes CronJob.

Wzorce przetwarzania

  • Fan-out — jeden event → wiele jobs (np. nowy user → wyślij email + utwórz workspace + zapisz w CRM)
  • Pipeline — job A → job B → job C
  • Retry with backoff — przy błędzie spróbuj ponownie z opóźnieniem (1s, 2s, 4s, 8s...)
  • Dead letter queue — joby, które padły N razy, lądują na DLQ do manualnej analizy

3. File storage

Pliki mają inne wymagania niż rekordy w bazie. Obrazy, dokumenty, avatary i eksportowane PDF-y zwykle nie powinny trafiać bezpośrednio do relacyjnej bazy danych. Najczęściej zapisujesz je w object storage, a w bazie trzymasz tylko metadane i ścieżkę.

Code
const key = `uploads/${filename}`
 
await storage.putObject({
  bucket: 'my-bucket',
  key,
  body: fileBuffer,
  contentType: mimeType,
})
 
// Zapisz URL w bazie
await db.user.update({
  where: { id: userId },
  data: { avatar: `https://cdn.example.com/${key}` },
})

Popularne: AWS S3, Cloudflare R2 (zero egress fee!), Google Cloud Storage, Backblaze B2, MinIO (self-hosted).

Presigned URLs — upload bezpośrednio z frontu

W większych systemach backend nie powinien przepuszczać każdego pliku przez siebie. Zamiast tego generuje presigned URL, a frontend uploaduje plik bezpośrednio do storage. Backend kontroluje uprawnienia i zapisuje metadane, ale nie musi trzymać całego pliku w pamięci.

Code
// Backend — generuje presigned URL
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { PutObjectCommand } from '@aws-sdk/client-s3'
 
app.post('/api/upload-url', async (req, res) => {
  const key = `uploads/${userId}/${crypto.randomUUID()}-${filename}`
 
  const url = await getSignedUrl(
    s3,
    new PutObjectCommand({ Bucket, Key: key, ContentType: req.body.type }),
    { expiresIn: 300 } // 5 minut
  )
 
  res.json({ url, key })
})
 
// Frontend — upload bezpośrednio do S3
const { url, key } = await fetch('/api/upload-url', { ... }).then(r => r.json())
await fetch(url, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } })
await fetch('/api/save-meta', { method: 'POST', body: JSON.stringify({ key }) })

Plusy:

  • Backend nie przepuszcza pliku przez siebie — oszczędza pamięć i bandwidth
  • Skalowalne — S3 wytrzyma więcej niż Twój serwer
  • Szybsze dla usera — brak hop-a przez backend

CDN dla statycznych plików

Pliki publiczne (avatary, obrazki postów) idą zwykle przez CDN — Cloudflare, Bunny, CloudFront:

Code
Origin: https://my-bucket.s3.amazonaws.com/uploads/avatar.jpg
CDN:    https://cdn.example.com/uploads/avatar.jpg  (bliżej usera, cachowane)

Optymalizacja obrazów

Surowe obrazy są ciężkie. Najpopularniejsze podejścia:

  • Next.js Image — automatyczne resize, WebP / AVIF, lazy loading
  • Cloudinary / imgix / Cloudflare Images — managed image CDN z transformacjami w URL
  • Sharp (Node) — backend-side resize / optimize przy upload

4. Środowiska i deployment

Kod działający lokalnie to dopiero początek. Backend musi mieć środowiska, sekrety, migracje, monitoring, backupy i powtarzalny deployment. Bez tego każda zmiana na produkcji zaczyna przypominać ręczną operację na żywym organizmie.

Środowiska

Code
Development (localhost)
    ↓
Staging (testowanie)
    ↓
Production (użytkownicy)

Zmienne środowiskowe

Code
# .env
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
JWT_SECRET=super-secret-key
REDIS_URL=redis://localhost:6379
Code
const dbUrl = process.env.DATABASE_URL

Nigdy nie commituj .env do repo!

Opcje deploymentu

Nie ma jednego najlepszego hostingu dla backendu. Wybór zależy od tego, czy chcesz prostoty, kontroli, globalnego edge'a, czy przewidywalnych kosztów.

Platform as a Service (łatwe):

  • Vercel (Next.js)
  • Railway
  • Render
  • Heroku

Container (więcej kontroli):

  • Docker + Kubernetes
  • AWS ECS
  • Google Cloud Run

VPS (pełna kontrola):

  • DigitalOcean
  • AWS EC2
  • Hetzner (Niemcy, świetna cena/wydajność)
  • Coolify (self-hosted PaaS na własnym VPS)

Edge / serverless:

  • Cloudflare Workers
  • Vercel Edge Functions
  • Deno Deploy
  • AWS Lambda + API Gateway
  • Fly.io (globalne VM)

CI/CD — automatyzacja deploymentu

Deploy nie powinien zależeć od tego, kto ma akurat poprawnie skonfigurowany laptop. Pipeline w GitHub Actions albo GitLab CI sprawia, że testy, build i wdrożenie są powtarzalne:

Code
# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test
      - run: npm run lint
 
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }}

Strategie deploymentu

  • Blue-green — dwa środowiska, switch ruchu w jednej chwili (zero downtime)
  • Canary — nowa wersja dostaje 1% ruchu, jeśli nie pada → 10% → 50% → 100%
  • Rolling — instancje aktualizowane po kolei (Kubernetes default)
  • Feature flags — kod wdrożony, ale wyłączony, włączony per user / cohort (LaunchDarkly, Unleash, GrowthBook)

Backups i disaster recovery

Backup jest użyteczny dopiero wtedy, gdy potrafisz go odtworzyć. Sama informacja „mamy backupy” niewiele znaczy, jeśli nikt nigdy nie sprawdził restore'u.

  • Automatyczne backupy bazy (PostgreSQL: pg_dump, point-in-time recovery)
  • Test restore — backup, którego nie restartowałeś, nie istnieje
  • RTO (Recovery Time Objective) — jak długo możesz być down
  • RPO (Recovery Point Objective) — ile danych możesz stracić

5. Monitoring i logging

Bez monitoringu backend jest czarną skrzynką. Widzisz tylko, że użytkownik zgłasza problem, ale nie wiesz, czy zawiodła baza, zewnętrzne API, deployment, cache czy konkretna ścieżka w kodzie.

Logi

Code
// Prosty logging
console.log('User created:', userId)
 
// Strukturalny logging
logger.info('User created', {
  userId,
  email,
  timestamp: new Date(),
})

Narzędzia: Winston, Pino, Datadog, Papertrail

Monitoring

Monitoring odpowiada na pytanie, czy system działa zdrowo. Logi mówią, co się wydarzyło w konkretnym przypadku, a metryki pokazują trend i skalę problemu.

  • Uptime — czy serwer działa?
  • Latency — jak szybko odpowiada?
  • Errors — ile błędów 5xx?
  • Resources — CPU, RAM, disk

Narzędzia: Grafana, Datadog, New Relic, Sentry (errors), Better Stack, Honeycomb.

Trzy filary observability

  1. Logs — co się stało (event-by-event)
  2. Metrics — ile czego (latency, throughput, errors)
  3. Traces — pełna ścieżka requestu przez system (request-id przez wszystkie usługi)

Distributed tracing

W większych systemach pojedynczy request często przechodzi przez kilka usług. Bez trace'u widzisz tylko „endpoint trwa 800 ms”. Z trace'em widzisz, że 600 ms zjada konkretne zapytanie do bazy albo jedna integracja zewnętrzna.

Code
[trace-id: abc] frontend → API gateway → user service → auth service → database
                  120ms    8ms           45ms           12ms          50ms

Standard: OpenTelemetry (otel) — agnostic; eksportuje do Datadog, Honeycomb, Tempo, Jaeger.

Alerty — co budzi w nocy

Alert powinien odpowiadać na objaw, nie na przyczynę. SRE Google wyznacza tzw. "Four Golden Signals":

  • Latency — p50 / p95 / p99 czasu odpowiedzi
  • Traffic — RPS (requests per second)
  • Errors — % błędów 5xx
  • Saturation — wykorzystanie CPU, RAM, disku, pool connections

Alert niech budzi tylko, gdy user faktycznie cierpi. Reszta to ticket "do obejrzenia".

Frontend → backend observability

Frontend też powinien wysyłać błędy i metryki. Sentry, LogRocket, Datadog RUM — pokazują jak user dochodzi do błędu. Powiąż user-id / request-id między frontem a backendem, żeby widzieć całą ścieżkę.

6. Bezpieczeństwo

Bezpieczeństwo backendu nie polega na jednej bibliotece ani checkliście odhaczonej przed deployem. To zestaw nawyków: walidujesz dane, ograniczasz uprawnienia, nie ufasz klientowi, logujesz zdarzenia i zakładasz, że każdy publiczny endpoint będzie testowany przez kogoś nieżyczliwego.

Podstawowe zasady

  1. Waliduj input — nigdy nie ufaj danym od usera
  2. Parametryzuj zapytania — unikaj SQL injection
  3. Hashuj hasła — bcrypt, argon2
  4. HTTPS everywhere — szyfruj transmisję
  5. Rate limiting — ogranicz requesty
  6. CORS — kontroluj kto może odpytywać API
Code
// ❌ SQL Injection
db.query(`SELECT * FROM users WHERE id = ${userId}`)
 
// ✅ Parametryzowane zapytanie
db.query('SELECT * FROM users WHERE id = $1', [userId])
Code
// ❌ Przechowywanie hasła
user.password = 'password123'
 
// ✅ Hashowanie
user.passwordHash = await bcrypt.hash('password123', 10)

Rate limiting w praktyce

Code
import rateLimit from 'express-rate-limit'
 
// Globalny limit
app.use(
  '/api/',
  rateLimit({
    windowMs: 60 * 1000, // okno 1 minuta
    max: 60, // 60 requestów / minutę / IP
    standardHeaders: true, // X-RateLimit-* + Retry-After
    legacyHeaders: false,
    message: {
      type: 'about:blank',
      title: 'Too Many Requests',
      status: 429,
      detail: 'Try again in a moment',
    },
  }),
)
 
// Bardziej restrykcyjny dla loginu
app.post(
  '/api/auth/login',
  rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minut
    max: 5, // 5 prób
    skipSuccessfulRequests: true,
  }),
  loginHandler,
)

Frontend reaguje na 429:

Code
async function fetchWithRetry(url, opts, attempt = 0) {
  const res = await fetch(url, opts)
  if (res.status === 429 && attempt < 3) {
    const retryAfter = Number(res.headers.get('Retry-After')) || 2 ** attempt
    await new Promise((r) => setTimeout(r, retryAfter * 1000))
    return fetchWithRetry(url, opts, attempt + 1)
  }
  return res
}

W produkcji rate limiting zwykle siedzi na CDN / WAF (Cloudflare, AWS WAF), nie w aplikacji — chroni przed DDoS i nie zużywa zasobów backendu.

OWASP Top 10 — must-know

OWASP Top 10 to lista najczęstszych kategorii zagrożeń aplikacji webowych. Nie musisz znać każdego scenariusza ataku na pamięć, ale jako frontendowiec powinieneś rozumieć, które problemy są rozwiązywane wyłącznie po stronie backendu.

  1. Broken Access Control — sprawdzanie uprawnień na backendzie, nie tylko ukrywanie buttonów w UI
  2. Cryptographic Failures — HTTPS wszędzie, nigdy nie loguj danych wrażliwych
  3. Injection — SQL injection, XSS, NoSQL injection, command injection
  4. Insecure Design — np. brak rate limitu na reset hasła
  5. Security Misconfiguration — domyślne hasła, otwarte porty, debug w prod
  6. Vulnerable Components — npm audit, Dependabot, Snyk
  7. Authentication Failures — słabe hasła, brak MFA, długie sesje
  8. Software and Data Integrity Failures — niepodpisane updates, supply chain attacks
  9. Logging and Monitoring Failures — nie wiesz, że Cię włamano
  10. Server-Side Request Forgery (SSRF) — backend pobiera URL od usera bez walidacji

XSS — Cross-Site Scripting

Atakujący wstrzykuje JS w kontekst Twojej domeny (komentarz, profil). Skradzione cookies, wyświetlone fałszywe formularze.

Obrona:

  • Escapuj output — React i większość frameworków robi to domyślnie. Uważaj na dangerouslySetInnerHTML.
  • Content-Security-Policy (CSP) — header, który mówi przeglądarce, skąd wolno ładować skrypty:
    Code
    Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com
    
  • Sanityzuj HTML od usera — DOMPurify, sanitize-html

Walidacja input — nigdy nie ufaj klientowi

Walidacja w formularzu poprawia UX. Walidacja na serwerze chroni system. Użytkownik może wyłączyć JavaScript, zmodyfikować payload w DevTools albo wysłać request z własnego skryptu.

Code
import { z } from 'zod'
 
const CreateUserSchema = z.object({
  email: z.string().email().max(255),
  age: z.number().int().min(13).max(120),
  role: z.enum(['user', 'admin']),
})
 
app.post('/api/users', (req, res) => {
  const result = CreateUserSchema.safeParse(req.body)
  if (!result.success) {
    return res.status(422).json({
      type: 'about:blank',
      title: 'Validation failed',
      status: 422,
      errors: result.error.flatten().fieldErrors,
    })
  }
  // result.data jest typowane i zwalidowane
})

Reguła: klient waliduje dla UX, serwer waliduje dla bezpieczeństwa.

Mass assignment

Code
// ❌ Niebezpieczne — user może wysłać { role: 'admin' } w body
await db.user.update({ where: { id }, data: req.body })
 
// ✅ Tylko allowlistowane pola
const { name, bio } = req.body
await db.user.update({ where: { id }, data: { name, bio } })

Secret management

Sekrety nigdy nie idą do repo, nawet do prywatnego.

  • Lokalnie: .env + .gitignore, direnv, 1Password CLI
  • CI/CD: GitHub Secrets, GitLab CI Variables
  • Produkcja: AWS Secrets Manager, HashiCorp Vault, Doppler, Infisical
  • Rotacja — sekrety powinny mieć datę ważności (klucze API, hasła do bazy)

Jeśli sekret kiedyś trafił do repo (nawet usuniętego commita) — uznaj go za skompromitowany i wymień. Git history żyje wiecznie.

Security headers

Standardowe headery, które dodaje helmet:

Code
import helmet from 'helmet'
app.use(helmet())

Zwraca między innymi:

  • Strict-Transport-Security — wymuszaj HTTPS przez N dni (HSTS)
  • X-Content-Type-Options: nosniff — przeglądarka nie zgaduje MIME
  • X-Frame-Options: DENY — chroni przed clickjacking
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy — wyłącza geolokalizację, kamerę itd. domyślnie

Sprawdź swoje headery na securityheaders.com.

GDPR / RODO — minimalne minimum

Jeśli zbierasz dane od userów z UE:

  • Cel przetwarzania — wiedz, po co masz dane
  • Right to access / erasure — endpoint pozwalający exportować i kasować dane
  • Pseudonimizacja — adres IP w logach maskuj
  • Consent — banner cookie dla narzędzi analitycznych
  • DPA — umowa powierzenia z każdym vendorem przetwarzającym dane

Co dalej? — ścieżka nauki

Nie próbuj uczyć się backendu przez czytanie wszystkiego naraz. Najszybciej rośnie się przez mały projekt, który dotyka kilku prawdziwych problemów: logowania, bazy, walidacji, uploadu, webhooka i deploymentu. Poniższa ścieżka układa tematy od fundamentów do architektury.

Poziom 1: Fundamenty

  1. HTTP — request / response, kody statusu, headery
  2. Node.js + Express (lub Hono / Fastify)
  3. SQL podstawy — CRUD, JOIN, indeksy, transakcje
  4. REST API design — zasoby, metody, status codes
  5. CORS — co to, kiedy psuje requesty, jak konfigurować

Poziom 2: Praktyka

  1. PostgreSQL + Prisma / Drizzle
  2. Autentykacja — sessions vs JWT, cookies, CSRF
  3. Walidacja input (Zod) i strukturalne błędy (RFC 9457)
  4. Deployment — Vercel / Railway / Render
  5. Webhooks — przyjmowanie i wysyłanie

Poziom 3: Zaawansowane

  1. Redis — cache strategies, pub/sub, kolejki (BullMQ)
  2. Real-time — SSE i WebSockets
  3. Docker + CI/CD pipeline
  4. Monitoring / logging / tracing (OpenTelemetry, Sentry)
  5. Connection pooling / serverless drivery (pgbouncer, Neon)

Poziom 4: Architektura

  1. Microservices vs monolith
  2. Event-driven architecture, queues
  3. CQRS, Event Sourcing
  4. Distributed systems — CAP theorem, eventual consistency
  5. Load testing (k6, Artillery)

Projekt do nauki

Najlepszy projekt do nauki nie musi być oryginalny. Ma zmusić Cię do przejścia przez typowe decyzje backendowe. Dobrym kandydatem jest prosty blog z autentykacją:

  • Rejestracja / logowanie (sesja albo JWT)
  • CRUD to skrót od Create, Read, Update, Delete, czyli podstawowych operacji wykonywanych na danych. postów z paginacją
  • Komentarze (real-time z SSE — bonus)
  • Upload obrazów (presigned URLs do S3 / R2)
  • Webhook handler (Stripe sandbox jako "płatne posty")
  • Deployment + CI/CD
  • Sentry / monitoring

Taki projekt nie wygląda efektownie na pierwszy rzut oka, ale pokrywa większość problemów, które wracają w komercyjnych aplikacjach: stan użytkownika, dane, uprawnienia, pliki, integracje, błędy i deployment.

Źródła i dokumentacja dla całej serii

Fundamenty:

  • MDN: An overview of HTTP
  • MDN: CORS
  • MDN: HTTP cookies
  • MDN: HTTP caching
  • Node.js Learn

Bazy danych:

  • PostgreSQL Tutorial
  • Use The Index, Luke! — SQL performance
  • Prisma docs

API design:

  • REST API design — Microsoft
  • JSON:API specification
  • RFC 9457: Problem Details for HTTP APIs
  • Stripe API design — postmortem

Bezpieczeństwo:

  • OWASP Top 10
  • OWASP Authentication Cheat Sheet
  • OWASP Cross-Site Request Forgery Prevention
  • web.dev: Security headers

Real-time:

  • MDN: Server-sent events
  • MDN: WebSockets API

Architektura:

  • Google SRE: Monitoring distributed systems
  • Martin Fowler: Microservices

Podsumowanie

Jeżeli masz zapamiętać z tej serii jedną rzecz, niech będzie nią ta: backend to nie osobna magia, tylko zestaw warstw, które muszą ze sobą sensownie współpracować. Frontendowiec nie musi znać każdej z nich na poziomie senior backend engineera, ale powinien rozumieć, gdzie leży odpowiedzialność i jak rozmawiać o problemach.

KomponentRolaPopularne (2026)
SerwerLogika aplikacjiExpress, Fastify, Hono, NestJS
RuntimeWykonanie koduNode.js, Bun, Deno, Cloudflare Workers
Baza danychPrzechowywaniePostgreSQL, MySQL, MongoDB, SQLite (Turso)
ORM / query builderAbstrakcja bazyPrisma, Drizzle, Kysely
CachePrzyspieszanieRedis, Memcached, HTTP cache
AuthBezpieczeństwoAuth.js, Clerk, Supabase Auth, Lucia
API styleKomunikacjaREST, GraphQL, tRPC
Real-timePush z serweraSSE, WebSockets, Pusher
StoragePlikiS3, Cloudflare R2, R2 + CDN
KolejkiBackground jobsBullMQ, Inngest, Trigger.dev
WalidacjaBezpieczne inputZod, Valibot, Yup
DeploymentHostingVercel, Railway, Fly.io, Render, Cloudflare
ObservabilityCo się dziejeSentry, Datadog, Better Stack, Honeycomb

Najczęstsze pułapki dla frontendowca

Na koniec warto zebrać te błędy w jednym miejscu. To rzeczy, które najczęściej wychodzą dopiero przy prawdziwym ruchu, wielu użytkownikach albo produkcyjnych integracjach:

  • Traktowanie wszystkich błędów 4xx jak 5xx — 401, 403, 422, 429 wymagają różnych reakcji UI
  • Brak obsługi Retry-After przy 429 — bombardujesz API i pogarszasz problem
  • Wszystkie listy bez paginacji — działa, dopóki ktoś nie doda 10 000 rekordów
  • Cookies + CORS bez credentials: 'include' — ślepy zaułek 4h debugowania
  • POST bez idempotency — duplikaty po retry / podwójnym kliku
  • JWT bez refresh rotation — albo długi token (niebezpieczny), albo user wylogowuje się co 15 min
  • Brak cache HTTP — backend Redis OK, ale nadal każdy user ściąga to samo
  • Pomijanie walidacji na serwerze — walidacja w UI to UX, nie security

Tematu backendu nie trzeba się bać. Zacznij od prostego API, dodawaj kolejne elementy dopiero wtedy, gdy rozumiesz, po co są potrzebne, i ucz się na błędach w małych projektach. Jako frontendowiec nie musisz być backendowcem, ale musisz rozumieć, co dzieje się po drugiej stronie API — to jedna z najkrótszych dróg do lepszych decyzji produktowych i spokojniejszego debugowania.


Chcesz praktyczny tutorial? Sprawdź artykuł Prisma + Next.js — zbuduj fullstack app od zera.

Pozostałe części serii

  • Backend dla frontendowca: serwer, bazy danych i API
  • Backend dla frontendowca: auth, real-time i integracje

Często zadawane pytania

Redis to in-memory data store, czyli baza trzymająca dane głównie w pamięci RAM. Najczęściej używa się go jako cache, storage sesji, kolejki zadań albo mechanizmu pub/sub dla real-time. Redis nie zastępuje relacyjnej bazy danych — jest warstwą przyspieszającą albo pomocniczą.

Pracuję z tym zawodowo.

Jeśli chcesz przełożyć ten temat na lepszą architekturę frontendu, uporządkować React lub Next.js i podnieść jakość pracy zespołu, skontaktuj się ze mną. Pomagam zamieniać wiedzę z artykułów w praktyczne decyzje technologiczne.

Skontaktuj się ze mną
Maciej Sala

O autorze

Maciej Sala

Maciej Sala — project manager i frontendowiec z doświadczeniem w marketingu internetowym. Na co dzień pracuję z Reactem, Next.js i TypeScriptem, łącząc perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat związany z branżą gier wideo jako project manager i game designer.

Absolwent historii na Uniwersytecie Jagiellońskim i studiów podyplomowych z marketingu internetowego na Akademii Górniczo-Hutniczej w Krakowie. Poza pracą trenuje na siłowni, maluje figurki i realizuje własne projekty.

Moje artykułyWięcej o mnie

Seria

Backend dla frontendowca
Część 3 / 3
  1. 1Backend dla frontendowca: serwer, bazy danych i API
  2. 2Backend dla frontendowca: auth, real-time i integracje
  3. Backend dla frontendowca: cache, deployment i bezpieczeństwo
Poprzedni wpisBackend dla frontendowca: auth, real-time i integracjeDruga część serii Backend dla frontendowca: SSE, WebSockets, polling, webhooki, sesje, JWT, cookies, CSRF, refresh token rotation i MFA.
Maciej Sala

Maciej Sala

Founder Strivelab

29 lipca 2025
Następny wpisClaude vs ChatGPT vs Gemini — porównanie dla deweloperówPraktyczne porównanie Claude, ChatGPT i Gemini z perspektywy dewelopera. Kodowanie, analiza, API, prywatność i workflow — kiedy które narzędzie ma sens.
Maciej Sala

Maciej Sala

Founder Strivelab

12 sierpnia 2025

Spis treści

10 sekcji · 17 min

  • 1. Cache — przyspieszanie odpowiedzi2 min
  • 2. Kolejki i background jobs1 min
  • 3. File storage1 min
  • 4. Środowiska i deployment2 min
  • 5. Monitoring i logging2 min
  • 6. Bezpieczeństwo3 min
  • Co dalej? — ścieżka nauki2 min
  • Źródła i dokumentacja dla całej serii1 min
  • Podsumowanie2 min
  • Pozostałe części serii1 min

Biblioteka wiedzy

Czytaj dalej

Zobacz więcej wpisów
Backend dla frontendowca: serwer, bazy danych i API

Backend dla frontendowca: serwer, bazy danych i API

Pierwsza część serii Backend dla frontendowca: architektura aplikacji, serwer, bazy danych, API, status code, paginacja, idempotency, BFF i CORS.

Maciej Sala

Maciej Sala

Founder Strivelab

28 lipca 2025
Backend dla frontendowca: auth, real-time i integracje

Backend dla frontendowca: auth, real-time i integracje

Druga część serii Backend dla frontendowca: SSE, WebSockets, polling, webhooki, sesje, JWT, cookies, CSRF, refresh token rotation i MFA.

Maciej Sala

Maciej Sala

Founder Strivelab

29 lipca 2025
REST API — zasady projektowania i dobre praktyki

REST API — zasady projektowania i dobre praktyki

Praktyczny przewodnik po projektowaniu REST API. Konwencje URL, metody HTTP, błędy, wersjonowanie, paginacja i kilka ważnych niuansów, które zwykle pomija się w prostych tutorialach.

Maciej Sala

Maciej Sala

Founder Strivelab

5 grudnia 2025