Backend Interview Questions - Easy

Easy-level backend interview questions covering HTTP, REST APIs, databases, and server fundamentals.

Q1: What is REST and what are its principles?

Answer:

REST Principles

1. Stateless: Each request contains all information needed 2. Client-Server: Separation of concerns 3. Cacheable: Responses can be cached 4. Uniform Interface: Standard HTTP methods 5. Layered System: Client doesn't know if connected to end server 6. Code on Demand (optional): Server can send executable code

HTTP Methods (CRUD)

Example REST API:

1GET    /api/users          # List all users
2GET    /api/users/123      # Get user by ID
3POST   /api/users          # Create new user
4PUT    /api/users/123      # Update user (full)
5PATCH  /api/users/123      # Update user (partial)
6DELETE /api/users/123      # Delete user

Q2: What are HTTP status codes and their meanings?

Answer:

Common Status Codes

2xx Success:

  • 200 OK: Request succeeded
  • 201 Created: Resource created
  • 204 No Content: Success, no body

3xx Redirection:

  • 301 Moved Permanently: Resource moved
  • 302 Found: Temporary redirect
  • 304 Not Modified: Use cached version

4xx Client Error:

  • 400 Bad Request: Invalid request
  • 401 Unauthorized: Authentication required
  • 403 Forbidden: No permission
  • 404 Not Found: Resource doesn't exist
  • 429 Too Many Requests: Rate limited

5xx Server Error:

  • 500 Internal Server Error: Server error
  • 502 Bad Gateway: Invalid response from upstream
  • 503 Service Unavailable: Server overloaded
  • 504 Gateway Timeout: Upstream timeout

Q3: What is the difference between SQL and NoSQL databases?

Answer:

SQL Example (PostgreSQL)

 1-- Create table
 2CREATE TABLE users (
 3    id SERIAL PRIMARY KEY,
 4    name VARCHAR(100) NOT NULL,
 5    email VARCHAR(100) UNIQUE NOT NULL,
 6    created_at TIMESTAMP DEFAULT NOW()
 7);
 8
 9-- Insert
10INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
11
12-- Query
13SELECT * FROM users WHERE email = 'alice@example.com';
14
15-- Join
16SELECT orders.*, users.name 
17FROM orders 
18JOIN users ON orders.user_id = users.id;

NoSQL Example (MongoDB)

 1// Insert document
 2db.users.insertOne({
 3  name: "Alice",
 4  email: "alice@example.com",
 5  created_at: new Date(),
 6  preferences: {
 7    theme: "dark",
 8    notifications: true
 9  },
10  tags: ["premium", "verified"]
11});
12
13// Query
14db.users.findOne({ email: "alice@example.com" });
15
16// Update
17db.users.updateOne(
18  { email: "alice@example.com" },
19  { $set: { "preferences.theme": "light" } }
20);

When to Use Each


Q4: What is database indexing and why is it important?

Answer:

Without Index

With Index

Creating Indexes

 1-- Create index
 2CREATE INDEX idx_users_email ON users(email);
 3
 4-- Composite index
 5CREATE INDEX idx_users_name_email ON users(name, email);
 6
 7-- Unique index
 8CREATE UNIQUE INDEX idx_users_email_unique ON users(email);
 9
10-- Check query plan
11EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com';

Trade-offs:

  • ✅ Faster reads
  • ❌ Slower writes (index must be updated)
  • ❌ More storage space

Q5: What is authentication vs authorization?

Answer:

Authentication Flow

Authorization Flow

Implementation Example

  1package main
  2
  3import (
  4	"context"
  5	"encoding/json"
  6	"net/http"
  7	"strings"
  8
  9	"github.com/golang-jwt/jwt/v5"
 10)
 11
 12type contextKey string
 13
 14const userContextKey contextKey = "user"
 15
 16// User represents authenticated user
 17type User struct {
 18	ID    string `json:"id"`
 19	Email string `json:"email"`
 20	Role  string `json:"role"`
 21}
 22
 23// AuthMiddleware validates JWT token
 24func AuthMiddleware(secretKey string) func(http.Handler) http.Handler {
 25	return func(next http.Handler) http.Handler {
 26		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 27			authHeader := r.Header.Get("Authorization")
 28			if authHeader == "" {
 29				http.Error(w, `{"error":"No token provided"}`, http.StatusUnauthorized)
 30				return
 31			}
 32
 33			parts := strings.Split(authHeader, " ")
 34			if len(parts) != 2 || parts[0] != "Bearer" {
 35				http.Error(w, `{"error":"Invalid authorization header"}`, http.StatusUnauthorized)
 36				return
 37			}
 38
 39			token, err := jwt.Parse(parts[1], func(token *jwt.Token) (interface{}, error) {
 40				return []byte(secretKey), nil
 41			})
 42
 43			if err != nil || !token.Valid {
 44				http.Error(w, `{"error":"Invalid token"}`, http.StatusUnauthorized)
 45				return
 46			}
 47
 48			claims, ok := token.Claims.(jwt.MapClaims)
 49			if !ok {
 50				http.Error(w, `{"error":"Invalid token claims"}`, http.StatusUnauthorized)
 51				return
 52			}
 53
 54			user := &User{
 55				ID:    claims["id"].(string),
 56				Email: claims["email"].(string),
 57				Role:  claims["role"].(string),
 58			}
 59
 60			ctx := context.WithValue(r.Context(), userContextKey, user)
 61			next.ServeHTTP(w, r.WithContext(ctx))
 62		})
 63	}
 64}
 65
 66// AuthorizeMiddleware checks user roles
 67func AuthorizeMiddleware(allowedRoles ...string) func(http.Handler) http.Handler {
 68	return func(next http.Handler) http.Handler {
 69		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 70			user, ok := r.Context().Value(userContextKey).(*User)
 71			if !ok {
 72				http.Error(w, `{"error":"Not authenticated"}`, http.StatusUnauthorized)
 73				return
 74			}
 75
 76			allowed := false
 77			for _, role := range allowedRoles {
 78				if user.Role == role {
 79					allowed = true
 80					break
 81				}
 82			}
 83
 84			if !allowed {
 85				http.Error(w, `{"error":"Insufficient permissions"}`, http.StatusForbidden)
 86				return
 87			}
 88
 89			next.ServeHTTP(w, r)
 90		})
 91	}
 92}
 93
 94// Usage
 95func main() {
 96	mux := http.NewServeMux()
 97
 98	// Protected admin endpoint
 99	adminHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
100		json.NewEncoder(w).Encode(map[string]string{"message": "Admin access granted"})
101	})
102
103	mux.Handle("/admin/users",
104		AuthMiddleware("your-secret-key")(
105			AuthorizeMiddleware("admin")(adminHandler),
106		),
107	)
108
109	http.ListenAndServe(":8080", mux)
110}

Q6: What is JWT (JSON Web Token)?

Answer:

JWT Structure

1eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2
3[Header].[Payload].[Signature]

Header:

1{
2  "alg": "HS256",
3  "typ": "JWT"
4}

Payload:

1{
2  "sub": "1234567890",
3  "name": "John Doe",
4  "iat": 1516239022,
5  "exp": 1516242622
6}

Signature:

1HMACSHA256(
2  base64UrlEncode(header) + "." + base64UrlEncode(payload),
3  secret
4)

Creating and Verifying JWT

 1package main
 2
 3import (
 4	"errors"
 5	"time"
 6
 7	"github.com/golang-jwt/jwt/v5"
 8)
 9
10type Claims struct {
11	UserID string `json:"userId"`
12	Email  string `json:"email"`
13	Role   string `json:"role"`
14	jwt.RegisteredClaims
15}
16
17// CreateToken generates a JWT token
18func CreateToken(user User, secretKey string) (string, error) {
19	claims := Claims{
20		UserID: user.ID,
21		Email:  user.Email,
22		Role:   user.Role,
23		RegisteredClaims: jwt.RegisteredClaims{
24			ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
25			IssuedAt:  jwt.NewNumericDate(time.Now()),
26		},
27	}
28
29	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
30	return token.SignedString([]byte(secretKey))
31}
32
33// VerifyToken validates and decodes a JWT token
34func VerifyToken(tokenString, secretKey string) (*Claims, error) {
35	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
36		return []byte(secretKey), nil
37	})
38
39	if err != nil {
40		return nil, err
41	}
42
43	if !token.Valid {
44		return nil, errors.New("invalid token")
45	}
46
47	claims, ok := token.Claims.(*Claims)
48	if !ok {
49		return nil, errors.New("invalid token claims")
50	}
51
52	return claims, nil
53}
54
55// Login handler example
56func LoginHandler(w http.ResponseWriter, r *http.Request) {
57	// Authenticate user (check credentials)
58	user := User{
59		ID:    "123",
60		Email: "user@example.com",
61		Role:  "admin",
62	}
63
64	token, err := CreateToken(user, "your-secret-key")
65	if err != nil {
66		http.Error(w, "Failed to create token", http.StatusInternalServerError)
67		return
68	}
69
70	json.NewEncoder(w).Encode(map[string]string{
71		"token": token,
72	})
73}

Q7: What is CORS and why is it needed?

Answer:

Same-Origin Policy

CORS Flow

Implementing CORS

 1package main
 2
 3import (
 4	"net/http"
 5	"strings"
 6)
 7
 8// CORSMiddleware handles CORS headers
 9func CORSMiddleware(allowedOrigins []string) func(http.Handler) http.Handler {
10	return func(next http.Handler) http.Handler {
11		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
12			origin := r.Header.Get("Origin")
13
14			// Check if origin is allowed
15			allowed := false
16			for _, allowedOrigin := range allowedOrigins {
17				if allowedOrigin == "*" || allowedOrigin == origin {
18					allowed = true
19					break
20				}
21			}
22
23			if allowed {
24				w.Header().Set("Access-Control-Allow-Origin", origin)
25				w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
26				w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
27				w.Header().Set("Access-Control-Allow-Credentials", "true")
28			}
29
30			// Handle preflight requests
31			if r.Method == http.MethodOptions {
32				w.WriteHeader(http.StatusOK)
33				return
34			}
35
36			next.ServeHTTP(w, r)
37		})
38	}
39}
40
41// Usage
42func main() {
43	mux := http.NewServeMux()
44
45	// Your handlers
46	mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
47		json.NewEncoder(w).Encode(map[string]string{"message": "Users endpoint"})
48	})
49
50	// Wrap with CORS middleware
51	handler := CORSMiddleware([]string{"https://example.com"})(mux)
52
53	http.ListenAndServe(":8080", handler)
54}
55
56// Simple CORS middleware (allow all)
57func SimpleCORSMiddleware(next http.Handler) http.Handler {
58	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
59		w.Header().Set("Access-Control-Allow-Origin", "*")
60		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
61		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
62
63		if r.Method == http.MethodOptions {
64			w.WriteHeader(http.StatusOK)
65			return
66		}
67
68		next.ServeHTTP(w, r)
69	})
70}

Q8: What is the difference between GET and POST?

Answer:

GET Request

1GET /api/users?page=1&limit=10 HTTP/1.1
2Host: api.example.com
3Authorization: Bearer <token>
 1package main
 2
 3import (
 4	"encoding/json"
 5	"net/http"
 6	"strconv"
 7)
 8
 9// GET handler
10func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
11	// Parse query parameters
12	pageStr := r.URL.Query().Get("page")
13	limitStr := r.URL.Query().Get("limit")
14
15	page, _ := strconv.Atoi(pageStr)
16	limit, _ := strconv.Atoi(limitStr)
17
18	if page == 0 {
19		page = 1
20	}
21	if limit == 0 {
22		limit = 10
23	}
24
25	// Fetch users from database
26	users := fetchUsers(page, limit)
27
28	w.Header().Set("Content-Type", "application/json")
29	json.NewEncoder(w).Encode(map[string]interface{}{
30		"users": users,
31		"page":  page,
32		"limit": limit,
33	})
34}

POST Request

1POST /api/users HTTP/1.1
2Host: api.example.com
3Content-Type: application/json
4Authorization: Bearer <token>
5
6{
7  "name": "Alice",
8  "email": "alice@example.com"
9}
 1package main
 2
 3import (
 4	"encoding/json"
 5	"net/http"
 6)
 7
 8type CreateUserRequest struct {
 9	Name  string `json:"name"`
10	Email string `json:"email"`
11}
12
13type UserResponse struct {
14	ID    string `json:"id"`
15	Name  string `json:"name"`
16	Email string `json:"email"`
17}
18
19// POST handler
20func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
21	var req CreateUserRequest
22
23	// Decode JSON body
24	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
25		http.Error(w, "Invalid request body", http.StatusBadRequest)
26		return
27	}
28
29	// Validate
30	if req.Name == "" || req.Email == "" {
31		http.Error(w, "Name and email are required", http.StatusBadRequest)
32		return
33	}
34
35	// Create user in database
36	user := createUser(req.Name, req.Email)
37
38	w.Header().Set("Content-Type", "application/json")
39	w.WriteStatus(http.StatusCreated)
40	json.NewEncoder(w).Encode(UserResponse{
41		ID:    user.ID,
42		Name:  user.Name,
43		Email: user.Email,
44	})
45}

Comparison

FeatureGETPOST
PurposeRetrieveSubmit
Data locationURLBody
CacheableYesNo
IdempotentYesNo
Size limitYes (~2KB)No
Visible in URLYesNo
BookmarkableYesNo

Q9: What is database normalization?

Answer:

Unnormalized Data

 1-- ❌ Redundant data
 2CREATE TABLE orders (
 3    order_id INT,
 4    customer_name VARCHAR(100),
 5    customer_email VARCHAR(100),
 6    customer_address TEXT,
 7    product_name VARCHAR(100),
 8    product_price DECIMAL,
 9    quantity INT
10);
11
12-- Problems:
13-- 1. Customer data repeated for each order
14-- 2. Product data repeated for each order
15-- 3. Update anomalies (change email in multiple places)

Normalized Data (3NF)

 1-- ✅ Separate tables, no redundancy
 2
 3CREATE TABLE customers (
 4    customer_id SERIAL PRIMARY KEY,
 5    name VARCHAR(100),
 6    email VARCHAR(100) UNIQUE,
 7    address TEXT
 8);
 9
10CREATE TABLE products (
11    product_id SERIAL PRIMARY KEY,
12    name VARCHAR(100),
13    price DECIMAL
14);
15
16CREATE TABLE orders (
17    order_id SERIAL PRIMARY KEY,
18    customer_id INT REFERENCES customers(customer_id),
19    order_date TIMESTAMP DEFAULT NOW()
20);
21
22CREATE TABLE order_items (
23    order_item_id SERIAL PRIMARY KEY,
24    order_id INT REFERENCES orders(order_id),
25    product_id INT REFERENCES products(product_id),
26    quantity INT,
27    price_at_purchase DECIMAL
28);

Normal Forms

1NF (First Normal Form):

  • Atomic values (no lists in cells)
  • No repeating groups

2NF (Second Normal Form):

  • 1NF + No partial dependencies
  • Non-key attributes depend on entire primary key

3NF (Third Normal Form):

  • 2NF + No transitive dependencies
  • Non-key attributes depend only on primary key

Q10: What is caching and common caching strategies?

Answer:

Cache Layers

Caching Strategies

1. Cache-Aside (Lazy Loading):

 1package main
 2
 3import (
 4	"context"
 5	"encoding/json"
 6	"fmt"
 7	"time"
 8
 9	"github.com/redis/go-redis/v9"
10)
11
12type User struct {
13	ID    string `json:"id"`
14	Name  string `json:"name"`
15	Email string `json:"email"`
16}
17
18func GetUser(ctx context.Context, rdb *redis.Client, userID string) (*User, error) {
19	// Check cache first
20	cacheKey := fmt.Sprintf("user:%s", userID)
21	cached, err := rdb.Get(ctx, cacheKey).Result()
22
23	if err == nil {
24		// Cache hit
25		var user User
26		if err := json.Unmarshal([]byte(cached), &user); err == nil {
27			return &user, nil
28		}
29	}
30
31	// Cache miss: fetch from database
32	user, err := fetchUserFromDB(userID)
33	if err != nil {
34		return nil, err
35	}
36
37	// Store in cache
38	userData, _ := json.Marshal(user)
39	rdb.Set(ctx, cacheKey, userData, time.Hour)
40
41	return user, nil
42}

2. Write-Through:

 1func UpdateUser(ctx context.Context, rdb *redis.Client, userID string, data User) (*User, error) {
 2	// Update database
 3	user, err := updateUserInDB(userID, data)
 4	if err != nil {
 5		return nil, err
 6	}
 7
 8	// Update cache
 9	cacheKey := fmt.Sprintf("user:%s", userID)
10	userData, _ := json.Marshal(user)
11	rdb.Set(ctx, cacheKey, userData, time.Hour)
12
13	return user, nil
14}

3. Write-Behind (Write-Back):

 1func UpdateUserAsync(ctx context.Context, rdb *redis.Client, queue Queue, userID string, data User) error {
 2	// Update cache immediately
 3	cacheKey := fmt.Sprintf("user:%s", userID)
 4	userData, _ := json.Marshal(data)
 5	if err := rdb.Set(ctx, cacheKey, userData, time.Hour).Err(); err != nil {
 6		return err
 7	}
 8
 9	// Queue database update (async)
10	return queue.Enqueue("update-user", map[string]interface{}{
11		"userId": userID,
12		"data":   data,
13	})
14}

Cache Invalidation

 1package main
 2
 3import (
 4	"context"
 5	"time"
 6
 7	"github.com/redis/go-redis/v9"
 8)
 9
10func CacheInvalidationExamples(ctx context.Context, rdb *redis.Client) {
11	// TTL (Time To Live)
12	rdb.Set(ctx, "key", "value", time.Hour) // Expires in 1 hour
13
14	// Manual invalidation
15	rdb.Del(ctx, "user:123")
16
17	// Pattern-based invalidation
18	keys, _ := rdb.Keys(ctx, "user:*").Result()
19	if len(keys) > 0 {
20		rdb.Del(ctx, keys...)
21	}
22
23	// Or use SCAN for large datasets (more efficient)
24	iter := rdb.Scan(ctx, 0, "user:*", 0).Iterator()
25	for iter.Next(ctx) {
26		rdb.Del(ctx, iter.Val())
27	}
28}

Summary

Key backend concepts:

  • REST: Stateless, uniform interface, HTTP methods
  • HTTP Status Codes: 2xx success, 4xx client error, 5xx server error
  • SQL vs NoSQL: Relational vs document, ACID vs BASE
  • Indexing: B-tree, faster queries, trade-offs
  • Authentication vs Authorization: Identity vs permissions
  • JWT: Token-based auth, stateless
  • CORS: Cross-origin security, browser protection
  • GET vs POST: Retrieve vs submit, idempotent vs not
  • Normalization: Reduce redundancy, normal forms
  • Caching: Strategies, invalidation, layers

These fundamentals are essential for backend development.

Related Snippets