Tutorials Logic, IN info@tutorialslogic.com

golang web api: Build a REST Endpoint with net/http

golang web api

Golang can build useful web APIs with only the standard library. The net/http package provides servers, handlers, requests, responses, headers, status codes, and routing basics.

A handler is a function that receives http.ResponseWriter and *http.Request. The request contains method, URL, headers, query parameters, and body. The response writer sends headers, status, and body data back to the client.

Many production teams use routers or frameworks, but learning the standard library first helps you understand what those tools are doing under the hood.

Add one worked example that compares the normal path with the boundary case for golang web api: Build a REST Endpoint with net/http.

golang web api Build a REST Endpoint with net http should be studied as a practical Golang lesson, not as a label. Start by naming the input, the rule that changes the input, and the result a learner should be able to predict after reading the page.

Building a Small API

Basic JSON API

Basic JSON API
package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type Tutorial struct {
    ID    int    `json:"id"`
    Title string `json:"title"`
}

func tutorialsHandler(w http.ResponseWriter, r *http.Request) {
    tutorials := []Tutorial{
        {ID: 1, Title: "Golang Syntax"},
        {ID: 2, Title: "Golang Web API"},
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(tutorials)
}

func main() {
    http.HandleFunc("/tutorials", tutorialsHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Handlers and ResponseWriter

A handler should write exactly one response for each request path. Set headers first, then set the status code, then write the response body.

If you call json.NewEncoder(w).Encode(...) without calling WriteHeader, Golang sends 200 OK automatically when the body is written. For non-200 responses, call WriteHeader before writing the body.

Step Example
Set header w.Header().Set("Content-Type", "application/json")
Set status w.WriteHeader(http.StatusCreated)
Write body json.NewEncoder(w).Encode(response)

HTTP Methods and Status Codes

Real APIs should validate the HTTP method. A list endpoint usually allows GET, a create endpoint usually allows POST, and unsupported methods should return 405 Method Not Allowed.

Status codes are part of the API contract. Use them consistently so clients can handle success and failure without reading fragile text messages.

Status Common Meaning
200 OK Request succeeded and response body is returned.
201 Created A new resource was created.
400 Bad Request Input is invalid or malformed.
404 Not Found Requested resource does not exist.
405 Method Not Allowed Endpoint exists, but the method is unsupported.
500 Internal Server Error Unexpected server failure.

Method Guard

Method Guard
func tutorialsHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode([]string{"Golang", "TypeScript"})
}

Query Parameters

Use query parameters for optional filters, pagination, search terms, and sorting options. They are available through r.URL.Query().

Query values arrive as strings, so parse and validate them before use. Do not trust a query parameter just because it exists.

Read Query Parameters

Read Query Parameters
func listTutorialsHandler(w http.ResponseWriter, r *http.Request) {
    query := r.URL.Query()
    pageValue := query.Get("page")

    page := 1
    if pageValue != "" {
        parsed, err := strconv.Atoi(pageValue)
        if err != nil || parsed < 1 {
            http.Error(w, "invalid page", http.StatusBadRequest)
            return
        }
        page = parsed
    }

    json.NewEncoder(w).Encode(map[string]int{"page": page})
}

Reading JSON Request Bodies

For endpoints that accept JSON, decode the request body into a struct. After decoding, validate required fields and business rules. Valid JSON syntax does not mean the request is valid for your application.

Use struct tags such as json:"title" to control field names. Keep request structs separate from database models when validation or public API shape differs from storage shape.

Decode and Validate JSON

Decode and Validate JSON
type CreateTutorialRequest struct {
    Title string `json:"title"`
}

func createTutorialHandler(w http.ResponseWriter, r *http.Request) {
    var input CreateTutorialRequest
    if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
        http.Error(w, "invalid json", http.StatusBadRequest)
        return
    }

    if input.Title == "" {
        http.Error(w, "title is required", http.StatusBadRequest)
        return
    }

    w.WriteHeader(http.StatusCreated)
}

Writing JSON Errors

For APIs, JSON error responses are often easier for clients to consume than plain text. A small helper keeps error formatting consistent across handlers.

Keep public error messages safe. Do not expose database errors, stack traces, secrets, or internal infrastructure details to API clients.

JSON Error Helper

JSON Error Helper
type ErrorResponse struct {
    Error string `json:"error"`
}

func writeJSONError(w http.ResponseWriter, status int, message string) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(ErrorResponse{Error: message})
}

Separating Handler and Business Logic

A clean handler should focus on HTTP details: read input, validate simple request shape, call a service, and write a response. Business rules should live in functions or services that are easy to test without a web server.

This separation becomes important as the API grows. It keeps routing, JSON, validation, database calls, and business rules from turning into one giant handler function.

Layer Responsibility
Handler HTTP method, request parsing, status codes, response format
Service Business rules and use-case orchestration
Repository Database or external storage access
Model Data structures used by the app

Middleware Basics

Middleware wraps a handler with shared behavior. Common middleware handles logging, panic recovery, authentication, request IDs, CORS, timeouts, and metrics.

In the standard library, middleware is usually a function that accepts an http.Handler and returns another http.Handler.

Logging Middleware

Logging Middleware
func logging(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/tutorials", tutorialsHandler)
    log.Fatal(http.ListenAndServe(":8080", logging(mux)))
}

Testing API Handlers

The standard library includes net/http/httptest, which lets you test handlers without opening a real network port. This makes API tests fast and predictable.

Test success cases, invalid methods, bad JSON, missing fields, query validation, and service errors. Good API tests verify both the status code and the response body.

  • Return JSON with the correct Content-Type header.
  • Use clear status codes such as 200, 201, 400, 404, 405, and 500.
  • Validate path values, query parameters, and request bodies.
  • Keep handlers small enough to test comfortably.
  • Use httptest.NewRecorder and httptest.NewRequest for handler tests.

golang web api Build a REST Endpoint with net http normal path trace

golang web api Build a REST Endpoint with net http normal path trace
1. Define the input for golang web api Build a REST Endpoint with net http.
2. Apply the rule from the lesson.
3. Compare the actual result with the expected result.
4. Record the fix if the result differs.

golang web api Build a REST Endpoint with net http edge path trace

golang web api Build a REST Endpoint with net http edge path trace
1. Try empty, missing, duplicate, or invalid data.
2. Identify where golang web api Build a REST Endpoint with net http changes behavior.
3. Explain the safest correction.
4. Retest the normal path.
Key Takeaways
  • net/http is enough to build practical Golang APIs.
  • Handlers receive http.ResponseWriter and *http.Request.
  • Set headers and status codes before writing the body.
  • Validate request method, query parameters, JSON body, and required fields.
  • Use JSON helpers for consistent API responses.
  • Move business logic out of handlers so it can be tested directly.
  • Middleware wraps handlers with shared cross-cutting behavior.
Common Mistakes to Avoid
WRONG Memorizing golang web api Build a REST Endpoint with net http without the situation where it is useful.
RIGHT Connect golang web api Build a REST Endpoint with net http to a concrete Golang task.
Purpose makes syntax easier to recall.
WRONG Testing golang web api Build a REST Endpoint with net http only with the perfect input.
RIGHT Include empty, missing, duplicate, incompatible, or failed cases when relevant.
Real bugs usually appear outside the perfect path.
WRONG Changing code before reading the visible symptom or error message.
RIGHT Inspect the output, state, configuration, or stack trace connected to golang web api Build a REST Endpoint with net http.
Evidence keeps debugging focused.
WRONG Memorizing golang web api Build a REST Endpoint with net http without the situation where it is useful.
RIGHT Connect golang web api Build a REST Endpoint with net http to a concrete Golang task.
Purpose makes syntax easier to recall.

Practice Tasks

  • Modify the example so it handles a different input or condition.
  • Write one mistake related to golang web api: Build a REST Endpoint with net/http, then fix it and explain the fix.
  • Summarize when to use golang web api: Build a REST Endpoint with net/http and when another approach is better.
  • Write a small example that uses golang web api Build a REST Endpoint with net http in a realistic Golang scenario.
  • Change one important value in the golang web api Build a REST Endpoint with net http example and predict the result first.

Frequently Asked Questions

The common mistake is memorizing syntax without understanding when the behavior changes or fails.

Remember the problem it solves in Golang, then attach the syntax or steps to that problem.

You can predict the result of a small example, explain a failure case, and choose it over a nearby alternative for a clear reason.

They often copy the syntax but skip the state, input, dependency, selector, route, type, or configuration that controls the behavior.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.