REST API - Pytania Rekrutacyjne dla Backend Developer [2026]
REST API to standard komunikacji w nowoczesnych aplikacjach webowych i mobilnych. Na rozmowie rekrutacyjnej backend developer musi wykazać się znajomością nie tylko implementacji, ale też zasad projektowania dobrych API. Ten przewodnik zawiera 50+ pytań rekrutacyjnych z odpowiedziami - od podstaw HTTP po zaawansowane tematy jak wersjonowanie, bezpieczeństwo i optymalizacja.
Podstawy REST
Co to jest REST i jakie są jego główne zasady?
Odpowiedź w 30 sekund:
REST (Representational State Transfer) to styl architektoniczny oparty na 6 zasadach:
- Client-Server - separacja klienta od serwera
- Stateless - brak stanu sesji na serwerze
- Cacheable - odpowiedzi mogą być cache'owane
- Uniform Interface - standardowy interfejs
- Layered System - warstwy pośrednie
- Code on Demand - opcjonalny kod wykonywalny
Odpowiedź w 2 minuty:
REST opiera się na komunikacji klient-serwer za pomocą standardowych metod HTTP i zwracaniu danych w uniwersalnym formacie.
Client → Request (GET /users/1) → Server
← Response (JSON) + Status Code ←
Stateless - najważniejsza zasada:
// ❌ Stateful - serwer pamięta stan
session.setAttribute("user", user);
// ✅ Stateless - każdy request zawiera pełny kontekst
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Uniform Interface - 4 ograniczenia:
- Identyfikacja zasobów (URI)
- Manipulacja przez reprezentacje (JSON/XML)
- Self-descriptive messages (Content-Type, Accept)
- HATEOAS (hypermedia links)
Korzyści REST:
- Skalowalność (stateless = łatwy load balancing)
- Prostota (HTTP jest znany)
- Niezależność (klient i serwer mogą ewoluować osobno)
Czym różni się REST od RESTful?
Odpowiedź w 30 sekund:
- REST - styl architektoniczny, zbiór zasad
- RESTful - API które implementuje te zasady
W praktyce większość API to "REST-ish" - implementuje część zasad (HTTP methods, JSON) ale nie wszystkie (np. pomija HATEOAS).
Odpowiedź w 2 minuty:
Stopień "restfulness" API można określić za pomocą modelu dojrzałości Richardsona, który klasyfikuje implementacje na cztery poziomy.
Poziomy dojrzałości REST (Richardson Maturity Model):
| Level | Opis | Przykład |
|---|---|---|
| 0 | HTTP jako transport | POST /api z XML |
| 1 | Zasoby | GET /users, GET /orders |
| 2 | HTTP verbs | GET, POST, PUT, DELETE |
| 3 | HATEOAS | Linki w odpowiedziach |
// Level 2 (typowe "REST" API)
GET /users/1
{
"id": 1,
"name": "John"
}
// Level 3 (RESTful z HATEOAS)
GET /users/1
{
"id": 1,
"name": "John",
"_links": {
"self": { "href": "/users/1" },
"orders": { "href": "/users/1/orders" },
"update": { "href": "/users/1", "method": "PUT" }
}
}
Większość produkcyjnych API to Level 2. Level 3 (HATEOAS) jest rzadko w pełni implementowany.
HTTP Methods
Jakie metody HTTP używamy w REST i do czego?
Odpowiedź w 30 sekund:
| Method | Operacja | Idempotentne | Safe |
|---|---|---|---|
| GET | Read | Tak | Tak |
| POST | Create | Nie | Nie |
| PUT | Replace | Tak | Nie |
| PATCH | Update | Nie* | Nie |
| DELETE | Delete | Tak | Nie |
Odpowiedź w 2 minuty:
Każda metoda HTTP ma określony cel i właściwości, które determinują jej zachowanie w kontekście idempotentności i bezpieczeństwa.
# GET - pobierz zasób
GET /users/1 HTTP/1.1
# POST - utwórz nowy zasób
POST /users HTTP/1.1
Content-Type: application/json
{"name": "John", "email": "john@example.com"}
# PUT - zastąp cały zasób
PUT /users/1 HTTP/1.1
Content-Type: application/json
{"name": "John Updated", "email": "john@example.com", "age": 30}
# PATCH - częściowa aktualizacja
PATCH /users/1 HTTP/1.1
Content-Type: application/json
{"email": "newemail@example.com"}
# DELETE - usuń zasób
DELETE /users/1 HTTP/1.1
Idempotentność:
GET /users/1 → wynik zawsze ten sam
PUT /users/1 → wielokrotne wywołanie = ten sam efekt
DELETE /users/1 → 1x delete = usunięte, 2x delete = nadal usunięte
POST /users → każde wywołanie tworzy nowy zasób!
Safe methods (GET, HEAD, OPTIONS) nie modyfikują stanu - mogą być cache'owane, prefetchowane.
Czym różni się PUT od PATCH?
Odpowiedź w 30 sekund:
- PUT - zastępuje cały zasób (wszystkie pola wymagane)
- PATCH - aktualizuje tylko podane pola
PUT jest idempotentne, PATCH może nie być (zależy od implementacji).
Odpowiedź w 2 minuty:
Różnica między PUT a PATCH jest fundamentalna - PUT wymaga przesłania pełnej reprezentacji zasobu, podczas gdy PATCH aktualizuje tylko wybrane pola.
// Obecny zasób
{
"id": 1,
"name": "John",
"email": "john@example.com",
"age": 25
}
// PUT /users/1 - ZASTĘPUJE cały zasób
{
"name": "John Updated"
}
// Wynik: {id: 1, name: "John Updated", email: null, age: null}
// Brakujące pola → null/default!
// PATCH /users/1 - aktualizuje TYLKO podane pola
{
"name": "John Updated"
}
// Wynik: {id: 1, name: "John Updated", email: "john@example.com", age: 25}
Spring implementation:
// PUT - wymaga pełnego obiektu
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
return userRepository.save(user); // nadpisuje wszystko
}
// PATCH - częściowa aktualizacja
@PatchMapping("/{id}")
public User patchUser(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
User user = userRepository.findById(id).orElseThrow();
updates.forEach((key, value) -> {
Field field = ReflectionUtils.findField(User.class, key);
field.setAccessible(true);
ReflectionUtils.setField(field, user, value);
});
return userRepository.save(user);
}
Kiedy używać POST a kiedy PUT?
Odpowiedź w 30 sekund:
- POST - tworzenie zasobu gdy serwer generuje ID
- PUT - tworzenie/update gdy klient zna ID
POST tworzy pod różnymi URI, PUT zawsze pod tym samym.
Odpowiedź w 2 minuty:
Wybór między POST a PUT zależy od tego, kto odpowiada za identyfikator zasobu - klient czy serwer.
# POST - serwer decyduje o ID
POST /users HTTP/1.1
{"name": "John"}
201 Created
Location: /users/42
{"id": 42, "name": "John"}
# PUT - klient podaje ID (upsert)
PUT /users/42 HTTP/1.1
{"id": 42, "name": "John"}
201 Created # jeśli nie istniał
# lub
200 OK # jeśli istniał i został zaktualizowany
Use cases:
| Scenariusz | Method |
|---|---|
| Nowy user, ID generowane | POST /users |
| User z określonym ID | PUT /users/{id} |
| Upload file z nazwą | PUT /files/report.pdf |
| Action na zasobie | POST /orders/1/cancel |
| Partial update | PATCH /users/1 |
// Spring - POST zwraca 201 + Location header
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User saved = userRepository.save(user);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(saved.getId())
.toUri();
return ResponseEntity.created(location).body(saved);
}
HTTP Status Codes
Jakie kody HTTP status powinno zwracać REST API?
Odpowiedź w 30 sekund:
2xx - sukces:
- 200 OK - ogólny sukces
- 201 Created - zasób utworzony
- 204 No Content - sukces bez body
4xx - błąd klienta:
- 400 Bad Request - nieprawidłowe dane
- 401 Unauthorized - brak autoryzacji
- 403 Forbidden - brak uprawnień
- 404 Not Found - zasób nie istnieje
5xx - błąd serwera:
- 500 Internal Server Error
Odpowiedź w 2 minuty:
Poprawne używanie kodów HTTP status pomaga klientom API zrozumieć wynik operacji i odpowiednio zareagować na błędy.
# 200 OK - GET, PUT, PATCH sukces
GET /users/1 → 200 + body
# 201 Created - POST sukces
POST /users → 201 + Location header + body
# 204 No Content - DELETE sukces (lub PUT bez zwracania body)
DELETE /users/1 → 204
# 400 Bad Request - złe dane wejściowe
POST /users {"email": "invalid"} → 400
# 401 Unauthorized - brak lub nieprawidłowy token
GET /users (no token) → 401
# 403 Forbidden - token OK, ale brak uprawnień
DELETE /admin/users/1 (as regular user) → 403
# 404 Not Found - zasób nie istnieje
GET /users/9999 → 404
# 409 Conflict - konflikt (np. duplicate email)
POST /users {"email": "existing@email.com"} → 409
# 422 Unprocessable Entity - walidacja biznesowa
POST /orders {"quantity": -5} → 422
# 429 Too Many Requests - rate limiting
GET /api/... → 429 + Retry-After header
Spring - globalna obsługa błędów:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(EntityNotFoundException e) {
return ResponseEntity.status(404)
.body(new ErrorResponse("NOT_FOUND", e.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining(", "));
return ResponseEntity.badRequest()
.body(new ErrorResponse("VALIDATION_ERROR", message));
}
}
Czym różni się 401 od 403?
Odpowiedź w 30 sekund:
- 401 Unauthorized - nie wiem kim jesteś (brak/nieprawidłowy token)
- 403 Forbidden - wiem kim jesteś, ale nie masz uprawnień
401 = "Zaloguj się", 403 = "Nie masz dostępu"
Odpowiedź w 2 minuty:
Te dwa kody błędów są często mylone, ale mają zupełnie różne znaczenie w kontekście bezpieczeństwa API.
# 401 Unauthorized - brak tokenu
GET /users
Authorization: (brak)
→ 401 Unauthorized
WWW-Authenticate: Bearer
# 401 Unauthorized - nieprawidłowy/wygasły token
GET /users
Authorization: Bearer expired-token
→ 401 Unauthorized
# 403 Forbidden - token OK, user nie jest adminem
DELETE /admin/users/1
Authorization: Bearer valid-user-token
→ 403 Forbidden
Spring Security:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN") // 403 jeśli nie admin
.anyRequest().authenticated() // 401 jeśli niezalogowany
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt())
.build();
}
}
API Design
Jak projektować URL-e w REST API?
Odpowiedź w 30 sekund:
- Rzeczowniki (nie czasowniki) -
/usersnie/getUsers - Liczba mnoga -
/usersnie/user - Hierarchia zasobów -
/users/1/orders - Kebab-case -
/user-profilesnie/userProfiles - Filtry w query params -
/users?status=active
Odpowiedź w 2 minuty:
Dobrze zaprojektowane URL-e są intuicyjne, spójne i odzwierciedlają hierarchię zasobów w systemie.
# ✅ DOBRZE
GET /users # lista użytkowników
GET /users/1 # konkretny użytkownik
POST /users # utwórz użytkownika
PUT /users/1 # aktualizuj użytkownika
DELETE /users/1 # usuń użytkownika
GET /users/1/orders # zamówienia użytkownika
GET /users/1/orders/5 # konkretne zamówienie użytkownika
# ❌ ŹLE
GET /getUsers
GET /user/1
POST /createUser
GET /getUserOrders/1
DELETE /deleteUser/1
# Akcje jako sub-zasoby lub POST
POST /orders/1/cancel # akcja na zasobie
POST /users/1/activate # akcja na zasobie
# Query params dla filtrowania, sortowania, paginacji
GET /users?status=active&sort=name&page=1&size=20
GET /products?category=electronics&minPrice=100&maxPrice=500
Zagnieżdżone zasoby:
# Płytkie zagnieżdżenie (zalecane)
GET /users/1/orders
# Głębokie zagnieżdżenie (unikaj)
GET /users/1/orders/5/items/3/reviews # za głęboko!
# Lepiej - bezpośredni dostęp gdy zasób ma globalne ID
GET /orders/5
GET /order-items/3
Jak obsługiwać paginację w REST API?
Odpowiedź w 30 sekund:
Query params: ?page=1&size=20 lub ?offset=0&limit=20
Odpowiedź zawiera metadata: total, hasNext, links.
Odpowiedź w 2 minuty:
Efektywna paginacja wymaga nie tylko podziału danych na strony, ale także dostarczenia metadanych ułatwiających nawigację.
GET /users?page=0&size=20&sort=name,asc
HTTP/1.1 200 OK
{
"content": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
],
"page": {
"number": 0,
"size": 20,
"totalElements": 150,
"totalPages": 8
},
"_links": {
"self": "/users?page=0&size=20",
"next": "/users?page=1&size=20",
"last": "/users?page=7&size=20"
}
}
Spring Data:
@GetMapping
public Page<User> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(defaultValue = "id") String sortBy
) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
return userRepository.findAll(pageable);
}
// Spring HATEOAS
@GetMapping
public PagedModel<EntityModel<User>> getUsers(Pageable pageable) {
Page<User> users = userRepository.findAll(pageable);
return pagedResourcesAssembler.toModel(users);
}
Cursor-based pagination (dla dużych zbiorów):
GET /events?after=cursor123&limit=20
{
"data": [...],
"cursors": {
"before": "cursor122",
"after": "cursor143"
},
"hasMore": true
}
Cursor jest lepszy gdy dane się zmieniają (unikasz duplicate/skip).
Jak wersjonować REST API?
Odpowiedź w 30 sekund:
3 podejścia:
-
URL path -
/api/v1/users(najpopularniejsze) -
Query param -
/api/users?version=1 -
Header -
Accept: application/vnd.api.v1+json
Odpowiedź w 2 minuty:
1. URL versioning (zalecane):
GET /api/v1/users
GET /api/v2/users
@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 { }
@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 { }
Zalety: jasne, łatwe w dokumentacji, testowaniu Wady: zmienia URI zasobu
2. Header versioning:
GET /api/users
Accept: application/vnd.myapi.v1+json
@GetMapping(produces = "application/vnd.myapi.v1+json")
public UserV1 getUserV1() { }
@GetMapping(produces = "application/vnd.myapi.v2+json")
public UserV2 getUserV2() { }
Zalety: URI stabilne Wady: trudniejsze testowanie (curl, browser)
Strategia wersjonowania:
- Nowa wersja gdy breaking change (zmiana struktury, usunięcie pola)
- Addytywne zmiany (nowe pola) - bez nowej wersji
- Deprecate starą wersję z notice period
- Sunset header:
Sunset: Sat, 1 Jan 2025 00:00:00 GMT
Bezpieczeństwo API
Jak zabezpieczyć REST API?
Odpowiedź w 30 sekund:
- HTTPS - zawsze, bez wyjątków
- Authentication - JWT, OAuth2
- Authorization - sprawdzaj uprawnienia
- Input validation - waliduj wszystko
- Rate limiting - ochrona przed DDoS
Odpowiedź w 2 minuty:
Bezpieczeństwo REST API wymaga warstwowego podejścia - od zabezpieczenia transportu przez autentykację, po walidację danych wejściowych.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
// HTTPS tylko
.requiresChannel(channel -> channel.anyRequest().requiresSecure())
// CSRF dla stateless API
.csrf(csrf -> csrf.disable())
// JWT authentication
.oauth2ResourceServer(oauth2 -> oauth2.jwt())
// Authorization rules
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/users/**").hasRole("USER")
.requestMatchers(HttpMethod.DELETE, "/api/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
// No session
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}
}
Walidacja input:
@PostMapping
public User createUser(@Valid @RequestBody CreateUserRequest request) {
// @Valid automatycznie waliduje
}
public record CreateUserRequest(
@NotBlank @Size(max = 100) String name,
@Email String email,
@Min(18) @Max(120) Integer age
) {}
Rate limiting (Bucket4j):
@GetMapping
@RateLimiter(name = "api")
public List<User> getUsers() { }
Czym różni się autentykacja od autoryzacji?
Odpowiedź w 30 sekund:
- Autentykacja - "Kim jesteś?" (weryfikacja tożsamości)
- Autoryzacja - "Co możesz zrobić?" (weryfikacja uprawnień)
Autentykacja → Autoryzacja (w tej kolejności)
Odpowiedź w 2 minuty:
Proces zabezpieczania żądania przebiega w dwóch krokach - najpierw weryfikujemy tożsamość, potem sprawdzamy uprawnienia.
Request → Autentykacja → Autoryzacja → Controller
↓ ↓
"To jest John" "John może czytać users"
Autentykacja (Authentication):
# Basic Auth (nie zalecane)
Authorization: Basic am9objpwYXNzd29yZA==
# Bearer Token (JWT)
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
# API Key (dla integracji)
X-API-Key: sk_live_abc123
Autoryzacja (Authorization):
// Role-based
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) { }
// Permission-based
@PreAuthorize("hasAuthority('users:delete')")
public void deleteUser(Long id) { }
// Resource-based (sprawdź czy user jest właścicielem)
@PreAuthorize("@userService.isOwner(#id, authentication)")
public void updateUser(Long id, User user) { }
JWT claims dla autoryzacji:
{
"sub": "user123",
"roles": ["USER", "EDITOR"],
"permissions": ["users:read", "posts:write"],
"exp": 1704067200
}
Jak działa JWT i jakie ma wady?
Odpowiedź w 30 sekund:
JWT (JSON Web Token) = Header.Payload.Signature
- Self-contained (zawiera wszystkie dane)
- Stateless (serwer nie przechowuje sesji)
- Nie można unieważnić przed expiration (główna wada)
Odpowiedź w 2 minuty:
JWT składa się z trzech części oddzielonych kropkami - każda zakodowana w Base64URL. Header określa algorytm, Payload zawiera claims (dane), a Signature potwierdza integralność.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIiwiZXhwIjoxNzA0MDY3MjAwfQ.signature
HEADER . PAYLOAD . SIGNATURE
// Header
{"alg": "HS256", "typ": "JWT"}
// Payload
{
"sub": "user123",
"name": "John Doe",
"roles": ["USER"],
"iat": 1704063600,
"exp": 1704067200
}
// Signature
HMACSHA256(base64(header) + "." + base64(payload), secret)
Zalety:
- Stateless - łatwe skalowanie
- Self-contained - brak database lookup
- Cross-domain / microservices
Wady:
- Nie można unieważnić (tylko czekać na exp)
- Payload czytelny (base64, nie szyfrowany)
- Rozmiar tokenu większy niż session ID
Rozwiązania wad:
// Short-lived access token + refresh token
Access Token: 15 min expiration
Refresh Token: 7 days, stored in DB (można unieważnić)
// Blacklist (dla logout)
Set<String> blacklistedTokens = new ConcurrentHashSet<>();
public boolean isValid(String token) {
return !blacklistedTokens.contains(token) && !isExpired(token);
}
Obsługa błędów
Jak projektować odpowiedzi błędów w REST API?
Odpowiedź w 30 sekund:
Standardowa struktura:
{
"error": "VALIDATION_ERROR",
"message": "Invalid input",
"details": [...],
"timestamp": "2024-01-15T10:30:00Z",
"path": "/api/users"
}
Odpowiedź w 2 minuty:
RFC 7807 Problem Details:
{
"type": "https://api.example.com/errors/validation",
"title": "Validation Error",
"status": 400,
"detail": "One or more fields failed validation",
"instance": "/api/users",
"errors": [
{
"field": "email",
"message": "must be a valid email address",
"rejectedValue": "invalid-email"
},
{
"field": "age",
"message": "must be at least 18",
"rejectedValue": 15
}
]
}
Spring implementation:
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ProblemDetail handleNotFound(EntityNotFoundException e, WebRequest request) {
ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
problem.setTitle("Resource Not Found");
problem.setDetail(e.getMessage());
problem.setProperty("timestamp", Instant.now());
return problem;
}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatusCode status,
WebRequest request) {
ProblemDetail problem = ProblemDetail.forStatus(status);
problem.setTitle("Validation Error");
List<Map<String, String>> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> Map.of(
"field", error.getField(),
"message", error.getDefaultMessage()
))
.toList();
problem.setProperty("errors", errors);
return ResponseEntity.status(status).body(problem);
}
}
Optymalizacja
Jak zoptymalizować wydajność REST API?
Odpowiedź w 30 sekund:
- Caching - ETag, Cache-Control headers
- Compression - gzip responses
- Paginacja - nie zwracaj wszystkiego
- Projection - zwracaj tylko potrzebne pola
- Connection pooling - HTTP/2, keep-alive
Odpowiedź w 2 minuty:
HTTP Caching:
# Response
HTTP/1.1 200 OK
Cache-Control: max-age=3600
ETag: "abc123"
# Conditional request
GET /users/1
If-None-Match: "abc123"
# If not modified
HTTP/1.1 304 Not Modified
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
String etag = Integer.toHexString(user.hashCode());
return ResponseEntity.ok()
.eTag(etag)
.cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS))
.body(user);
}
Compression:
# application.yml
server:
compression:
enabled: true
mime-types: application/json,application/xml,text/html
min-response-size: 1024
Sparse fieldsets:
GET /users/1?fields=id,name,email
# Response (tylko wybrane pola)
{
"id": 1,
"name": "John",
"email": "john@example.com"
}
Batch requests:
POST /batch
{
"requests": [
{"method": "GET", "path": "/users/1"},
{"method": "GET", "path": "/users/2"},
{"method": "GET", "path": "/users/3"}
]
}
Zobacz też
- Kompletny Przewodnik - Rozmowa Java Backend Developer - pełny przewodnik przygotowania
- Spring Boot - Pytania Rekrutacyjne - implementacja REST w Spring
- Wzorce i Architektura Backend - architektura API
Ten artykuł jest częścią serii przygotowującej do rozmów rekrutacyjnych na stanowisko Backend Developer. Sprawdź nasze fiszki z pytaniami rekrutacyjnymi.
Chcesz więcej pytań rekrutacyjnych?
To tylko jeden temat z naszego kompletnego przewodnika po rozmowach rekrutacyjnych. Uzyskaj dostęp do 800+ pytań z 13 technologii.
