Technical Guide

From Installing Codex to Building a Simple Go HTTP Service

A step-by-step developer tutorial showing how to use Codex to create a small Go HTTP service, test it locally, and package it with Docker.

Published on 2026-02-1810 min read

Readable data flow

A practical mental model for the guide below

01

Raw payload

02

Validate

03

Format

04

Review

Original CodeToolia illustration for this developer guide.

What we will build

This guide walks through a small but realistic Codex workflow: starting from a blank folder, describing a task, asking Codex to generate code, reviewing the result, testing it locally, and packaging the service with Docker. The example is inspired by a compact Go project that accepts a batch of URL check requests and returns a JSON result for each item.

The service is intentionally simple enough to understand in one sitting, but practical enough to show how Codex can help with real development work. It includes request validation, JSON input and output, a reusable HTTP client, timeout settings, basic logging, and a production-friendly Dockerfile.

Install or open Codex

Start by installing Codex from the official OpenAI distribution channel available to your account, or open the Codex desktop app if it is already installed. After signing in, choose or create a local workspace folder. In this tutorial, the project folder is named `net_click`, but any folder name is fine.

Codex works best when the task is concrete. Instead of asking it to build a vague backend, describe the inputs, outputs, language, runtime, and test expectations. A clear first prompt saves time because Codex can create the correct files and then iterate with you.

Example Codex prompt

text
Create a small Go HTTP service. It should listen on PORT or default to 8080. It should accept POST / with a JSON array of objects containing path and body. For each item, send a POST request to path with the body encoded as JSON. Return a JSON array containing path, success, status, location, and error when needed. Add a Dockerfile and README.

Readable data flow

A practical mental model for the guide below

01

Raw payload

02

Validate

03

Format

04

Review

Original CodeToolia illustration for this developer guide.

Create the Go module

The first file is `go.mod`. It declares the module name and Go version. This example uses the standard library only, so there are no third-party dependencies to install. That makes the service easy to audit and simple to build in a container.

A small service is a good starting point for Codex because the generated code is easy to review. You can ask Codex to explain each function, tighten timeouts, add tests, or change the API shape after the first version works.

go.mod

go
module net_click

go 1.22

Define request and response types

The service receives a JSON array. Each item contains a target URL and a `body` object that represents simulated request parameters. This makes the example useful for testing webhook receivers, mock API endpoints, and small integration services without relying on custom client headers.

The response returns one result per input item. Pointer fields are used for `path`, `status`, and `location` so missing values can be represented cleanly in JSON. This is a good place to ask Codex for changes if your real API contract is different. For example, you might rename `path` to `url`, add an ID field, or include response time in milliseconds.

Request and response structs

go
type BatchRequestItem struct {
	Path string         `json:"path"`
	Body map[string]any `json:"body"`
}

type BatchResult struct {
	Path     *string `json:"path"`
	Success  bool    `json:"success"`
	Status   *int    `json:"status,omitempty"`
	Location *string `json:"location,omitempty"`
	Error    string  `json:"error,omitempty"`
}

type ErrorResponse struct {
	Error  string `json:"error"`
	Method string `json:"method,omitempty"`
}

Build the HTTP server

The server uses Go's standard `net/http` package. It only accepts `POST /`. Other paths return `404`, and other methods return `405`. The body is decoded as a JSON array and each item is processed concurrently, preserving the original order in the final response.

When reviewing generated code from Codex, look for boring reliability details: timeouts, body closing, content types, error responses, and predictable ordering. Those details matter more than cleverness in a small HTTP service.

Server and handler

go
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", handler)

	addr := getListenAddr()
	log.Printf("listening on %s", addr)

	server := &http.Server{
		Addr:              addr,
		Handler:           mux,
		ReadHeaderTimeout: 10 * time.Second,
	}

	if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		log.Fatal(err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		writeJSON(w, http.StatusNotFound, ErrorResponse{Error: "Not Found"})
		return
	}

	if r.Method != http.MethodPost {
		writeJSON(w, http.StatusMethodNotAllowed, ErrorResponse{Error: "Method Not Allowed", Method: r.Method})
		return
	}

	defer r.Body.Close()

	var list []BatchRequestItem
	if err := json.NewDecoder(r.Body).Decode(&list); err != nil {
		writeJSON(w, http.StatusBadRequest, ErrorResponse{Error: "Invalid JSON body"})
		return
	}

	results := make([]BatchResult, len(list))
	ch := make(chan struct {
		index  int
		result BatchResult
	}, len(list))

	for i, item := range list {
		go func(index int, reqItem BatchRequestItem) {
			ch <- struct {
				index  int
				result BatchResult
			}{index: index, result: processRequest(reqItem)}
		}(i, item)
	}

	for range list {
		item := <-ch
		results[item.index] = item.result
	}

	writeJSON(w, http.StatusOK, results)
}

Process each URL request

The `processRequest` function validates the input, serializes the simulated body parameters, creates a POST request, performs the request, discards the response body, and returns status information. Redirects are not followed automatically, so the service can report a `Location` header when a URL responds with a redirect status.

This is the kind of code that benefits from Codex review. You can ask it to explain why redirects are disabled, why the response body is discarded, or how to add metrics. You can also ask it to harden the function against private network access if the service will be exposed publicly.

Request processing

go
func processRequest(item BatchRequestItem) BatchResult {
	if strings.TrimSpace(item.Path) == "" {
		return BatchResult{Path: nil, Success: false, Error: "Missing path"}
	}

	payload, err := json.Marshal(item.Body)
	if err != nil {
		return BatchResult{Path: stringPtr(item.Path), Success: false, Error: err.Error()}
	}

	req, err := http.NewRequest(http.MethodPost, item.Path, bytes.NewReader(payload))
	if err != nil {
		return BatchResult{Path: stringPtr(item.Path), Success: false, Error: err.Error()}
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Cache-Control", "no-cache")

	resp, err := httpClient.Do(req)
	if err != nil {
		return BatchResult{Path: stringPtr(item.Path), Success: false, Error: err.Error()}
	}
	defer resp.Body.Close()

	_, _ = io.Copy(io.Discard, resp.Body)

	var location *string
	if value := resp.Header.Get("Location"); value != "" {
		location = stringPtr(value)
	}

	status := resp.StatusCode
	return BatchResult{Path: stringPtr(item.Path), Success: true, Status: &status, Location: location}
}

Run and test locally

After Codex creates the files, run the service locally. The default port is `8080`, but the service also reads a `PORT` environment variable. This makes it easy to run in Docker, cloud platforms, or local shells with different ports.

Use a small curl request to verify the full request and response flow. The result should be a JSON array with one object for each input item. The `body` object can contain any simulated parameters you want to send to the target endpoint. If the target URL redirects, the `location` field may appear. If the URL is invalid, the result should include an error message.

Run the service

powershell
go run .

Test with curl

powershell
curl -X POST http://localhost:8080/ `
  -H "Content-Type: application/json" `
  -d "[{\"path\":\"https://codetoolia.com/webhook\",\"body\":{\"event\":\"signup\",\"userId\":\"user_123\",\"plan\":\"pro\"}}]"

Package it with Docker

A multi-stage Dockerfile keeps the final image small. The build stage compiles the Go binary, and the final stage copies only the binary into an Alpine image. The service runs as a non-root user and exposes port `8080`.

This is another useful Codex workflow: ask it to create the first Dockerfile, then ask it to explain each line. You can also ask for improvements such as adding labels, switching base images, or producing a minimal scratch-based image if your deployment environment supports it.

Dockerfile

dockerfile
FROM golang:1.22-alpine AS build
WORKDIR /src

COPY go.mod ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /app/net-click .

FROM alpine:3.20 AS final
WORKDIR /app

RUN adduser -D -H -u 10001 appuser

ENV PORT=8080
EXPOSE 8080

COPY --from=build /app/net-click /app/net-click

USER appuser

ENTRYPOINT ["/app/net-click"]

Build and run the container

powershell
docker build -t net-click:latest .
docker run --rm -p 8080:8080 net-click:latest

What to ask Codex next

Once the first version works, use Codex as a review partner. Ask it to add tests for invalid JSON, missing paths, non-POST methods, redirects, and request failures. Ask it to explain timeout choices. Ask it to add structured logging or a health endpoint. Small follow-up requests produce better results than one giant prompt.

For production use, also think about abuse prevention. A service that fetches arbitrary URLs can become risky if exposed publicly. You may need allowlists, private network blocking, rate limits, authentication, request size limits, and better observability. Codex can help draft those changes, but the final security decisions should be reviewed carefully.

The main lesson is not that Codex writes perfect code in one shot. The lesson is that a developer can describe a clear target, let Codex produce a useful first draft, inspect the code, run it, and iterate. That loop is where AI-assisted development becomes practical.

Implementation Checklist

Checklist
  • 01.Validate data protocols in your specific target runtime environment.
  • 02.Perform edge-case testing beyond basic 'happy-path' scenarios.
  • 03.Document specific debugging context for future maintenance.
  • 04.Use specialized validation tools for mission-critical services.
DT

Written by the CodeToolia editorial team

CodeToolia publishes practical references for developers who work with APIs, browser data, encoding formats, automation, and debugging workflows. Articles are written to be useful alongside the tools on this site.

Read more insights