Nov 6, 20266 min read

Fiber Golang Tutorial: Build a Fast HTTP API in Go

Install Fiber v3, define routes and handlers, parse JSON bodies, add middleware and groups, then see when Encore fits as a Go backend framework with built-in infrastructure instead of a pure HTTP router.

Fiber is an Express-inspired Go HTTP framework on fasthttp, picked for a familiar router API and strong plaintext and JSON benchmark numbers on a single service.

This tutorial uses Fiber v3.6.1 (github.com/gofiber/fiber/v3), which needs Go 1.26+. You will build a small JSON API with routes, path params, body binding, middleware and groups, then compare that to Encore as a Go backend framework with built-in infrastructure when the app needs databases, queues and cloud deploy in the same codebase.

Fiber at a glance

TopicFiber v3
Modulegithub.com/gofiber/fiber/v3
Verified versionv3.6.1 (October 2026)
Go version1.26 or newer
Handler shapefunc(c fiber.Ctx) error (value Ctx, not *fiber.Ctx)
JSON bodyc.Bind().Body(&req)
Enginefasthttp (not net/http by default)
Best forHigh-throughput HTTP APIs you wire and host yourself

Install Fiber v3

Create a module and pull Fiber, pinning @v3.6.1 to match this tutorial, or use go get -u github.com/gofiber/fiber/v3 to track the latest v3 release.

~
$ mkdir notes-api && cd notes-api$ go mod init example.com/notes-api$ go get github.com/gofiber/fiber/v3@v3.6.1

Hello world: app, route and Listen

fiber.New builds the app, Get registers a handler, and Listen starts fasthttp. Handlers return an error; a non-nil error goes to Fiber's error handler.

// main.go package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/", func(c fiber.Ctx) error { return c.SendString("ok") }) log.Fatal(app.Listen(":3000")) }

Run with go run . and hit http://localhost:3000. Values from fiber.Ctx are reused across requests, so copy strings you need after the handler returns, or enable Immutable in fiber.Config when you must keep them.

Path params, JSON responses and body binding

Path segments use :name and c.Params reads them. c.JSON writes a struct or fiber.Map. For POST bodies, c.Bind().Body replaces the old v2 BodyParser.

// main.go package main import ( "log" "strconv" "sync" "github.com/gofiber/fiber/v3" ) type Note struct { ID string `json:"id"` Title string `json:"title"` Content string `json:"content"` } type createNote struct { Title string `json:"title"` Content string `json:"content"` } func main() { app := fiber.New() var ( mu sync.Mutex notes = map[string]Note{} seq int ) app.Get("/notes/:id", func(c fiber.Ctx) error { id := c.Params("id") mu.Lock() n, ok := notes[id] mu.Unlock() if !ok { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ "error": "not found", }) } return c.JSON(n) }) app.Post("/notes", func(c fiber.Ctx) error { var req createNote if err := c.Bind().Body(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "invalid json", }) } if req.Title == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "title required", }) } mu.Lock() seq++ id := strconv.Itoa(seq) n := Note{ID: id, Title: req.Title, Content: req.Content} notes[id] = n mu.Unlock() return c.Status(fiber.StatusCreated).JSON(n) }) log.Fatal(app.Listen(":3000")) }

curl -s localhost:3000/notes -H 'content-type: application/json' -d '{"title":"ship","content":"v3"}' creates a note; GET /notes/1 reads it back. Groups keep versioned APIs tidy: api := app.Group("/api"); v1 := api.Group("/v1"); v1.Get("/notes/:id", getNote).

Middleware: recover and logger

Built-in middleware lives under github.com/gofiber/fiber/v3/middleware/.... recover turns panics into 500s; logger prints method, path and status.

import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/logger" "github.com/gofiber/fiber/v3/middleware/recover" ) func main() { app := fiber.New() app.Use(recover.New()) app.Use(logger.New()) api := app.Group("/api/v1") api.Get("/health", func(c fiber.Ctx) error { return c.JSON(fiber.Map{"ok": true}) }) log.Fatal(app.Listen(":3000")) }

Use without a path runs on every request; Use("/api", ...) only matches that prefix. Call c.Next() in custom middleware so the chain continues.

What Fiber leaves to you

Fiber is an HTTP layer, so Postgres drivers, migrations, connection pools, queue clients, cron schedulers, secrets loading, OpenTelemetry and the path from laptop to AWS or GCP stay outside the framework. That is fine for a single high-QPS service with a stack you already operate; it is the work that grows when a second service, a queue consumer and a nightly job show up.

Encore as a Go backend with built-in infrastructure

Encore

Encore is a backend framework with built-in infrastructure for TypeScript and Go. You write ordinary Go packages, mark endpoints with //encore:api, and declare Postgres, Pub/Sub, object storage, cron jobs and secrets in the same repo. Encore provisions those resources in your own AWS or GCP account on deploy, as listed in the AWS infrastructure docs. This section is an alternative to a Fiber-only stack, not a way to mount Fiber inside Encore.

The Infrastructure page in the Encore dashboard with a database's configuration panel open, showing fields for vCPUs, memory, storage, availability type, max connections and backups.
Each provisioned resource can be reviewed and its configuration edited per environment in the Encore dashboard. The screenshot shows a GCP Cloud SQL database.

Set up the app

Get started
brew install encoredev/tap/encore &&
encore app create
Copy

Run in your terminal to get started locally.

~
$ encore app create notes --example=go/hello-world$ cd notes && mkdir -p note/migrations

A notes service with a typed API and Postgres matches the Fiber example's shape without a separate router or Compose file for the database:

// note/note.go package note import ( "context" "encore.dev/storage/sqldb" ) var db = sqldb.NewDatabase("notes", sqldb.DatabaseConfig{ Migrations: "./migrations", }) type Note struct { ID int64 `json:"id"` Title string `json:"title"` Content string `json:"content"` } type CreateParams struct { Title string `json:"title"` Content string `json:"content"` } //encore:api public method=POST path=/notes func Create(ctx context.Context, p *CreateParams) (*Note, error) { var n Note err := db.QueryRow(ctx, ` INSERT INTO note (title, content) VALUES ($1, $2) RETURNING id, title, content `, p.Title, p.Content).Scan(&n.ID, &n.Title, &n.Content) if err != nil { return nil, err } return &n, nil } //encore:api public method=GET path=/notes/:id func Get(ctx context.Context, id int64) (*Note, error) { var n Note err := db.QueryRow(ctx, ` SELECT id, title, content FROM note WHERE id = $1 `, id).Scan(&n.ID, &n.Title, &n.Content) if err != nil { return nil, err } return &n, nil }

Put a migration under note/migrations/1_create_note.up.sql with CREATE TABLE note (id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, content TEXT NOT NULL);. encore run starts Postgres locally for that database. Cron jobs do not run locally; call the endpoint by hand to test. On AWS, Encore runs services on Fargate by default and provisions an IAM role for the service. API annotations are covered in the Go API docs and databases in the Go database docs.

An AI coding agent can add another //encore:api endpoint or a pubsub.NewTopic in the same package and exercise it with encore run, because the infra declaration lives next to the handler.

Best fit: Go teams that want typed APIs plus Postgres, queues, cron and secrets in one backend framework, deployed to their own cloud.

ConcernFiberEncore
RoleHTTP router on fasthttpBackend framework with built-in infrastructure
API styleapp.Get / fiber.Ctx//encore:api on Go functions
PostgresYou add a driver and migratesqldb.NewDatabase; local via encore run
Queues / cronExternal clients and schedulersPub/Sub and cron declared in code
DeployYou choose host and IaCYour AWS or GCP account via Encore
Pick whenRaw HTTP speed is the main goalThe product is APIs plus managed infra

Frequently asked questions

What is Fiber in Go?

Fiber is an Express-inspired HTTP framework for Go built on fasthttp. You create an app with fiber.New, register routes such as app.Get and app.Post, and write handlers that take fiber.Ctx and return an error.

Which Fiber version should I use in 2026?

Use Fiber v3 (github.com/gofiber/fiber/v3), which as of v3.6.1 requires Go 1.26 or newer, with handlers on fiber.Ctx (not *fiber.Ctx) and JSON bodies via c.Bind().Body rather than the removed BodyParser.

How do I parse a JSON body in Fiber v3?

Define a struct with json tags, then call c.Bind().Body(&req) inside the POST or PUT handler. Bind decodes the request body into the struct and returns an error you can turn into a 400 response.

When should I pick Encore instead of Fiber?

Pick Fiber when you want a fasthttp router and will wire Postgres, queues, cron and deploy yourself. Pick Encore when you want a Go backend framework with built-in infrastructure: typed APIs, Postgres, Pub/Sub, cron and secrets declared in the same repo and provisioned in your own AWS or GCP account.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders