# Web Server EVO uses **[Fiber v3](https://github.com/gofiber/fiber)** as its web framework, which in turn uses **[fasthttp](https://github.com/valyala/fasthttp)** under the hood. EVO wraps the Fiber context into a `*Request` object that adds extra helpers, structured responses, and integration with other EVO subsystems. --- ## Setup & Run ```go package main import "github.com/getevo/evo/v2" func main() { if err := evo.Setup(); err != nil { panic(err) } evo.Get("/hello", func(r *evo.Request) any { return "hello world" }) if err := evo.Run(); err != nil { panic(err) } } ``` --- ## HTTP Methods All route registration functions accept one or more `Handler` functions: ```go type Handler func(request *evo.Request) any ``` ```go evo.Get(path, handlers...) evo.Post(path, handlers...) evo.Put(path, handlers...) evo.Patch(path, handlers...) evo.Delete(path, handlers...) evo.Head(path, handlers...) evo.Options(path, handlers...) evo.Trace(path, handlers...) evo.Connect(path, handlers...) evo.All(path, handlers...) // all HTTP methods ``` All functions return `fiber.Router` for further chaining (e.g. `.Name("my-route")`). --- ## Middleware ```go type Middleware func(request *evo.Request) error ``` ```go // Apply middleware to a path prefix evo.Use("/api", func(r *evo.Request) error { token := r.Header("Authorization") if token == "" { return r.Context.SendStatus(401) } return r.Next() }) // Catch-all handler (set before evo.Run) evo.Any = func(r *evo.Request) error { return r.Context.SendStatus(404) } ``` --- ## Route Groups ```go api := evo.Group("/api/v1") api.Get("/users", listUsers) api.Post("/users", createUser) // Group with middleware admin := evo.Group("/admin", authMiddleware) admin.Get("/dashboard", dashboard) // Nested groups v2 := api.Group("/v2") v2.Get("/users", listUsersV2) // Named groups (for URL generation) api.Name("api.") api.Get("/orders", listOrders) // route name: "api.GET/api/v1/orders" ``` --- ## Route Parameters ```go // Single named parameter evo.Get("/users/:id", func(r *evo.Request) any { id := r.Param("id").Int() return id }) // Multiple parameters evo.Get("/users/:userId/orders/:orderId", func(r *evo.Request) any { userId := r.Param("userId").Int64() orderId := r.Param("orderId").String() return nil }) // Wildcard evo.Get("/files/*", func(r *evo.Request) any { path := r.Param("*") return path }) // All route params as a map evo.Get("/a/:x/b/:y", func(r *evo.Request) any { params := r.Params() // map[string]string{"x":"...", "y":"..."} return params }) // Override a param value in middleware (before the handler runs) evo.Use("/", func(r *evo.Request) error { r.OverrideParam("id", "canonical-id") return r.Next() }) ``` --- ## Query String ```go // GET /search?q=fiber&page=2&active=true evo.Get("/search", func(r *evo.Request) any { q := r.Query("q").String() page := r.Query("page").Int() active := r.Query("active").Bool() // All query params at once all := r.Queries() // map[string]string return all }) ``` --- ## Request Headers ```go evo.Get("/path", func(r *evo.Request) any { ct := r.Header("Content-Type") token := r.Header("Authorization") // Quick existence check if r.HasHeader("X-Request-ID") { ... } // Unique request ID (set by Fiber's RequestID middleware) id := r.RequestID() // All request headers headers := r.ReqHeaders() // map[string]string _ = ct; _ = token; _ = id return nil }) ``` --- ## Request Body ### Auto-detect & bind (recommended — Fiber v3 Bind API) ```go type CreateUserDTO struct { Name string `json:"name" form:"name" query:"name"` Email string `json:"email" form:"email"` Age int `json:"age" form:"age"` } evo.Post("/users", func(r *evo.Request) any { var dto CreateUserDTO if err := r.Bind().Body(&dto); err != nil { return err } return dto }) ``` `Bind()` automatically selects the decoder based on `Content-Type` (JSON, XML, form, multipart). You can also bind multiple sources in one chain: ```go if err := r.Bind().Body(&body).Query(&q).Header(&h); err != nil { ... } ``` ### BodyParser (legacy — auto-detects JSON / form / XML) ```go evo.Post("/path", func(r *evo.Request) any { var body MyStruct if err := r.BodyParser(&body); err != nil { return err } return body }) ``` ### Raw body ```go evo.Post("/raw", func(r *evo.Request) any { raw := r.Body() // string b := r.BodyRaw() // []byte — undecoded (no decompression) _ = b return raw }) ``` ### JSON body without struct (gjson) ```go evo.Post("/path", func(r *evo.Request) any { name := r.ParseJsonBody().Get("user.name").String() age := r.ParseJsonBody().Get("user.age").Int() return name + " " + strconv.Itoa(int(age)) }) ``` ### Single form value ```go evo.Post("/path", func(r *evo.Request) any { name := r.FormValue("name").String() age := r.FormValue("age").Int() return name }) ``` ### Body existence check ```go if !r.HasBody() { return r.Context.SendStatus(400) } ``` ### File upload ```go evo.Post("/upload", func(r *evo.Request) any { file, err := r.FormFile("avatar") if err != nil { return err } // Save to local disk return r.SaveFile(file, "./uploads/"+file.Filename) }) ``` ### File upload to custom storage `SaveFileToStorage` writes the upload to any backend that satisfies the `fiber.Storage` interface (e.g. S3, Redis, in-memory): ```go evo.Post("/upload", func(r *evo.Request) any { file, err := r.FormFile("avatar") if err != nil { return err } return r.SaveFileToStorage(file, "avatars/"+file.Filename, myS3Storage) }) ``` --- ## URL Information ```go evo.Get("/info", func(r *evo.Request) any { full := r.FullURL() // https://example.com/info?x=1 base := r.BaseURL() // https://example.com path := r.Path() // /info original := r.OriginalURL() // /info?x=1 host := r.Hostname() // example.com (no port) rawHost := r.Host() // example.com:8080 (includes port when present) proto := r.Protocol() // https scheme := r.Scheme() // https (alias for Protocol) port := r.Port() // 443 method := r.Method() // GET secure := r.IsSecure() // true local := r.IsFromLocal() // false xhr := r.XHR() // false referer := r.Referer() // "https://other.com" ips := r.IPs() // []string ip := r.IP() // "203.0.113.5" fullPath := r.FullPath() // "/info" — matched route pattern (e.g. "/users/:id") _ = full; _ = base; _ = path; _ = original; _ = host; _ = rawHost _ = proto; _ = scheme; _ = port; _ = method; _ = secure; _ = local _ = xhr; _ = referer; _ = ips; _ = ip; _ = fullPath return nil }) ``` --- ## Request Inspection ```go evo.Get("/ws", func(r *evo.Request) any { if r.IsWebSocket() { // handle WebSocket upgrade } if r.IsPreflight() { // CORS preflight OPTIONS request r.Set("Access-Control-Allow-Origin", "*") return r.SendStatus(204) } if r.IsProxyTrusted() { // request came from a trusted proxy range } return nil }) ``` ### Range requests (partial content) ```go evo.Get("/video", func(r *evo.Request) any { fileSize := int64(1_000_000) rng, err := r.Range(fileSize) if err != nil { return r.SendStatus(416) // Range Not Satisfiable } // rng.Type — "bytes" // rng.Ranges — []fiber.RangeSet, each has Start and End offsets r.Status(206) return r.SendFile("./video.mp4") }) ``` --- ## Content-Type Helpers ```go mt := r.MediaType() // "application/json" cs := r.Charset() // "utf-8" ok := r.Is("json") // true if Content-Type is application/json // Quick boolean checks r.IsJSON() // Content-Type: application/json r.IsForm() // Content-Type: application/x-www-form-urlencoded r.IsMultipart() // Content-Type: multipart/form-data ``` --- ## Accept Header Helpers ```go // Boolean shortcuts r.AcceptsJSON() // true when Accept allows application/json r.AcceptsHTML() // true when Accept allows text/html r.AcceptsXML() // true when Accept allows application/xml or text/xml r.AcceptsEventStream() // true when Accept allows text/event-stream (SSE) // Single best-match values lang := r.AcceptLanguage() // e.g. "en-US" enc := r.AcceptEncoding() // e.g. "gzip" // Multi-offer negotiation (returns the best match or "") best := r.AcceptsLanguages("en", "fr", "de") best = r.AcceptsLanguagesExtended("en-US", "en", "fr") // RFC 4647 subtag matching best = r.AcceptsCharsets("utf-8", "iso-8859-1") best = r.AcceptsEncodings("gzip", "deflate", "identity") ``` --- ## Sending Responses ### JSON (most common) ```go evo.Get("/users/:id", func(r *evo.Request) any { user := getUser(r.Param("id").Int()) return user // auto-wrapped in {"success":true,"data":{...}} }) // Or send directly without the wrapper: r.JSON(user) ``` ### XML ```go r.XML(myStruct) // Content-Type: application/xml ``` ### Plain text / HTML ```go r.SendString("hello") r.SendHTML("