GO 51 - 60

Chapter 51 — SQL Transactions & Connection Management

Question

Given below is a code snippet that:

  • Demonstrates the basic structure of a database transaction.

  • Demonstrates Begin, Commit, and Rollback.

  • Demonstrates why an error should cause a transaction to be rolled back.

  • Demonstrates the intended all-or-nothing nature of a transaction.


What should be the output of the following code?

package main

import (
	"database/sql"
	"fmt"
)

func transfer(tx *sql.Tx) error {
	fmt.Println("debit account")
	fmt.Println("credit account")

	err := fmt.Errorf("credit failed")

	if err != nil {
		fmt.Println("rollback")
		return err
	}

	return tx.Commit()
}

func main() {
	db := (*sql.DB)(nil)

	_ = db

	tx := (*sql.Tx)(nil)

	err := transfer(tx)

	if err != nil {
		fmt.Println("transaction failed")
		return
	}

	fmt.Println("transaction committed")
}

Answer

debit account
credit account
rollback
transaction failed

Step-by-step explanation

  1. tx represents a database transaction.

  2. transfer begins by performing the conceptual debit operation.

  3. It then performs the conceptual credit operation.

  4. An error is deliberately created with fmt.Errorf.

  5. Because err != nil, the function reports "rollback" and returns the error.

  6. main receives the error and prints "transaction failed".

  7. "transaction committed" is never reached.

How to read the important code

if err != nil {

"if err is not equal to nil, open curly brace."

A transaction is a group of database operations treated as one unit.

The usual pattern is:

Begin → operations → Commit
              ↓
           error
              ↓
          Rollback

In real Go code, you normally use db.Begin() to start a transaction and tx.Commit() or tx.Rollback() to finish it.

Key takeaway

A transaction should leave the database in a consistent state: either the intended group of changes succeeds, or the changes are rolled back.


Chapter 52 — Authentication & Authorization

Question

Given below is a code snippet that:

  • Demonstrates the difference between authentication and authorization.

  • Checks whether a user is logged in.

  • Checks whether the authenticated user has permission to perform an action.

  • Demonstrates a simple role-based access check.

What should be the output of the following code?

package main

import "fmt"

type User struct {
	Name string
	Role string
}

func canDelete(user *User) bool {
	if user == nil {
		return false
	}

	return user.Role == "admin"
}

func main() {
	user := &User{
		Name: "Ravi",
		Role: "editor",
	}

	fmt.Println("authenticated:", user != nil)
	fmt.Println("can delete:", canDelete(user))

	user.Role = "admin"

	fmt.Println("can delete:", canDelete(user))
}

Answer

authenticated: true
can delete: false
can delete: true

Step-by-step explanation

  1. user points to a User containing Ravi's information.

  2. user != nil confirms that a user object exists.

  3. Therefore, the first authentication check prints true.

  4. Ravi's initial role is "editor".

  5. canDelete returns false because only "admin" is allowed to delete.

  6. The role is changed to "admin".

  7. The second authorization check therefore returns true.

How to read the important code

return user.Role == "admin"

"return user dot Role is equal to double quote admin double quote."

Authentication asks:

"Who are you?"

Authorization asks:

"Are you allowed to do this?"

For example, logging in successfully is authentication; being allowed to delete another user's account is authorization.

Key takeaway

Authentication identifies the user; authorization decides what that user is allowed to do.


Chapter 53 — Secure Go Programming

Question

Given below is a code snippet that:

  • Demonstrates parameterized SQL.

  • Demonstrates why user input should not be directly concatenated into SQL.

  • Keeps user-controlled data separate from SQL syntax.

  • Demonstrates a safer database-query pattern.

What should be the output of the following code?

package main

import "fmt"

func buildQuery(username string) (string, []any) {
	query := "SELECT id, name FROM users WHERE username = ?"
	args := []any{username}

	return query, args
}

func main() {
	query, args := buildQuery("admin' OR '1'='1")

	fmt.Println(query)
	fmt.Println(args)
}

Answer

SELECT id, name FROM users WHERE username = ?
[admin' OR '1'='1]

Step-by-step explanation

  1. username contains input supplied by a user.

  2. The SQL query contains ? as a placeholder.

  3. The username is placed separately inside args.

  4. Therefore, the username is treated as data rather than being inserted into the SQL command itself.

  5. Even though the input contains SQL-looking characters, it remains one argument.

  6. In actual database code, the database driver binds the argument to the placeholder.

How to read the important code

query := "SELECT id, name FROM users WHERE username = ?"

"query colon equals double quote SELECT id comma name FROM users WHERE username equals question mark double quote."

A parameterized query separates:

SQL command
+
user-provided data

Instead of constructing SQL like:

"... WHERE username = '" + username + "'"

you use placeholders and arguments.

Key takeaway

Never blindly concatenate untrusted user input into SQL; use parameterized queries.


Chapter 54 — Configuration & Environment Variables

Question

Given below is a code snippet that:

  • Demonstrates reading an environment variable.

  • Demonstrates providing a default value.

  • Demonstrates how configuration can change without changing source code.

  • Uses a realistic application configuration pattern.

What should be the output of the following code?

package main

import (
	"fmt"
	"os"
)

func getPort() string {
	port := os.Getenv("PORT")

	if port == "" {
		return "8080"
	}

	return port
}

func main() {
	os.Setenv("PORT", "9000")

	fmt.Println("server port:", getPort())
}

Answer

server port: 9000

Step-by-step explanation

  1. os.Setenv("PORT", "9000") creates or changes the PORT environment variable for this process.

  2. getPort() calls os.Getenv("PORT").

  3. It receives "9000".

  4. Because the value isn't empty, the function returns "9000".

  5. main() prints the configured port.

How to read the important code

port := os.Getenv("PORT")

"port colon equals os dot Getenv, open parenthesis double quote PORT double quote, close parenthesis."

An environment variable is a value supplied by the operating environment rather than hard-coded into the program.

Common production configuration includes:

PORT
DATABASE_URL
API_KEY
LOG_LEVEL

Sensitive values such as passwords and API keys should generally not be hard-coded into source code.

Key takeaway

Configuration should normally be external to your application code so the same program can run in different environments.


Chapter 55 — Logging & Observability

Question

Given below is a code snippet that:

  • Demonstrates structured logging with log/slog.

  • Adds useful context to a log message.

  • Demonstrates different log levels.

  • Shows how logs can help understand what a backend service is doing.

What should be the output of the following code?

package main

import (
	"log/slog"
	"os"
)

func main() {
	logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

	logger.Info("request completed",
		"method", "GET",
		"path", "/users",
		"status", 200,
	)

	logger.Error("database unavailable",
		"retry", 3,
	)
}

Answer

The exact formatting can vary slightly by Go version and handler configuration, but the important output is:

level=INFO msg="request completed" method=GET path=/users status=200
level=ERROR msg="database unavailable" retry=3

Step-by-step explanation

  1. slog.NewTextHandler creates a text-format log handler.

  2. slog.New creates a logger using that handler.

  3. logger.Info records an informational event.

  4. "method", "path", and "status" provide structured context.

  5. logger.Error records an error event.

  6. "retry", 3 tells us that three retries are associated with the error.

  7. In a production service, these contextual fields make logs much easier to search and analyze.

How to read the important code

logger.Info("request completed", "method", "GET")

"logger dot Info, open parenthesis double quote request completed double quote, comma double quote method double quote, comma double quote GET double quote, close parenthesis."

Observability means making a running system understandable from its external behavior, commonly through:

  • logs

  • metrics

  • traces

Key takeaway

Good logs don't merely say that something failed—they provide enough context to understand what happened.


Chapter 56 — Graceful Shutdown

Question

Given below is a code snippet that:

  • Demonstrates the basic shutdown sequence of a server.

  • Uses context to signal cancellation.

  • Demonstrates cleanup before the program exits.

  • Shows the order in which shutdown operations occur.

What should be the output of the following code?

package main

import (
	"context"
	"fmt"
)

func server(ctx context.Context) {
	fmt.Println("server started")

	<-ctx.Done()

	fmt.Println("shutdown signal received")
	fmt.Println("server stopped")
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())

	go server(ctx)

	cancel()

	fmt.Println("application exiting")
}

Answer

application exiting

Step-by-step explanation

  1. context.WithCancel creates a context and a cancel function.

  2. go server(ctx) starts server in a goroutine.

  3. cancel() signals cancellation.

  4. However, the main goroutine does not wait for server to finish.

  5. main immediately prints "application exiting".

  6. The program can terminate before the server goroutine gets a chance to print its messages.

  7. Therefore, you cannot safely depend on the server's output appearing here.

How to read the important code

ctx, cancel := context.WithCancel(context.Background())

"ctx comma cancel colon equals context dot WithCancel, open parenthesis context dot Background, open parenthesis close parenthesis, close parenthesis."

A context carries cancellation and request-related information through function calls.

cancel()

"cancel, open parenthesis close parenthesis."

This signals:

"The work using this context should stop."

Key takeaway

Graceful shutdown means signaling running work to stop and then waiting for cleanup to finish before the process exits.


Chapter 57 — Dependency Injection & Application Design

Question

Given below is a code snippet that:

  • Demonstrates dependency injection.

  • Uses an interface to separate business logic from a concrete dependency.

  • Injects a dependency into a service.

  • Demonstrates why this design makes code easier to test.

What should be the output of the following code?

package main

import "fmt"

type UserStore interface {
	FindName(id int) string
}

type MemoryStore struct{}

func (MemoryStore) FindName(id int) string {
	return "Ravi"
}

type UserService struct {
	store UserStore
}

func (s UserService) Greeting(id int) string {
	return "Hello, " + s.store.FindName(id)
}

func main() {
	store := MemoryStore{}
	service := UserService{store: store}

	fmt.Println(service.Greeting(10))
}

Answer

Hello, Ravi

Step-by-step explanation

  1. UserStore defines the behavior required by UserService.

  2. MemoryStore implements FindName, so it satisfies UserStore.

  3. UserService stores a UserStore rather than depending directly on MemoryStore.

  4. store is injected into UserService when the service is created.

  5. Greeting(10) asks the injected store for the user's name.

  6. The store returns "Ravi".

  7. The final result is "Hello, Ravi".

How to read the important code

service := UserService{store: store}

"service colon equals UserService, open curly brace, store colon store, close curly brace."

Dependency injection means:

Instead of a component creating everything it needs itself, those dependencies are supplied to it.

This is especially useful for replacing a real database with a fake or test implementation.

Key takeaway

Dependency injection keeps components loosely connected and makes testing and maintenance easier.


Chapter 58 — Clean Go Code & Idiomatic Go

Question

Given below is a code snippet that:

  • Demonstrates an idiomatic Go function.

  • Uses early return for an error condition.

  • Uses clear naming.

  • Demonstrates the common Go pattern of returning a value and an error.

What should be the output of the following code?

package main

import (
	"errors"
	"fmt"
)

func divide(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("cannot divide by zero")
	}

	return a / b, nil
}

func main() {
	result, err := divide(10, 2)

	if err != nil {
		fmt.Println("error:", err)
		return
	}

	fmt.Println("result:", result)
}

Answer

result: 5

Step-by-step explanation

  1. divide(10, 2) receives 10 and 2.

  2. b == 0 is false.

  3. The function returns 10 / 2, which is 5.

  4. It also returns nil as the error because no error occurred.

  5. err != nil is false, so the error branch is skipped.

  6. The result is printed.

How to read the important code

func divide(a, b float64) (float64, error)

"func divide, open parenthesis a comma b float64 close parenthesis, open parenthesis float64 comma error close parenthesis."

This function returns two values:

result
error

Go commonly uses this pattern:

value, err := someFunction()
if err != nil {
    // handle error
}

Idiomatic Go means writing code in the style that is natural, conventional, readable, and maintainable in the Go ecosystem.

Key takeaway

Idiomatic Go favors simple code, clear names, explicit error handling, and straightforward control flow.


Chapter 59 — Go Project Architecture

Question

Given below is a code snippet that:

  • Demonstrates separation between HTTP handling, business logic, and data access.

  • Uses interfaces to keep layers independent.

  • Demonstrates a simple dependency flow.

  • Shows a miniature version of how a real backend can be organized.

What should be the output of the following code?

package main

import "fmt"

type UserRepository interface {
	GetName(id int) string
}

type Repository struct{}

func (Repository) GetName(id int) string {
	return "Ravi"
}

type UserService struct {
	repo UserRepository
}

func (s UserService) GetUserName(id int) string {
	return s.repo.GetName(id)
}

type Handler struct {
	service UserService
}

func (h Handler) Handle(id int) {
	name := h.service.GetUserName(id)
	fmt.Println("HTTP response:", name)
}

func main() {
	repo := Repository{}
	service := UserService{repo: repo}
	handler := Handler{service: service}

	handler.Handle(1)
}

Answer

HTTP response: Ravi

Step-by-step explanation

  1. Repository represents the data-access layer.

  2. UserRepository defines what the service needs from the repository.

  3. UserService represents business logic.

  4. Handler represents the HTTP-facing layer.

  5. The repository is injected into the service.

  6. The service is injected into the handler.

  7. handler.Handle(1) starts the flow.

  8. The handler asks the service for the user's name.

  9. The service asks the repository.

  10. The repository returns "Ravi".

  11. The handler prints the simulated HTTP response.

How to read the important code

handler := Handler{service: service}

"handler colon equals Handler, open curly brace, service colon service, close curly brace."

The simplified flow is:

HTTP Handler
     ↓
Service / Business Logic
     ↓
Repository / Database

The important idea is separation of responsibilities:

  • Handler → deals with HTTP requests/responses.

  • Service → contains application/business logic.

  • Repository → deals with storing and retrieving data.

Key takeaway

Good Go architecture separates responsibilities so individual parts can be changed, tested, and maintained without rewriting the whole application.


Chapter 60 — Building Production-Ready REST APIs

Question

Given below is a code snippet that:

  • Demonstrates a realistic HTTP handler.

  • Decodes JSON from a request.

  • Validates input.

  • Returns a JSON response.

  • Uses an appropriate HTTP status code for invalid input.

What should be the output of the following code?

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
)

type User struct {
	Name string `json:"name"`
}

func createUser(w http.ResponseWriter, r *http.Request) {
	var user User

	err := json.NewDecoder(r.Body).Decode(&user)

	if err != nil || user.Name == "" {
		http.Error(w, "invalid user", http.StatusBadRequest)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)

	json.NewEncoder(w).Encode(user)
}

func main() {
	body := strings.NewReader(`{"name":"Ravi"}`)

	req := httptest.NewRequest(http.MethodPost, "/users", body)
	rec := httptest.NewRecorder()

	createUser(rec, req)

	fmt.Println("status:", rec.Code)
	fmt.Println("body:", strings.TrimSpace(rec.Body.String()))
}

Answer

status: 201
body: {"name":"Ravi"}

Step-by-step explanation

  1. The request body contains JSON: {"name":"Ravi"}.

  2. httptest.NewRequest creates a test HTTP request.

  3. httptest.NewRecorder captures the handler's response.

  4. json.NewDecoder(r.Body).Decode(&user) reads the JSON into the User struct.

  5. user.Name becomes "Ravi".

  6. The validation succeeds because the name isn't empty.

  7. The handler sets the response content type to JSON.

  8. http.StatusCreated is 201, which indicates that a resource was created.

  9. json.NewEncoder(w).Encode(user) writes the user as JSON.

  10. The recorder captures that response, allowing main() to print it.

How to read the important code

json.NewDecoder(r.Body).Decode(&user)

"json dot NewDecoder, open parenthesis r dot Body close parenthesis, dot Decode, open parenthesis ampersand user close parenthesis."

  • r.Body contains the request body.

  • Decode reads JSON and puts the result into user.

  • &user gives the decoder the address of user, allowing it to modify it.

w.WriteHeader(http.StatusCreated)

"w dot WriteHeader, open parenthesis http dot StatusCreated close parenthesis."

201 Created is a standard HTTP status meaning that a resource was successfully created.

Key takeaway

A production REST API must correctly handle requests, validate input, return appropriate HTTP status codes, and produce predictable response formats.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.