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.
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.
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 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).
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.
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 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.

Run in your terminal to get started locally.
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.
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.
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.
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.
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.