This episode builds your first actually-running Fiber project: installing Fiber v3, creating an app instance, adding the Logger and Recover middleware, then returning JSON and text responses with the correct status codes, complete with testing via curl.

Enough theory. Episode 3 invites you to build your first actually-running Fiber project: hello world. This is the most important moment in a series — when the first route comes alive and responds to a request via curl, all the concepts from episodes 1 and 2 start to feel real.
This episode's goal: build a small server with several endpoints, add the default Logger and Recover middleware, and return JSON and text responses with the correct status codes. All these patterns become the foundation for every following episode.
Make sure your episode 0 environment is ready. Create the module and install Fiber v3:
mkdir hello-fiber
cd hello-fiber
go mod init github.com/username/hello-fiber
go get github.com/gofiber/fiber/v3@latestgo get github.com/gofiber/fiber/v3@latest adds the dependency to go.mod. After that, create the main.go file — our entire hello world app lives in this single file.
package main
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(logger.New())
app.Use(recover.New())
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Halo Dunia")
})
app.Get("/health", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"status": "ok"})
})
log.Fatal(app.Listen(":3000"))
}fiber.New() creates an empty App instance. logger.New() logs every request to stdout, and recover.New() catches panics so the server doesn't die — we'll break both down in depth in episodes 6 and 11. Note that app.Listen(":3000") returns an error, so we wrap it with log.Fatal.
Run the server, then test from another terminal:
go run main.gocurl -i http://localhost:3000/The expected response: status 200 OK, header Content-Type: text/plain, and body Halo Dunia. curl -i http://localhost:3000/ shows both headers and body so you can see the whole response.
curl -i http://localhost:3000/healthThe health endpoint returns JSON {"status":"ok"} with the header Content-Type: application/json.
Fiber provides several of the most frequently used response helpers:
app.Get("/teks", func(c fiber.Ctx) error {
return c.SendString("respons teks biasa")
})
app.Get("/json", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "respons json"})
})
app.Get("/created", func(c fiber.Ctx) error {
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"id": "123",
})
})
app.Get("/teks-status", func(c fiber.Ctx) error {
return c.Status(201).SendString("resource dibuat")
})c.SendString returns text with Content-Type: text/plain, c.JSON converts a map or struct to JSON, and c.Status sets the status code before other helpers are called. c.Status(fiber.StatusCreated).JSON(...) is the common pattern for a 201 Created response on resource creation operations.
curl -i http://localhost:3000/createdThe output shows HTTP/1.1 201 Created. All status constants are available as fiber.StatusOK, fiber.StatusBadRequest, fiber.StatusNotFound, and so on — matching the standard net/http enumeration.
Without a logger, you won't see anything when requests come in. logger.New() writes one log line per request in a customizable format:
app.Use(logger.New(logger.Config{
Format: "[${time}] ${status} ${latency} ${ip} ${method} ${path}\n",
}))The ${time}, ${status}, ${latency}, ${ip}, ${method}, and ${path} tags are supported by Fiber's logger. logger.New(logger.Config{Format: ...}) lets you customize what gets recorded for audit or debugging needs.
Recover catches panics in handlers and returns a 500 Internal Server Error response instead of killing the server. Without recover, a single panic can take down the whole process. In production, the logger plus recover combination is the minimum baseline you should always have.
Key takeaways:
go mod init then go get github.com/gofiber/fiber/v3@latest.fiber.New() then app.Get("/", handler) is the basic recipe.app.Listen(":3000") returns an error; wrap it with log.Fatal.c.SendString for text, c.JSON for JSON, c.Status for status codes.logger.New() and recover.New() are mandatory default middleware.curl -i.In the next episode, episode 4, we move into the heart of the router: routing, params, and constraints — path parameters, wildcards, query parameters, :id<int> and minLen constraints, route chaining, domain routing, and groups and nested groups. This will change the way you design URLs.