quick

package module
v0.0.0-...-f155a4b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2025 License: MIT Imports: 30 Imported by: 13

README ΒΆ

Logo do Quick

GoDoc CircleCI Go Report License CircleCI Coveralls GitHub contributors GitHub stars GitHub stars GitHub stars

   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•—
  β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•   β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•
  β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘      β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•
  β–ˆβ–ˆβ•‘β–„β–„ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘      β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•—
  β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•” β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•—
   β•šβ•β•β–€β–€β•β•  β•šβ•β•β•β•β•β• β•šβ•β• β•šβ•β•β•β•β•β• β•šβ•β•  β•šβ•β•

 Quick v0.0.1 πŸš€ Fast & Minimal Web Framework
─────────────────── ───────────────────────────────
 🌎 Host : http://0.0.0.0
 πŸ“Œ Port : 8080
 πŸ”€ Routes: 4
─────────────────── ───────────────────────────────

πŸš€ Quick is a flexible and extensible route manager for the Go language. Its goal is to be fast and high-performance, as well as being 100% compatible with net/http. Quick is a project in constant development and is open for collaboration, everyone is welcome to contribute. 😍

πŸ’‘ If you’re new to coding, Quick is a great opportunity to start learning how to work with Go. With its ease of use and features, you can create custom routes and expand your knowledge of the language.

πŸ‘ I hope you can participate and enjoy Enjoy! 😍

πŸ” The repository of examples of the Framework Quick Run Examples.

Quick in action πŸ’•πŸ§πŸš€πŸ˜

Quick


πŸ“¦ Go Packages Documentation

To access the documentation for each Quick Framework package, click on the links below:

Package Description Go.dev
quick Main Router and Framework Features GoDoc
quick/http/client HTTP client optimized for requests and failover GoDoc
quick/middleware Framework middlewares GoDoc
quick/ctx HTTP request and response context GoDoc
quick/http/status HTTP status definitions in the framework GoDoc
quick/group Route group manipulation GoDoc
quickTest Package for unit testing and integration in Quick GoDoc
quick/route Route definition and management GoDoc
quick/config Framework configuration structures GoDoc
quick/qtest Auxiliary tools for testing in the Quick GoDoc
quick/uploadedFile File upload management GoDoc
quick/zeroth Framework helpers GoDoc
quick/template Template GoDoc
quick/benchmarks Benchmarks GoDoc
quick/example Usage examples GoDoc

πŸŽ›οΈ| Features

Features Has Status Completion
πŸ›£οΈ Route Manager yes 🟒 100%
πŸ“ Server Files Static yes 🟒 100%
πŸ”— Http Client yes 🟒 100%
πŸ“€ Upload Files (multipart/form-data) yes 🟒 100%
πŸšͺ Route Group yes 🟒 100%
πŸ›‘οΈ Middlewares yes 🟑 50%
⚑ HTTP/2 support yes 🟒 100%
πŸ”„ Data binding for JSON, XML and form payload yes 🟒 100%
πŸ” Regex support yes 🟑 80%
🌎 Website yes 🟑 90%
πŸ“š Documentation yes 🟑 40%
🧩 Template Engine with Layout Support yes 🟒 100%
πŸ“¦ Embedded Template Support (embed.FS) yes 🟒 100%
πŸ”§ Custom Template Functions (AddFunc) yes 🟒 100%
πŸ§ͺ Built-in Test Engine (Qtest) yes 🟒 100%
🧡 Middleware: Rate Limiting yes 🟒 100%
🧡 Middleware: Logger yes 🟒 100%
🧡 Middleware: Recover (panic handler) yes 🟒 100%
🧡 Middleware: CORS yes 🟒 100%
🧡 Middleware: Helmet (security headers) yes 🟒 100%
🧡 Middleware: MaxBody yes 🟒 100%
πŸ” Middleware: BasicAuth yes 🟒 100%
🧠 Middleware: PPROF yes 🟒 100%
πŸ› οΈ Healthcheck Middleware yes 🟒 100%
πŸš€ Performance Optimized Routing yes 🟒 100%
🧱 Extensible Plugin/Middleware System yes 🟑 60%

πŸ—ΊοΈ Development Roadmap

βœ… Completed Tasks

Task Progress
Develop MaxBodySize method Post βœ… 100%
Develop MaxBodySize method Put βœ… 100%
Develop Config in New(Config{}) not required βœ… 100%
Create print function to not use fmt too much βœ… 100%
Creation of own function for Concat String βœ… 100%
Creation of benchmarking between the Stdout and fmt.Println βœ… 100%
Develop Routes GET method βœ… 100%
Develop Routes GET method by accepting Query String βœ… 100%
Develop Routes GET method accepting Parameters βœ… 100%
Develop Routes GET method accepting Query String and Parameters βœ… 100%
Develop Routes GET method accepting regular expression βœ… 100%
Develop Routes Method POST βœ… 100%
Develop Routes POST method accepting JSON βœ… 100%
Develop for METHOD POST the parse JSON βœ… 100%
Develop for the POST METHOD functions to access byte or string from Parse βœ… 100%
Develop for PUT METHOD βœ… 100%
Develop for the PUT METHOD the JSON parse βœ… 100%
Develop for the PUT METHOD the JSON parse βœ… 100%
Develop for METHOD PUT functions to access byte or string from the Parse βœ… 100%
Develop for DELETE METHOD βœ… 100%
Develop method for ListenAndServe βœ… 100%
Develop ServeHTTP support βœ… 100%
Develop middleware support βœ… 100%
Develop support for middleware compress βœ… 100%
Develop support for middleware cors βœ… 100%
Develop logger middleware support βœ… 100%
Develop support for maxbody middlewares βœ… 100%
Develop middleware support msgid βœ… 100%
Develop middleware support msguuid βœ… 100%
Develop support Cors βœ… 100%
Develop Cient Get βœ… 100%
Develop Cient Post support βœ… 100%
Develop Cient Put support βœ… 100%
Develop Cient support Delete βœ… 100%

🚧 Roadmap in Progress

Task Progress
Develop and relate to Listen the Config ⏳ 42%
Develops support for Uploads and Uploads Multiples βœ… 100%
Develops support for JWT ⏳ 10%
Develop method to Facilitate ResponseWriter handling ⏳ 80%
Develop method to Facilitate the handling of the Request ⏳ 80%
Develop Standard of Unit Testing ⏳ 90%

πŸš€ Roadmap for Development

Task Progress
πŸ“š Documentation: Tests & Examples for Go Packages ⏳ 80%
βœ… Static Files support βœ… 100%
βœ… Support for HTTP OPTIONS method βœ… 100%
βš™οΈ Test coverage via go test -cover ⏳ 86.1%
πŸ” Regex feature coverage, dynamic routing possibilities ⏳ 80%
πŸ“¦ Template engine system (layouts, embed.FS, AddFunc) βœ… 100%
πŸ§ͺ Standard for unit testing + helper functions ⏳ 90%
πŸ” Rate Limiter middleware βœ… 100%
🧰 Middleware: Recover (panic handler) βœ… 100%
❀️ Middleware: Healthcheck βœ… 100%
πŸ›‘οΈ Middleware: Helmet (security headers) βœ… 100%
πŸ“ Middleware: MaxBody (request size limiter) βœ… 100%
πŸ“œ Middleware: Logger (request logging) βœ… 100%
πŸ” Middleware: BasicAuth βœ… 100%
🌐 Middleware: CORS βœ… 100%
🧠 Middleware: PPROF βœ… 100%
πŸ”Œ Develop support for HTTP CONNECT method RFC 9110 🟑 45%
πŸ”’ JWT Authentication support βœ… 100%
🌐 WebSocket support πŸ”΄ 0%
πŸ”’ ListenAndServeTLS method (HTTP/2) 🟑 50%
πŸ›  Create a CLI (Command Line Interface) for Quick 🟑 50%

πŸ“Š Cover Testing Roadmap

Archive Coverage Status
Ctx 🟑 84.1% 🟑
Group βœ… 100.0% 🟒
Http Status πŸ”΄ 7.8% πŸ”΄
Client 🟒 91.5% 🟒
Mock βœ… 100.0% 🟒
Concat βœ… 100.0% 🟒
Log πŸ”΄ 0.0% πŸ”΄
Print 🟑 66.7% 🟑
Qos πŸ”΄ 0.0% πŸ”΄
Rand πŸ”΄ 0.0% πŸ”΄
Compress βœ… 100.0% 🟒
Cors 🟑 79.2% 🟑
Logger 🟒 89.6% 🟒
Maxbody βœ… 100.0% 🟒
Msgid 🟒 80.0% 🟒
Msguuid 🟒 86.4% 🟒
Quick 🟒 85.3% 🟒
QuickTest βœ… 100.0% 🟒
Recover βœ… 100.0% 🟒
Pprof βœ… 100.0% 🟒
Healthcheck 🟒 83.3% 🟒
Helmet 🟒 81.2% 🟒
BasicAuth 🟑 78.9% 🟑
Template 🟑 53.3% 🟑
Limiter 🟑 71.4% 🟑
Fast quick example

When using New, you can configure global parameters such as request body limits, read/write time-out and route capacity. Below is a custom configuration:

var ConfigDefault = Config{
	BodyLimit:         4 * 1024 * 1024,
	MaxBodySize:       4 * 1024 * 1024,
	MaxHeaderBytes:    2 * 1024 * 1024,
	RouteCapacity:     500,
	MoreRequests:      500,
	ReadTimeout:       5 * time.Second,
	WriteTimeout:      5 * time.Second,
	IdleTimeout:       2 * time.Second,
	ReadHeaderTimeout: 1 * time.Second,
}

Check out the code below:

package main

import "github.com/jeffotoni/quick"
func main() {
	// Initialize a new Quick instance
    q := quick.New()

	// Define a simple GET route at the root path
    q.Get("/v1/user", func(c *quick.Ctx) error {
        c.Set("Content-Type", "application/json")
        return c.Status(200).SendString("Quick in action ❀️!")
    })

	/// Start the server on port 8080
    q.Listen("0.0.0.0:8080")
}

πŸ“Œ cURL
$ curl -i -XGET -H "Content-Type:application/json" \
'localhost:8080/v1/user'

"Quick in action ❀️!"
Quick Get Params

The example below defines a GET/v1/customer/:param1/:param2 endpoint, where :param1 and :param2 are variables on the route that can receive dynamic values.

package main

import "github.com/jeffotoni/quick"

func main() {
    // Initialize a new Quick instance
    q := quick.New()

    // Define a GET route with two dynamic parameters (:param1 and :param2)
    q.Get("/v1/customer/:param1/:param2", func(c *quick.Ctx) error {
        // Set response content type to JSON
        c.Set("Content-Type", "application/json")

        // Define a struct for the response format
        type my struct {
            Msg string `json:"msg"` 
            Key string `json:"key"` 
            Val string `json:"val"` 
        }

        // Return a JSON response with extracted parameters
        return c.Status(200).JSON(&my{
            Msg: "Quick ❀️",
            Key: c.Param("param1"), 
            Val: c.Param("param2"), 
        })
    })

	// Start the server on port 8080
    q.Listen("0.0.0.0:8080")
}

πŸ“Œ cURL
$ curl -i -XGET -H "Content-Type:application/json" \
'localhost:8080/v1/customer/val1/val2'

{
   "msg":"Quick ❀️",
   "key":"val1",
   "val":"val2"
}
Quick Post Body json

This example shows how to create a POST endpoint to receive and process a JSON request body. The code reads the data sent in the request body and converts it to a Go structure.

package main

import "github.com/jeffotoni/quick"

// Define a struct to map the expected JSON body
type My struct {
    Name string `json:"name"`
    Year int    `json:"year"`
}

func main() {
    // Initialize a new Quick instance
    q := quick.New()

    // Define a POST route to handle JSON request body
    q.Post("/v1/user", func(c *quick.Ctx) error {
        var my My

		// Parse the request body into the 'my' struct
        err := c.BodyParser(&my)
        if err != nil {
            return c.Status(400).SendString(err.Error())
        }

        // Return the received JSON data
        return c.Status(200).String(c.BodyString())
        // Alternative:
        // return c.Status(200).JSON(&my)
    })

	// Start the server on port 8080
    q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL
$ curl -i -XPOST -H "Content-Type:application/json" \
'localhost:8080/v1/user' \
-d '{"name":"jeffotoni", "year":1990}'

{
   "name":"jeffotoni",
   "year":1990
}

Uploads multipart/form-data

Quick provides a simplified API for managing uploads, allowing you to easily retrieve and manipulate files.

βœ… Main Methods and Functionalities:
Method Description
c.FormFile("file") Returns a single file uploaded in the form.
c.FormFiles("files") Returns a list of uploaded files (multiple uploads).
c.FormFileLimit("10MB") Sets an upload limit (default is 1MB).
uploadedFile.FileName() Returns the file name.
uploadedFile.Size() Returns the file size in bytes.
uploadedFile.ContentType() Returns the MIME type of the file.
uploadedFile.Bytes() Returns the bytes of the file.
uploadedFile.Save("/path/") Saves the file to a specified directory.
uploadedFile.Save("/path", "your-name-file") Saves the file with your name.
uploadedFile.SaveAll("/path") Saves the file to a specified directory.

πŸ“Œ File Upload Feature Comparison with other Frameworks
Framework FormFile() FormFiles() Dynamic Limit Methods (FileName(), Size()) Save(), SaveAll() Method
Quick βœ… Yes βœ… Yes βœ… Yes βœ… Yes βœ… Yes
Fiber βœ… Yes βœ… Yes ❌ No ❌ No (uses FileHeader directly) βœ… Yes
Gin βœ… Yes βœ… Yes ❌ No ❌ No (uses FileHeader directly) ❌ No
Echo βœ… Yes ❌ No ❌ No ❌ No ❌ No
net/http βœ… Yes ❌ No ❌ No ❌ No ❌ No

File Upload Example

This example shows how to create a file upload endpoint. It allows users to send a single file via POST to the/upload route.

package main

import (
    "fmt"
    "github.com/jeffotoni/quick"
)

// Define a struct for error messages
type Msg struct {
	Msg   string `json:"msg"`
	Error string `json:"error"`
}

func main() {
    // Initialize a new Quick instance
    q := quick.New()

	// Define a route for file upload
    q.Post("/upload", func(c *quick.Ctx) error {
        // set limit upload
        c.FormFileLimit("10MB")

		// Retrieve the uploaded file
        uploadedFile, err := c.FormFile("file")
        if err != nil {
            return c.Status(400).JSON(Msg{
                Msg: "Upload error",
                Error: err.Error(),
             })
        }

		// Print file details
        fmt.Println("Name:", uploadedFile.FileName())
        fmt.Println("Size:", uploadedFile.Size())
        fmt.Println("MIME Type:", uploadedFile.ContentType())

        // Save the file (optional)
        // uploadedFile.Save("/tmp/uploads")

		// Return JSON response with file details
		// Alternative:
        //return c.Status(200).JSONIN(uploadedFile)
		return c.Status(200).JSON(map[string]interface{}{
			"name": uploadedFile.FileName(),
			"size": uploadedFile.Size(),
			"type": uploadedFile.ContentType(),
		})
		
    })

	// Start the server on port 8080
    q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL
$ curl -i -X POST http://localhost:8080/upload -F "file=quick.txt"

{
   "name":"quick.txt",
   "size":1109,
   "type":"text/plain; charset=utf-8"
}
Multiple Upload Example

This example allows users to send multiple files via POST to the/upload-multiple route

package main

import (
	"fmt"

	"github.com/jeffotoni/quick"
)

// Define a struct for error messages
type Msg struct {
	Msg   string `json:"msg"`
	Error string `json:"error"`
}

func main() {
	// Initialize a new Quick instance
	q := quick.New()

	q.Post("/upload-multiple", func(c *quick.Ctx) error {
		// set limit upload
		c.FormFileLimit("10MB")

		// recebereceiving files
		files, err := c.FormFiles("files")
		if err != nil {
			return c.Status(400).JSON(Msg{
				Msg:   "Upload error",
				Error: err.Error(),
			})
		}

		// listing all files
		for _, file := range files {
			fmt.Println("Name:", file.FileName())
			fmt.Println("Size:", file.Size())
			fmt.Println("Type MINE:", file.ContentType())
			fmt.Println("Bytes:", file.Bytes())
		}

		// optional
		// files.SaveAll("/my-dir/uploads")

		// Alternative:
		//return c.Status(200).JSONIN(files)
		return c.Status(200).JSON("Upload successfully completed")
	})

	// Start the server on port 8080
	q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL
$ curl -X POST http://localhost:8080/upload-multiple \
-F "files=@image1.jpg" -F "files=@document.pdf"

Upload successfully completed
Quick Post Bind json
package main

import "github.com/jeffotoni/quick"

// Define a struct to map the expected JSON body
type My struct {
    Name string `json:"name"`
    Year int    `json:"year"`
}

func main() {
	// Initialize a new Quick instance
    q := quick.New()

   // Define a POST route to handle JSON request body
	q.Post("/v2/user", func(c *quick.Ctx) error {
		var my My

		// Parse the request body into the 'my' struct
		err := c.Bind(&my)
		if err != nil {
			// Return a 400 status if JSON parsing fails
			return c.Status(400).SendString(err.Error())
		}

		// Return the received JSON data
		return c.Status(200).JSON(&my)
	})

	// Start the server on port 8080
    q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL
$ curl -i -XPOST -H "Content-Type:application/json" \
'localhost:8080/v2/user' \
-d '{"name":"Marcos", "year":1990}'

{
   "name":"Marcos",
   "year":1990
}

Cors

Using the Cors middleware, making your call in the default way, which is:

var ConfigDefault = Config{
 AllowedOrigins: []string{"*"},
 AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
 AllowedHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
 ExposedHeaders: []string{"Content-Length"},
 AllowCredentials: false,
 MaxAge: 600,
 Debug: false,
}

Check out the code below:

package main

import (
    "github.com/jeffotoni/quick"
    "github.com/jeffotoni/quick/middleware/cors"
)

func main() {

   // Initialize a new Quick instance
    q := quick.New()

    // Use the CORS middleware to allow cross-origin requests
    q.Use(cors.New())

    // Define a GET route at /v1/user
    q.Get("/v1/user", func(c *quick.Ctx) error {
        // Set the response content type to JSON
        c.Set("Content-Type", "application/json")

        // Return a response with status 200 and a message
        return c.Status(200).SendString("Quick in action com Cors❀️!")
    })

	// Start the server on port 8080
    q.Listen("0.0.0.0:8080")
}

πŸ“Œ cURL
$ curl -i -XGET -H "Content-Type:application/json" \
'http://localhost:8080/v1/user'

Quick in action com Cors❀️!
Initializing Quick with Custom Configuration

This example demonstrates how to start a Quick server with a custom configuration.

package main

import "github.com/jeffotoni/quick"

func main() {
    // Create a new Quick server instance with custom configuration
    q := quick.New(quick.Config{
        MaxBodySize: 5 * 1024 * 1024, // Set max request body size to 5MB
    })

    // Define a GET route that returns a JSON response
    q.Get("/v1/user", func(c *quick.Ctx) error {
        c.Set("Content-Type", "application/json") // Set response content type
        return c.Status(200).SendString("Quick in action com Cors❀️!") // Return response
    })

	// Start the server on port 8080
    q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL
$ curl -i -XGET -H "Content-Type:application/json" \
'http://localhost:8080/v1/user'

Quick in action com Cors❀️!
Grouping Routes

This example demonstrates how to group routes using quick. Group(), making the code more organized

package main

import "github.com/jeffotoni/quick"

func main() {
     // Create a new Quick server instance with custom configuration
    q := quick.New(quick.Config{
        MaxBodySize: 5 * 1024 * 1024, 
    })

    // Group for /v1 routes
    v1 := q.Group("/v1")

    // Define GET and POST routes for /v1/user
    v1.Get("/user", func(c *quick.Ctx) error {
        return c.Status(200).SendString("[GET] [GROUP] /v1/user ok!!!")
    })
    v1.Post("/user", func(c *quick.Ctx) error {
        return c.Status(200).SendString("[POST] [GROUP] /v1/user ok!!!")
    })

 	 // Group for /v2 routes
    v2 := q.Group("/v2")

    // Define GET and POST routes for /v2/user
    v2.Get("/user", func(c *quick.Ctx) error {
        c.Set("Content-Type", "application/json")
        return c.Status(200).SendString("Quick in action com [GET] /v2/user ❀️!")
    })

    v2.Post("/user", func(c *quick.Ctx) error {
        c.Set("Content-Type", "application/json")
        return c.Status(200).SendString("Quick in action com [POST] /v2/user ❀️!")
    })

	// Start the server on port 8080
    q.Listen("0.0.0.0:8080")
}

πŸ“Œ cURL

1️⃣ GET /v1/user

$ curl -i -X GET http://localhost:8080/v1/user

[GET] [GROUP] /v1/user ok!!!

2️⃣ POST /v1/user

$ curl -i -X POST http://localhost:8080/v1/user

[POST] [GROUP] /v1/user ok!!!

3️⃣ GET /v2/user

$ curl -i -X GET http://localhost:8080/v2/user

Quick in action com [GET] /v2/user ❀️!

4️⃣ POST /v2/user

$ curl -i -X POST http://localhost:8080/v2/user

Quick in action com [POST] /v2/user ❀️!
Quick Tests

This example demonstrates how to unit test routes in Quick using QuickTest(). It simulates HTTP requests and verifies if the response matches the expected output

package main

import (
    "io"
    "strings"
    "testing"

    "github.com/jeffotoni/quick"
)

func TestQuickExample(t *testing.T) {

    // Here is a handler function Mock
    testSuccessMockHandler := func(c *quick.Ctx) error {
        c.Set("Content-Type", "application/json")
        b, _ := io.ReadAll(c.Request.Body)
        resp := `"data":` + string(b)
        return c.Byte([]byte(resp))
    }

    // Initialize Quick instance for testing
    q := quick.New()

    // Define test routes
    q.Post("/v1/user", testSuccessMockHandler)
    q.Post("/v1/user/:p1", testSuccessMockHandler)

    // Expected response data
    wantOutData := `"data":{"name":"jeff", "age":35}`
    reqBody := []byte(`{"name":"jeff", "age":35}`)
    reqHeaders := map[string]string{"Content-Type": "application/json"}

    // Perform test request
	data, err := q.Qtest(QuickTestOptions{
	Method:  MethodPost,
	URI:     "/v1/user",
	Headers: map[string]string{"Content-Type": "application/json"},
	})
	if err != nil {
		t.Errorf("Error during Qtest: %v", err)
		return
	}

    // Compare expected and actual response
    s := strings.TrimSpace(data.BodyStr())
    if s != wantOutData {
        t.Errorf("Expected %s but got %s", wantOutData, s)
        return
    }

    // Log test results
    t.Logf("\nOutputBodyString -> %v", data.BodyStr())
    t.Logf("\nStatusCode -> %d", data.StatusCode())
    t.Logf("\nOutputBody -> %v", string(data.Body()))
    t.Logf("\nResponse -> %v", data.Response())
}


Regex

This example allows access only when the ID is numeric ([0-9]+).

package main

import (
	"github.com/jeffotoni/quick"
)

func main() {
	// Initialize a new Quick instance
	q := quick.New()

	// Route that accepts only numeric IDs (using regex [0-9]+)
	q.Get("/users/{id:[0-9]+}", func(c *quick.Ctx) error {
		id := c.Param("id")
		return c.JSON(map[string]string{
			"message": "User found",
			"user_id": id,
		})
	})

	// Start the server on port 8080
	q.Listen(":8080")
}
πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/users/123

{
   "message":"User found",
   "user_id":"123"
}
Accepts only lowercase letters in the slug

This example ensures that only lowercase letters ([a-z]+) are accepted in the slug

package main

import (
	"github.com/jeffotoni/quick"
)

func main() {
	// Initialize a new Quick instance
	q := quick.New()

	// Route that accepts only lowercase slugs (words with lowercase letters)
		q.Get("/profile/{slug:[a-z]+}", func(c *quick.Ctx) error {
		slug := c.Param("slug")
		return c.JSON(map[string]string{
			"message": "Profile found",
			"profile": slug,
		})
	})

	// Start the server on port 8080
	q.Listen(":8080")
}
πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/profile/johndoe

{
   "message":"Profile found",
   "profile":"johndoe"
}
Supports API version and numeric Id
package main

import (
	"github.com/jeffotoni/quick"
)

func main() {
	// Initialize a new Quick instance
	q := quick.New()

	// Route that accepts an API version (v1, v2, etc.) and a numeric user ID
	q.Get("/api/{version:v[0-9]+}/users/{id:[0-9]+}", func(c *quick.Ctx) error {
		version := c.Param("version")
		id := c.Param("id")
		return c.JSON(map[string]string{
			"message": "API Versioned User",
			"version": version,
			"user_id": id,
		})
	})

	// Start the server on port 8080
	q.Listen(":8080")
}
πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/api/v1/users/123

{
   "message":"API Versioned User",
   "user_id":"123",
   "version":"v1"
}

πŸ”‘ Basic Authentication

Basic Authentication (Basic Auth) is a simple authentication mechanism defined in RFC 7617.
It is commonly used for HTTP-based authentication, allowing clients to provide credentials (username and password) in the request header.

πŸ”Ή How It Works

1️⃣ The client encodes the username and password in Base64:

2️⃣ The encoded credentials are sent in the Authorization header:

Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

3️⃣ The server decodes and verifies the credentials before granting access.


⚠️ Security Considerations
  • πŸ”’ Not encrypted – Basic Auth only encodes credentials in Base64, but does not encrypt them.
  • πŸ” Use over HTTPS – Always use Basic Auth with TLS/SSL (HTTPS) to prevent credentials from being exposed.
  • πŸ”‘ Consider alternatives – For stronger security, prefer OAuth2, JWT, or API keys. Basic Auth is suitable for simple use cases, but for production applications, stronger authentication mechanisms are recommended. πŸš€
Basic Auth environment variables

This example sets up Basic Authentication using environment variables to store the credentials securely. the routes below are affected, to isolate the route use group to apply only to routes in the group.

package main

import (
	"log"
	"os"

	"github.com/jeffotoni/quick"
	middleware "github.com/jeffotoni/quick/middleware/basicauth"
)

// Environment variables for authentication
// export USER=admin
// export PASSWORD=1234

var (
	// Retrieve the username and password from environment variables
	User     = os.Getenv("USER")
	Password = os.Getenv("PASSORD")
)

func main() {

	// Initialize a new Quick instance
	q := quick.New()

	// Apply Basic Authentication middleware
	q.Use(middleware.BasicAuth(User, Password))

	// Define a protected route
	q.Get("/protected", func(c *quick.Ctx) error {
		// Set the response content type to JSON
		c.Set("Content-Type", "application/json")

		// Return a success message
		return c.SendString("You have accessed a protected route!")
	})

	// Start the server on port 8080
	q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/api/v1/users/123

You have accessed a protected route!

Basic Authentication with Quick Middleware

This example uses the built-in BasicAuth middleware provided by Quick, offering a simple authentication setup.

package main

import (
	"log"

	"github.com/jeffotoni/quick"
	middleware "github.com/jeffotoni/quick/middleware/basicauth"
)

func main() {

	//starting Quick
	q := quick.New()

	// calling middleware
	q.Use(middleware.BasicAuth("admin", "1234"))

	// everything below Use will apply the middleware
	q.Get("/protected", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")
		return c.SendString("You have accessed a protected route!")
	})

	// Start the server on port 8080
	q.Listen("0.0.0.0:8080")
}

πŸ“Œ cURL
$ curl -i -X GET 'http://localhost:8080/protected' \
--header 'Authorization: Basic YWRtaW46MTIzNA=='

You have accessed a protected route!
Basic Authentication with Quick Route Groups

This example shows how to apply Basic Authentication to a specific group of routes using Quick's Group functionality. When we use group we can isolate the middleware, this works for any middleware in quick.

package main

import (
	"log"

	"github.com/jeffotoni/quick"
	middleware "github.com/jeffotoni/quick/middleware/basicauth"
)

func main() {
	//starting Quick
	q := quick.New()

	// using group to isolate routes and middlewares
	gr := q.Group("/")

	// middleware BasicAuth
	gr.Use(middleware.BasicAuth("admin", "1234"))

	// route public
	q.Get("/v1/user", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")
		return c.SendString("Public quick route")
	})

	// protected route
	gr.Get("/protected", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")
		return c.SendString("You have accessed a protected route!")
	})

	// Start the server on port 8080
	q.Listen("0.0.0.0:8080")
}

πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/v1/user

Public quick route
BasicAuth Customized

This example shows a custom implementation of Basic Authentication without using any middleware. It manually verifies user credentials and applies authentication to protected routes.

In quick you are allowed to make your own custom implementation directly in q.Use(..), that is, you will be able to implement it directly if you wish.

package main

import (
	"encoding/base64"
	"log"
	"net/http"
	"strings"

	"github.com/jeffotoni/quick"
)

func main() {
	//starting Quick
	q := quick.New()

	// implementing middleware directly in Use
	q.Use(func(next http.Handler) http.Handler {
		// credentials
		username := "admin"
		password := "1234"

		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			authHeader := r.Header.Get("Authorization")
			if authHeader == "" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}

			// Check if it starts with "Basic"
			if !strings.HasPrefix(authHeader, "Basic ") {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}

			// Decode credentials
			payload, err := base64.StdEncoding.DecodeString(authHeader[len("Basic "):])
			if err != nil {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}

			creds := strings.SplitN(string(payload), ":", 2)
			if len(creds) != 2 || creds[0] != username || creds[1] != password {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			next.ServeHTTP(w, r)
		})
	})

	q.Get("/protected", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")
		return c.SendString("You have accessed a protected route!")
	})

	// Start the server on port 8080
	q.Listen("0.0.0.0:8080")

}
πŸ“Œ cURL
$ curl -i -u admin:1234 -X GET http://localhost:8080/protected

You have accessed a protected route!

πŸ“‚ STATIC FILES

A Static File Server is an essential feature in web frameworks, enabling the efficient serving of static content such as HTML, CSS, JavaScript, images, and other assets.

It is particularly useful for:

  • βœ… Hosting front-end applications
  • βœ… Providing downloadable files
  • βœ… Serving resources directly from the backend

πŸ”Ή How It Works
  • 1️⃣ The server listens for HTTP requests targeting static file paths.
  • 2️⃣ If the requested file exists in the configured directory, the server reads and returns it as a response.
  • 3️⃣ MIME types are automatically determined based on the file extension for correct rendering.

⚑ Key Features
  • πŸš€ Efficient Handling – Serves files directly without additional processing.
  • 🎯 MIME Type Detection – Automatically identifies file types for proper rendering.
  • ⚑ Caching Support – Can be configured to improve performance via HTTP headers.
  • πŸ“‚ Optional Directory Listing – Allows browsing available static files (if enabled).

⚠️ Security Considerations
  • πŸ”’ Restrict Access – Prevent exposure of sensitive files like .env, .git, or configuration files.
  • 🌐 CORS Policies – Configure Cross-Origin Resource Sharing (CORS) to control file access.
  • πŸ›‘ Content Security Policy (CSP) – Helps mitigate XSS (Cross-Site Scripting) risks.

By properly configuring your static file server, you can ensure fast, efficient, and secure delivery of resources! πŸš€πŸ”₯


Serving Static Files with Quick Framework

This example sets up a basic web server that serves static files, such as HTML, CSS, or JavaScript.

package main

import "github.com/jeffotoni/quick"

func main() {

    //starting Quick
    q := quick.New()

    // Static Files Setup
    q.Static("/static", "./static")

    // Route Definition
    q.Get("/", func(c *quick.Ctx) error {
        c.File("./static/index.html")
        return nil
    })

    // Start the server on port 8080
    q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/

File Server Go example html

πŸ“ EMBED - Embedded Static Files
πŸ”Ή How do embedded static files work?
  1. Static assets (HTML, CSS, JS, images, etc.) are compiled directly into the binary at compile time, using the Go package embed.
  2. The application serves these files from memory, eliminating the need to access the disk.
  3. This removes external dependencies, making the deployment simpler and more efficient.

⚑ Advantages of using embedded files:
  • βœ… Portability - The binary contains everything you need, no extra files.
  • βœ… Performance - File access is faster because files are already loaded in memory.
  • βœ… Security - Reduces exposure to attacks because the file system does not need to be accessible.

πŸš€ How does Quick simplify this process?

The function q. Static() already handles the complexity of serving embedded files. Just call it with embed.FS.

The example shows how to serve static files with Quick and embed.FS

package main

import (
	"embed"

	"github.com/jeffotoni/quick"
)

//go:embed static
var staticFiles embed.FS

func main() {
	// Server Initialization
	q := quick.New()

	// Static Files Setup (serves files automatically from embed.FS)
	q.Static("/static", staticFiles)

	// Defines a route that serves the HTML index file
	q.Get("/", func(c *quick.Ctx) error {
		c.File("./static/index.html") 	
		return nil
	})

	// Start the server on port 8080
	q.Listen("0.0.0.0:8080")
}

πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/

File Server Go example html
πŸ“‚ Example Project Structure
quick-example
│── main.go
│── static/
β”‚   β”œβ”€β”€ index.html
β”‚   β”œβ”€β”€ style.css
β”‚   β”œβ”€β”€ script.js

🌍 HTTP Client

The HTTP Client package in Quick provides a simple and flexible way to make HTTP requests, supporting GET, POST, PUT, and DELETE operations. πŸš€

It is designed to handle different types of request bodies and parse responses easily.

🎯 Why Use Quick's HTTP Client?
  • βœ… Easy-to-Use – Simplified functions for common HTTP requests.
  • βœ… Highly Customizable – Supports headers, authentication, and transport settings.
  • βœ… Flexible Body Parsing – Works with JSON, plain text, and custom io.Reader types.
  • βœ… Automatic JSON Handling – No need to manually marshal/unmarshal JSON.
⚑ Key Features
  • πŸ”Ή Convenience Functions – Use Get, Post, Put, and Delete to make quick requests with a default client.
  • πŸ”Ή Customizable Requests – Easily add headers, authentication, and request settings.
  • πŸ”Ή Automatic JSON Processing – Seamless encoding and decoding of JSON data.
  • πŸ”Ή Flexible Request Body – Send data as JSON, plain text, or any io.Reader.

πŸ“Œ Client Structure

The Client struct represents a configurable HTTP client with advanced features:

var ClientDefault = Client{
	Ctx:          context.Background(),
	ClientHTTP:   httpGoClient{},
	Headers:      map[string]string{"Content-Type": "application/json"},
	EnableLogger: true,
	Logger:       slog.Default(),
}

Check out the code below:

GET Request Example

A GET request is used to retrieve data from a server.

package main

import (
	"fmt"
	"log"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Create a new HTTP client
	httpClient := client.New()

	// Making a GET request to fetch a list of users
	resp, err := httpClient.Get("https://reqres.in/api/users")
	if err != nil {
		log.Fatal(err)
	}

	// Alternative:
	//fmt.Println("GET response:", string(resp.Body))

	// Parse JSON response
	var result map[string]interface{}
	if err := json.Unmarshal(resp.Body, &result); err != nil {
		log.Fatal("Error decoding response:", err)
	}

	// Extract first user
	users := result["data"].([]interface{})
	firstUser := users[0].(map[string]interface{})

	// Print only first user
	fmt.Printf("Id: %v\n", firstUser["id"])
	fmt.Printf("Name: %v %v\n", firstUser["first_name"], firstUser["last_name"])
	fmt.Printf("Email: %v\n", firstUser["email"])
	fmt.Printf("Avatar: %v\n", firstUser["avatar"])
}
πŸ“Œ Response
Id: 1
Name: George Bluth
Email: george.bluth@reqres.in
Avatar: https://reqres.in/img/faces/1-image.jpg
POST Request Example (Using a Struct)

A POST request is used to send data to a server, often for creating new resources.

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Create a new HTTP client
	httpClient := client.New()

	// Define a struct for the request body
	data := struct {
		Name string `json:"name"`
	}{
		Name: "Emma",
	}

	// Making a POST request with JSON data
	resp, err := httpClient.Post("https://reqres.in/api/users", data)
	if err != nil {
		log.Fatal("Error making POST request:", err)
	}

	// Parse JSON response
	var result map[string]interface{}
	if err := json.Unmarshal(resp.Body, &result); err != nil {
		log.Fatal("Error decoding response:", err)
	}

	// Alternative:
	//fmt.Println("POST response:", result)

	// Print formatted response
	fmt.Println("Id:", result["id"])
	fmt.Println("Created_At:", result["createdAt"])
}
πŸ“Œ Response
Id: 322
Created_At: 2025-03-14T14:48:24.305Z
PUT Request Example (Using a String)

A PUT request is used to update an existing resource.

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Create a new HTTP client
	httpClient := client.New()

	// Define a struct with updated user data
	data := struct {
		Name string `json:"name"`
	}{
		Name: "Jeff",
	}

	// PUT request to ReqRes API
	resp, err := httpClient.Put("https://reqres.in/api/users/2", data)
	if err != nil {
		log.Fatal("Error making PUT request:", err)
	}

	// Parse JSON response
	var result map[string]interface{}
	if err := json.Unmarshal(resp.Body, &result); err != nil {
		log.Fatal("Error decoding response:", err)
	}

	// Alternative: Print the HTTP status and response body
	// fmt.Println("HTTP Status Code:", resp.StatusCode)
	// fmt.Println("Raw Response Body:", string(resp.Body))

	// Print formatted response
	fmt.Println("Updated_At:", result["updatedAt"])
}
πŸ“Œ Response
Updated_At: 2025-03-14T14:56:35.202Z
DELETE Request Example

A DELETE request is used to remove a resource from the server.

package main

import (
	"fmt"
	"log"

	"github.com/jeffotoni/quick/http/client"
)

func main() {

	// Create a new HTTP client
	httpClient := client.New()

	// DELETE request to ReqRes API
	resp, err := httpClient.Delete("https://reqres.in/api/users/2")
	if err != nil {
		log.Fatal("Error making request:", err)
	}

	// Print the HTTP status to confirm deletion
	fmt.Println("Status Code:", resp.StatusCode)

	// Since DELETE usually returns no content, we check if it's empty
	if len(resp.Body) > 0 {
		fmt.Println("Raw Response Body:", string(resp.Body))
	} else {
		fmt.Println("Response Body is empty (expected for 204 No Content)")
	}
}
πŸ“Œ Response
Status Code: 204
Response Body is empty (expected for 204 No Content)

πŸš€ Qtest - HTTP Testing Utility for Quick

Qtest is an advanced HTTP testing function designed to simplify route validation within the Quick framework. It enables seamless testing of simulated HTTP requests using httptest, supporting:

  • Custom HTTP methods (GET, POST, PUT, DELETE, etc.).
  • βœ… Custom headers.
  • βœ… Query parameters.
  • βœ… Request body.
  • βœ… Cookies.
  • βœ… Built-in validation methods for status codes, headers, and response bodies.

πŸ“Œ Overview

The Qtest function takes a QuickTestOptions struct containing request parameters, executes the request, and returns a QtestReturn object, which provides methods for analyzing and validating the result.

func TestQTest_Options_POST(t *testing.T) {
    // start Quick
    q := New()

    // Define the POST route
    q.Post("/v1/user/api", func(c *Ctx) error {
        c.Set("Content-Type", "application/json") // Simplified header setting
        return c.Status(StatusOK).String(`{"message":"Success"}`)
    })

    // Configure test parameters
    opts := QuickTestOptions{
        Method: "POST",
        URI:    "/v1/user/api",
        QueryParams: map[string]string{
            "param1": "value1",
            "param2": "value2",
        },
        Body: []byte(`{"key":"value"}`),
        Headers: map[string]string{
            "Content-Type": "application/json",
        },
        Cookies: []*http.Cookie{
            {Name: "session", Value: "abc123"},
        },
        LogDetails: true, // Enables detailed logging
    }

    // Execute test
    result, err := q.Qtest(opts)
    if err != nil {
        t.Fatalf("Error in Qtest: %v", err)
    }

    // Validations
    if err := result.AssertStatus(StatusOK); err != nil {
        t.Errorf("Status assertion failed: %v", err)
    }

    if err := result.AssertHeader("Content-Type", "application/json"); err != nil {
        t.Errorf("Header assertion failed: %v", err)
    }

    if err := result.AssertBodyContains("Success"); err != nil {
        t.Errorf("Body assertion failed: %v", err)
    }
}
πŸ“Œ Usage Reference
Function Description
Qtest(opts QuickTestOptions) Executes an HTTP test request
AssertStatus(expected int) Asserts expected HTTP status code
AssertHeader(key, value string) Checks response header value
AssertBodyContains(substr string) Verifies if body contains a string
πŸ“– More Details

πŸ”— Check out the full documentation: Qtest - Quick


πŸ”„ Retry & Failover Mechanisms in Quick HTTP Client

The Quick HTTP Client now includes built-in retry and failover support, allowing for more resilient and reliable HTTP requests. These features are essential for handling transient failures, network instability, and service downtime efficiently.

πŸš€ Key Features
  • πŸ”„ Automatic Retries: Retries failed requests based on configurable rules.
  • ⏳ Exponential Backoff: Gradually increases the delay between retry attempts.
  • πŸ“‘ Status-Based Retries: Retries only on specified HTTP status codes (e.g., 500, 502, 503).
  • 🌍 Failover Mechanism: Switches to predefined backup URLs if the primary request fails.
  • πŸ“‘ Logging Support: Enables detailed logs for debugging retry behavior.

πŸ”Ή How Retry & Failover Work

The retry mechanism automatically resends requests when they fail, with configurable options to:

  • Limit the number of retries to avoid excessive attempts.
  • Introduce backoff delays to prevent overwhelming the server.
  • Retry only on specific HTTP status codes (e.g., 500, 502, 503).

The failover system ensures high availability by redirecting failed requests to predefined backup URLs, reducing downtime and improving system resilience.

βš™οΈ Configuration Options

These options allow fine-grained control over retry and failover behavior:

Option Description πŸš€
MaxRetries Sets the maximum number of retry attempts before failure.
Delay Defines the initial delay before retrying a request.
UseBackoff Enables exponential backoff, increasing delay dynamically after each retry.
Statuses List of HTTP status codes (e.g., 500, 502, 503) that trigger a retry.
FailoverURLs List of backup URLs used if the primary request repeatedly fails.
EnableLog Enables detailed logging for debugging retry behavior.

βš™οΈ Configuring Retries & Failover in Quick HTTP Client

The Quick HTTP Client provides built-in support for retrying failed requests and switching to failover URLs when necessary.

You can configure these behaviors using the WithRetry option, which accepts a RetryConfig struct.

πŸ›  Creating a Custom Client with Retries

The following example shows how to create a Quick client with retry and failover mechanisms.

// Creating a Quick client using a custom HTTP client and retry settings.
cClient := client.New(
    client.WithCustomHTTPClient(customHTTPClient), 
    client.WithContext(context.Background()),      
 	client.WithHeaders(map[string]string{
			"Content-Type": "application/json", 
		}),
    client.WithRetry(client.RetryConfig{
        MaxRetries:   3,                      
        Delay:        2 * time.Second,        
        UseBackoff:   true,                
        Statuses:     []int{500, 502, 503, 504}, 
        FailoverURLs: []string{"http://hosterror", "https://httpbin.org/post"},
        EnableLog:    true,                  
    }),
)
πŸ”„ Smart Retries: Exponential Backoff & Failover to Backup URLs

This example demonstrates retrying a request with an increasing delay (backoff) when encountering errors.

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Create a new Quick HTTP client with retry settings
	cClient := client.New(
		client.WithRetry(
			client.RetryConfig{
				MaxRetries: 3,       
				Delay:      1 * time.Second,  
				UseBackoff: true,   
				Statuses:   []int{500, 502, 503}, 
				FailoverURLs: []string{"http://backup1.com/resource", "https://httpbin_error.org/get", "https://httpbin.org/get"},
				EnableLog:  true,   
			}),
	)

	// Send a GET request to the specified URL
	resp, err := cClient.Get("https://httpbin_error.org/get")
	if err != nil {
		log.Fatal("GET request failed:", err)
	}

	// Print the response body
	fmt.Println("GET Response:", string(resp.Body))
}
πŸ“Œ Response
{"time":"2025-03-14T14:27:02.069237664-03:00","level":"WARN","msg":"Retrying request","url":"https://httpbin_error.org/get","method":"GET","attempt":1,"failover":1}
{"time":"2025-03-14T14:27:13.076907091-03:00","level":"WARN","msg":"Retrying request","url":"http://backup1.com/resource","method":"GET","attempt":2,"failover":2}
{"time":"2025-03-14T14:27:15.258544931-03:00","level":"WARN","msg":"Retrying request","url":"https://httpbin_error.org/get","method":"GET","attempt":3,"failover":3}
GET Response: {
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Host": "httpbin_error.org", 
    "User-Agent": "Go-http-client/1.1", 
    "X-Amzn-Trace-Id": "Root=1-67d466f8-1aafed0512167ac32426bc9f"
  }, 
  "origin": "179.216.110.129", 
  "url": "https://httpbin_error.org/get"
}
πŸ”₯ How Each Feature Works in the Code
πŸ”„ Retry with Exponential Backoff
  • The retry mechanism is triggered because MaxRetries: 3 allows the request to be retried up to three times.
  • The wait time between attempts automatically increases due to UseBackoff: true.
  • A retry only occurs if the response contains an HTTP error listed in Statuses: []int{500, 502, 503}.
🌍 Failover to Backup URLs
  • If all retry attempts on the primary URL fail, the client will try the alternative URLs listed in FailoverURLs.
  • In this example, if https://httpbin.org/status/500 keeps failing, it will switch to https://httpbin.org/get.

πŸ“ Form Submission with PostForm in Quick HTTP Client

The Quick HTTP Client now supports PostForm, making it easier to send form-encoded data (application/x-www-form-urlencoded).
This feature is particularly useful for:

βœ… Authentication requests
βœ… Submitting data to web services
βœ… Integrations with legacy systems that do not accept JSON


πŸ”Ή Why Use PostForm?

πŸš€ Feature πŸ’‘ Benefit
πŸ“‘ Optimized for Forms Makes it easy to send form-encoded data (application/x-www-form-urlencoded).
βš™οΈ Automatic Encoding Converts url.Values into a valid form submission format.
πŸ“Œ Header Management Automatically sets Content-Type: application/x-www-form-urlencoded.
πŸ”„ Consistent API Follows the same design as Post, Get, Put, ensuring ease of use.
🌍 Better Compatibility Works seamlessly with APIs that do not accept JSON payloads.

πŸ”Ή How PostForm Works

The PostForm method encodes form parameters, adds necessary headers, and sends an HTTP POST request to the specified URL. It is specifically designed for APIs and web services that do not accept JSON payloads but require form-encoded data.

πŸ”Ή Quick Server with Form Submission

The following example demonstrates how to send form-encoded data using Quick PostForm:

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"time"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Initialize Quick framework
	q := quick.New()

	// Define a POST route to handle form data submission
	q.Post("/postform", func(c *quick.Ctx) error {
		// Retrieve form values from the request
		form := c.FormValues()

		// Return the received form data as JSON response
		return c.JSON(map[string]any{
			"message": "Received form data",
			"data":    form,
		})
	})

	// Start the Quick server in a separate goroutine
	go func() {
		fmt.Println("Quick server running at http://localhost:3000")
		if err := q.Listen(":3000"); err != nil {
			log.Fatalf("Failed to start Quick server: %v", err)
		}
	}()

	time.Sleep(2 * time.Second)

	// Create an HTTP client before calling PostForm
	cClient := client.New(
		client.WithTimeout(5*time.Second),
		client.WithHeaders(map[string]string{
			"Content-Type": "application/x-www-form-urlencoded",
		}),
	)

	// Declare form data (key-value pairs)
	formData := url.Values{}
	formData.Set("username", "quick_user")
	formData.Set("password", "supersecret")

	// Send a POST request with form data
	resp, err := cClient.PostForm("http://localhost:3000/postform", formData)
	if err != nil {
		log.Fatalf("PostForm request failed: %v", err)
	}

	// Unmarshal the JSON response from the server
	var result map[string]any
	if err := json.Unmarshal(resp.Body, &result); err != nil {
		log.Fatal("Failed to parse JSON response:", err)
	}

	// Print the formatted JSON response
	// Alternative:fmt.Println("POST Response:", result)

	// Print the formatted JSON response
	formattedResponse, err := json.MarshalIndent(result, "", "  ")
	if err != nil {
		log.Fatal("Failed to format JSON response:", err)
	}

	fmt.Println("POST Response:")
	fmt.Println(string(formattedResponse))

}
πŸ“Œ Response
POST Response:
{
  "data": {
    "password": [
      "supersecret"
    ],
    "username": [
      "quick_user"
    ]
  },
  "message": "Received form data"
}

🌐 Transport Configuration in HTTP Client

The Transport setting in the Quick HTTP Client manages the network layer, ensuring efficient, secure, and reliable HTTP communications. It provides fine-grained control over connection management, security settings, and protocol optimizations for both development and production environments.

βœ… Key Features of Transport Configuration
βš™οΈ Setting πŸ” Description
πŸ›  Proxy Settings Handles proxy servers using system environment settings for automatic configuration.
πŸ”’ TLS Configuration Controls security settings, such as TLS version and certificate verification. InsecureSkipVerify can be enabled for development to bypass SSL verification.
πŸ“‘ Connection Management Optimizes resource usage with settings like MaxIdleConns, MaxConnsPerHost, and MaxIdleConnsPerHost, improving scalability.
πŸš€ Persistent Connections Enables or disables Keep-Alives, reducing connection setup time and improving performance.
⚑ HTTP/2 Support Enables HTTP/2 for faster, more efficient communication when supported by the server.
Advanced HTTP client configuration with failover mechanism

This code example showcases the setup of an HTTP client capable of handling network interruptions and server failures gracefully. It features custom transport configurations, including enhanced security settings, connection management, and a robust failover mechanism. Such a setup ensures that the application remains resilient and responsive under various network conditions.

package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/jeffotoni/quick/http/client"
)

func main() {

	customTransport := &http.Transport{
		// Uses system proxy settings if available.
		Proxy: http.ProxyFromEnvironment,
		TLSClientConfig: &tls.Config{
			// Allows insecure TLS connections .
			InsecureSkipVerify: true,
			// Enforces a minimum TLS version for security.
			MinVersion:         tls.VersionTLS12,
		},
		 // Maximum number of idle connections across all hosts.
		MaxIdleConns:        50,
		// Maximum simultaneous connections per host.
		MaxConnsPerHost:     30,
		// Maximum number of idle connections per host.
		MaxIdleConnsPerHost: 10,
		// Enables persistent connections (Keep-Alive).
		DisableKeepAlives:   false,
	}

	// Creating a fully custom *http.Client with the transport and timeout settings.
	customHTTPClient := &http.Client{
		// Sets a global timeout for all requests.
		Timeout: 5 * time.Second,
	}

	// Creating a client using both the custom transport and other configurations.
	cClient := client.New(
		// Applying the custom HTTP client.
		client.WithCustomHTTPClient(customHTTPClient),
		// Custom context for request cancellation and deadlines.
		client.WithContext(context.Background()),
		client.WithHeaders(map[string]string{
			"Content-Type":  "application/json",
			"Authorization": "Bearer YOUR_ACCESS_TOKEN",
		}),
		// Applying the custom transport.
		client.WithTransport(customTransport),
		// Setting a timeout for requests.
		client.WithTimeout(5*time.Second),
		// Retry on specific status codes.
		client.WithRetry(
			client.RetryConfig{
				MaxRetries:   2,
				Delay:        1 * time.Second,
				UseBackoff:   true,
				Statuses:     []int{500},
				FailoverURLs: []string{"http://hosterror", "https://httpbin.org/post"},
				EnableLog:    true,
			}),
	)

	// call client to POST
	resp, err := cClient.Post("https://httpbin_error.org/post", map[string]string{"message": "Quick in action"})
	if err != nil {
		log.Fatal(err)
	}

	// show resp
	fmt.Println("POST response:\n", string(resp.Body))
}
πŸ“Œ Response
{"time":"2025-03-14T15:31:11.027180616-03:00","level":"WARN","msg":"Retrying request","url":"https://httpbin_error.org/post","method":"POST","attempt":1,"failover":1}
{"time":"2025-03-14T15:31:12.028294877-03:00","level":"WARN","msg":"Retrying request","url":"http://hosterror","method":"POST","attempt":2,"failover":2}
POST response:
 {
  "args": {}, 
  "data": "{\"message\":\"Quick in action\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Authorization": "Bearer YOUR_ACCESS_TOKEN", 
    "Content-Length": "29", 
    "Content-Type": "application/json", 
    "Host": "httpbin_error.org", 
    "User-Agent": "Go-http-client/1.1", 
    "X-Amzn-Trace-Id": "Root=1-67d475f2-713b9e4c2fff65d413fcd097"
  }, 
  "json": {
    "message": "Quick in action"
  }, 
  "origin": "179.216.110.129", 
  "url": "https://httpbin_error.org/post"
}

HTTP Client with Advanced Transport and Failover Capabilities

Explore how to set up an HTTP client that not only adheres to security best practices with TLS configurations but also ensures your application remains operational through network issues. This example includes detailed setups for handling HTTP client retries and switching to failover URLs when typical requests fail. Ideal for systems requiring high reliability and fault tolerance.

package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/jeffotoni/quick/http/client"
)

func main() {

	// Creating a custom HTTP transport with advanced settings.
	customTransport := &http.Transport{
		// Uses system proxy settings if available.
		Proxy: http.ProxyFromEnvironment,
		TLSClientConfig: &tls.Config{
			 // Allows insecure TLS connections (not recommended for production).
			InsecureSkipVerify: true,
			// Enforces a minimum TLS version for security.
			MinVersion:         tls.VersionTLS12,
		},
		// Maximum number of idle connections across all hosts.
		MaxIdleConns:        50,
		// Maximum simultaneous connections per host.
		MaxConnsPerHost:     30,
		 // Maximum number of idle connections per host.
		MaxIdleConnsPerHost: 10,
		// Enables persistent connections (Keep-Alive).
		DisableKeepAlives:   false,
	}

	// Creating a fully custom *http.Client with the transport and timeout settings.
	customHTTPClient := &http.Client{
		 // Sets a global timeout for all requests.
		Timeout:   5 * time.Second,
		// Uses the custom transport.
		Transport: customTransport,
	}

	// Creating a client using both the custom transport and other configurations.
	cClient := client.New(
		// Applying the custom HTTP client.
		client.WithCustomHTTPClient(customHTTPClient),
		 // Custom context for request cancellation and deadlines.
		client.WithContext(context.Background()),
		client.WithHeaders(map[string]string{
			"Content-Type":  "application/json",
			"Authorization": "Bearer YOUR_ACCESS_TOKEN",
		}),
		client.WithTimeout(5*time.Second), // Setting a timeout for requests.
		// Retry on specific status codes.
		client.WithRetry(
			client.RetryConfig{
				MaxRetries:   2,
				Delay:        1 * time.Second,
				UseBackoff:   true,
				Statuses:     []int{500},
				FailoverURLs: []string{"http://hosterror", "https://httpbin.org/post"},
				EnableLog:    true,
			}),
	)

	resp, err := cClient.Post("https://httpbin_error.org/post", map[string]string{"name": "jeffotoni"})
	if err != nil {
		log.Fatalf("POST request failed: %v", err)
	}

	// show resp
	fmt.Println("POST response:", string(resp.Body))
}
πŸ“Œ Response
{"time":"2025-03-14T15:37:43.481220287-03:00","level":"WARN","msg":"Retrying request","url":"https://httpbin_error.org/post","method":"POST","attempt":1,"failover":1}
{"time":"2025-03-14T15:37:44.482388761-03:00","level":"WARN","msg":"Retrying request","url":"http://hosterror","method":"POST","attempt":2,"failover":2}
POST response: {
  "args": {}, 
  "data": "{\"name\":\"jeffotoni\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Authorization": "Bearer YOUR_ACCESS_TOKEN", 
    "Content-Length": "20", 
    "Content-Type": "application/json", 
    "Host": "httpbin_error.org", 
    "User-Agent": "Go-http-client/1.1", 
    "X-Amzn-Trace-Id": "Root=1-67d4777b-50d494284d3d242224dc62c0"
  }, 
  "json": {
    "name": "jeffotoni"
  }, 
  "origin": "179.216.110.129", 
  "url": "https://httpbin_error.org/post"
}

Configuring HTTP Client with Retry and Failover Mechanisms

Discover how to build an HTTP client capable of dealing with network instabilities and server failures. This setup includes detailed retry configurations and introduces failover URLs to ensure that your application can maintain communication under adverse conditions. The example demonstrates using exponential backoff for retries and provides multiple endpoints to guarantee the availability of services.

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Create a new HTTP client with specific configurations.
	cClient := client.New(
		// Set a timeout for all requests made by this client to 10 seconds.
		// This helps prevent the client from hanging indefinitely on requests.
		client.WithTimeout(10*time.Second),

		// Set default headers for all requests made by this client.
		// Here, 'Content-Type' is set to 'application/json'
		//  which is typical for API calls.
		client.WithHeaders(map[string]string{
			"Content-Type": "application/json",
		}),

		// Enable automatic retry mechanism with specific configurations.
		// This is useful for handling intermittent errors and ensuring robustness.
		client.WithRetry(
			client.RetryConfig{
				 // Retry failed requests up to two times.
				MaxRetries: 2,
				// Wait for 1 second before retrying.
				Delay:      1 * time.Second,
				 // Use exponential backoff strategy for retries.
				UseBackoff: true,
				// HTTP status codes that trigger a retry.
				Statuses:   []int{500, 502, 503},
				// Alternate URLs to try if the main request fails.
				FailoverURLs: []string{
					"http://hosterror",
					"https://httpbin.org/post",
				},
				// Enable logging for retry operations.
				EnableLog: true,
			}),
	)

	// Perform a POST request using the configured HTTP client.
	// Includes a JSON payload with a "name" key.
	resp, err := cClient.Post("https://httpbin_error.org/post", map[string]string{
		"name": "jeffotoni in action with Quick!!!",
	})

	// Check if there was an error with the POST request.
	if err != nil {
		// If an error occurs, log the error and terminate the program.
		log.Fatalf("POST request failed: %v", err)
	}

	// Print the response from the server to the console.
	fmt.Println("POST Form Response:", string(resp.Body))
}

πŸ“Œ Response
{"time":"2025-03-14T15:40:30.617507958-03:00","level":"WARN","msg":"Retrying request","url":"https://httpbin_error.org/post","method":"POST","attempt":1,"failover":1}
{"time":"2025-03-14T15:40:31.618144855-03:00","level":"WARN","msg":"Retrying request","url":"http://hosterror","method":"POST","attempt":2,"failover":2}
POST Form Response: {
  "args": {}, 
  "data": "{\"name\":\"jeffotoni in action with Quick!!!\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Length": "44", 
    "Content-Type": "application/json", 
    "Host": "httpbin_error.org", 
    "User-Agent": "Go-http-client/1.1", 
    "X-Amzn-Trace-Id": "Root=1-67d47822-5c80648f5a30c75c6a500470"
  }, 
  "json": {
    "name": "jeffotoni in action with Quick!!!"
  }, 
  "origin": "179.216.110.129", 
  "url": "https://httpbin_error.org/post"
}

Advanced HTTP Client Configuration with Transport and Retry Settings

Explore the configuration of an HTTP client designed for high reliability and security in network communications. This example includes sophisticated transport settings, featuring TLS configurations for enhanced security, and a robust retry mechanism to handle request failures gracefully. These settings are essential for applications requiring reliable data exchange with external APIs, especially in environments where network stability might be a concern.

package main

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Create an HTTP client with custom configurations using the Quick framework.
	cClient := client.New(
		// Set a global timeout for all requests made by this client to 10 seconds.
		// This helps prevent the client from hanging indefinitely on requests.
		client.WithTimeout(10*time.Second),

		// Set default headers for all requests made by this client.
		// Here, we specify that we expect to send and receive JSON data.
		client.WithHeaders(map[string]string{"Content-Type": "application/json"}),

		// Configure the underlying transport for the HTTP client.
		client.WithTransportConfig(&http.Transport{
			// Use the system environment settings for proxy configuration.
			Proxy: http.ProxyFromEnvironment,

			// Configure TLS settings to skip verification of the server's
			// certificate chain and hostname.
			// Warning: Setting InsecureSkipVerify to true is not recommended for
			//  production as it is insecure.
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},

			// Enable HTTP/2 for supported servers.
			ForceAttemptHTTP2: true,

			// Set the maximum number of idle connections in the connection pool for all hosts.
			MaxIdleConns: 20,

			// Set the maximum number of idle connections in the connection pool per host.
			MaxIdleConnsPerHost: 10,

			// Set the maximum number of simultaneous connections per host.
			MaxConnsPerHost: 20,

			// Keep connections alive between requests. This can help improve performance.
			DisableKeepAlives: false,
		}),
	)

	// Perform a POST request with a JSON payload.
	// The payload includes a single field "name" with a value.
	resp, err := cClient.Post("https://httpbin.org/post", map[string]string{"name": "jeffotoni"})
	if err != nil {
		// Log the error and stop the program if the POST request fails.
		log.Fatalf("POST request failed: %v", err)
	}

	// Output the response from the POST request.
	fmt.Println("POST Form Response:", string(resp.Body))
}

πŸ“Œ Response
POST Form Response: {
  "args": {}, 
  "data": "{\"name\":\"jeffotoni\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Length": "20", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "Go-http-client/2.0", 
    "X-Amzn-Trace-Id": "Root=1-67d4786f-61ddc079287866e673f4f584"
  }, 
  "json": {
    "name": "jeffotoni"
  }, 
  "origin": "179.216.110.129", 
  "url": "https://httpbin.org/post"
}

πŸ” TLS (Transport Layer Security) in Quick HTTP Server

TLS (Transport Layer Security) is a cryptographic protocol that provides secure communication over a network. It is widely used to encrypt data transmitted between clients and servers, ensuring confidentiality, integrity, and authentication. TLS is the successor to SSL (Secure Sockets Layer) and is used in HTTPS, email security, and many other applications.


πŸš€ Key TLS Features
πŸ”Ή Feature πŸ” Description
πŸ” Encryption Protects data from being intercepted during transmission.
πŸ”‘ Authentication Ensures the server (and optionally the client) is legitimate.
πŸ”„ Data Integrity Prevents data from being modified or tampered with in transit.
⚑ Performance Modern TLS versions (1.2, 1.3) offer strong security with minimal overhead.

🌍 Running a Secure HTTPS Server with Quick and TLS

This example demonstrates how to set up an HTTPS server using Quick with TLS encryption, ensuring secure communication between clients and the server.

package main

import (
	"fmt"

	"github.com/jeffotoni/quick"
)

func main() {
	// Initialize Quick instance
	q := quick.New()

	// Print a message indicating that the server is starting on port 8443
	fmt.Println("Run Server port:8443")

	// Start the HTTPS server with TLS encryption
	// - The server will listen on port 8443 (non-privileged port)
	// - cert.pem: SSL/TLS certificate file
	// - key.pem: Private key file for SSL/TLS encryption
	err := q.ListenTLS(":8443", "cert.pem", "key.pem", false)
	if err != nil {
		// Log an error message if the server fails to start
		fmt.Printf("Error when trying to connect with TLS: %v\n", err)
	}
}

⚠️Ports & Permissions Considerations

This example uses port 8443 so that it runs on any operating system without requiring extra permissions.

However, in production, you may want to use the standard HTTPS port 443.

  • Port 443 (default for HTTPS) is a privileged port (below 1024).
  • On Linux, running a service on port 443 requires superuser privileges.

To run on port 443 on Linux, use:

$ sudo go run main.go

🚦 Rate Limiter - Request Limiting Middleware

The Rate Limiter is a middleware for the Quick framework that controls the number of requests allowed in a given time period. It helps prevent API abuse and improves system stability by preventing server overload.

πŸš€ Features
Feature Description
🎯 Request Rate Limiting Configurable maximum number of requests per client within a time window.
⏳ Automatic Expiration Resets the request counter automatically after the configured time.
πŸ”‘ Custom Client Identification Uses a KeyGenerator function to define a unique client key (e.g., IP-based).
⚠️ Custom Response on Limit Exceeded Allows defining a custom response when the request limit is reached.
⚑ Efficient Performance Implements sharding and optimizations to reduce concurrency issues.
🌍 Global Rate Limiter

The example below shows how to apply the Rate Limiter as global middleware.

package main

import (
	"time"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/limiter"
)

func main() {
	q := quick.New()

	// Apply the rate limiter middleware
	q.Use(limiter.New(limiter.Config{
		// Maximum 10 requests allowed per IP
		Max: 10,
		// The limit resets after 5 seconds
		Expiration: 5 * time.Second,
		KeyGenerator: func(c *quick.Ctx) string {
			// Uses the client's IP address as the key
			return c.RemoteIP()
		},
		LimitReached: func(c *quick.Ctx) error {
			c.Set("Content-Type", "application/json")
			// The client should wait 10 seconds before retrying
			c.Set("Retry-After", "10")
			return c.Status(quick.StatusTooManyRequests).JSON(map[string]string{
				"error":   "Too many requests",
				"message": "You have exceeded the request limit. 
				Please wait 1 second and try again.",
				"retry_after": "10s",
			})
		},
	}))

	// Define a simple GET route
	q.Get("/", func(c *quick.Ctx) error {
		return c.Status(200).JSON(map[string]string{"msg": "Quick in action ❀️!"})
	})

	// Start the server on port 8080
	q.Listen(":8080")
}


πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/

If the same IP makes more than 10 requests in 5 seconds, the middleware returns:

{
    "error": "Too many requests",
    "message": "You have exceeded the request limit. 
	 Please wait 1 second and try again.",
    "retry_after": "10s"
}
🌐 Using Rate Limiter for Specific Route Groups

The example below shows how to apply the Rate Limiter with route group.

package main

import (
	"log"
	"time"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/limiter"
)

func main() {
	// Create a new Quick instance
	q := quick.New()

	// Rate Limiter Middleware
	limiterMiddleware := limiter.New(limiter.Config{
		// Maximum 3 requests allowed per IP address within a 10-second window
		Max: 3,
		// The limit resets every 10 seconds
		Expiration: 10 * time.Second,
		// Use the client's IP address as the unique key to track rate limits
		KeyGenerator: func(c *quick.Ctx) string {
			return c.RemoteIP()
		},
		// If the rate limit is exceeded, send an error message and instructions
		LimitReached: func(c *quick.Ctx) error {
			// Set content type to JSON
			c.Set("Content-Type", "application/json")
			c.Set("Retry-After", "10") 
			// Response structure
			response := map[string]string{
				"error":       "Too many requests",
				"message":     "You have exceeded the request limit. 
				Please wait 10 seconds and try again.",
				"retry_after": "10s",
			}

			// Log to verify that the rate limit exceeded response is being sent
			log.Println("Rate Limit Exceeded:", response)

			// Return the response with HTTP status 429 (Too Many Requests)
			return c.Status(quick.StatusTooManyRequests).JSON(response)
		},
	})

	// Create an API group with rate limit middleware
	api := q.Group("/v1")
	// Apply the rate limiter middleware to the /api group
	api.Use(limiterMiddleware)

	// Define route /api/users that responds with a list of users
	api.Get("/users", func(c *quick.Ctx) error {
		return c.JSON(map[string]string{"msg": "list of users"})
	})

	// Define route /api/posts that responds with a list of posts
	api.Get("/posts", func(c *quick.Ctx) error {
		return c.JSON(map[string]string{"msg": "list of posts"})
	})

	// Define route without rate limit
	q.Get("/", func(c *quick.Ctx) error {
		return c.JSON(map[string]string{"msg": "Quick in action ❀️!"})
	})

	// Start the server on port 8080
	q.Listen(":8080")
}
πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/users

If an IP makes more than 3 requests within 10 seconds, the response is blocked and returns a 429 Too Many Requests error:

{
    "error": "Too many requests",
    "message": "You have exceeded the request limit. 
	Please wait 10 seconds and try again.",    "retry_after": "10s"
}


πŸš€ Benchmarks

Benchmarking is a performance evaluation technique that measures response time, resource usage, and processing capacity. It helps developers identify bottlenecks and optimize their code. This approach is widely used in various areas, including software testing and hardware evaluations.

πŸ“Š Why Benchmarking?
βœ… Benefit πŸ” Description
πŸ“ Measure performance Evaluates how a system responds under different workloads.
πŸ”„ Compare technologies Allows you to analyze different frameworks, libraries or implementations.
πŸ” Identify bottlenecks Helps detect critical points that need optimization.
πŸ“ˆ Ensure scalability Test system behavior with multiple simultaneous requests.
🎭 Simulate real-world scenarios Reproduces heavy use situations, such as thousands of users accessing a service at the same time.
πŸ› οΈ Load Testing with Quick and k6

To evaluate the performance of our API, we conducted a benchmark test using the Quick framework along with k6 for load testing.

πŸ”Ή API Code for Benchmarking

The following Go API was used for benchmarking. It provides a POST endpoint at /v1/user, which:

  • Accepts large JSON payloads.
  • Parses the incoming JSON into a Go struct.
  • Returns the parsed JSON as a response.
package main

import (
	"github.com/jeffotoni/quick"
)

// Struct representing a user model
type My struct {
	ID       string                 `json:"id"`
	Name     string                 `json:"name"`
	Year     int                    `json:"year"`
	Price    float64                `json:"price"`
	Big      bool                   `json:"big"`
	Car      bool                   `json:"car"`
	Tags     []string               `json:"tags"`
	Metadata map[string]interface{} `json:"metadata"`
	Options  []Option               `json:"options"`
	Extra    interface{}            `json:"extra"`
	Dynamic  map[string]interface{} `json:"dynamic"`
}

type Option struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

func main() {
	// Initialize Quick framework with a 20MB body limit
	q := quick.New(quick.Config{
		MaxBodySize: 20 * 1024 * 1024,
	})

	// Define a POST route at /v1/user
	q.Post("/v1/user", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")
		var users []My // Store incoming user data

		// Parse the request body into the struct
		err := c.Bind(&users)
		if err != nil {
			// If parsing fails, return a 400 Bad Request response
			return c.Status(400).SendString(err.Error())
		}

		// Return the parsed JSON data as a response with 200 OK
		return c.Status(200).JSON(users)
	})

	// Start the server and listen on port 8080
	q.Listen("0.0.0.0:8080")
}
πŸ† k6 Load Test Script
import http from 'k6/http';
import { check, sleep } from 'k6';

// Load the JSON from the environment variable
const payloadData = open('./data_1k_list.json');

// K6 configuration
export let options = {
    stages: [
        { duration: '40s', target: 1000 }, // Ramp-up para 500 VUs
        { duration: '7s', target: 500 },  // MantΓ©m 500 VUs
        { duration: '5s', target: 0 },   // Ramp-down
    ],
 };


export default function () {
let url = 'http://localhost:8080/v1/user';

// Always use the same list for sending
// let payload = JSON.stringify(payloadData);

let params = {
headers: { 'Content-Type': 'application/json' },
};

let res = http.post(url, payloadData, params);

// Check if the response is correct
check(res, {
'status is 200 or 201': (r) => r.status === 200 || r.status === 201,
'response contains JSON': (r) => r.headers['Content-Type'] === 'application/json',
});

}
πŸ“ˆ Running the Tests
  • 1️⃣ Start the Quick API - Run the Quick server:
$ go run main.go
  • 2️⃣ Execute the Load Test
$ k6 run benchmark.js

πŸ“¦ Compression Middleware (compress)

The compress middleware in Quick enables automatic GZIP compression for HTTP responses, reducing the size of data transferred over the network. This improves performance and bandwidth efficiency, especially for text-based content like JSON, HTML, and CSS.

πŸš€ Benefits of Compression
  • βœ… Reduced response size – improves loading speed.
  • βœ… Bandwidth savings – ideal for mobile or limited connections.
  • βœ… Seamless integration – works automatically for compatible clients.
  • βœ… Better user experience – faster response times.

πŸ”Ή Ways to Use Quick provides three different ways to enable GZIP compression:

🌟 Available Usage Methods

Quick provides three different ways to enable GZIP compression:

  • Using quick.Handler (Default) – Follows Quick’s native syntax.
  • Using quick.HandlerFunc – Alternative method for direct function-based handlers.
  • Using net/http standard implementation – For applications using Go’s native HTTP package.
For more details on using compress, check the documentation:

πŸ“– Compress Documentation

πŸš€ Usage Example (Default)

Here is a practical example of enabling the GZIP middleware in Quick using the default approach (quick.Handler)

package main

import (
	"log"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/compress"
)

func main() {
	q := quick.New()

	// Enable Gzip middleware
	q.Use(compress.Gzip())

	// Define a route that returns a compressed JSON response
	q.Get("/v1/compress", func(c *quick.Ctx) error {
		// Setting response headers
		c.Set("Content-Type", "application/json")
		// Enabling Gzip compression
		c.Set("Accept-Encoding", "gzip") 
		// Defining the response structure
		type response struct {
			Msg     string              `json:"msg"`
			Headers map[string][]string `json:"headers"`
		}

		// Returning a JSON response with headers
		return c.Status(200).JSON(&response{
			Msg:     "Quick ❀️",
			Headers: c.Headers,
		})
	})

	// Start the HTTP server on port 8080
	log.Fatal(q.Listen("0.0.0.0:8080"))
}
πŸ“Œ cURL
$ curl -X GET http://localhost:8080/v1/compress -H 
"Accept-Encoding: gzip" --compressed -i
πŸ“Œ Response
{
   "msg":"Quick ❀️",
   "headers":{
      "Accept":[
         "*/*"
      ],
      "Accept-Encoding":[
         "gzip"
      ],
      "Cache-Control":[
         "no-cache"
      ],
      "Connection":[
         "keep-alive"
      ],
      "Postman-Token":[
         "e0b65cfe-9516-4803-96df-d443d7e6a95a"
      ],
      "User-Agent":[
         "PostmanRuntime/7.43.2"
      ]
   }
}

πŸ“ Maxbody (Request Size Limiter)

The maxbody middleware restricts the maximum request body size to prevent clients from sending excessively large payloads. This helps optimize memory usage, enhance security, and avoid unnecessary processing of oversized requests.

πŸ”Ή Why Use maxbody?
  • βœ… Prevents excessive memory usage and improves performance.
  • βœ… Mitigates DoS (Denial-of-Service) attacks by limiting large payloads.
  • βœ… Automatically returns 413 Payload Too Large when the limit is exceeded.

πŸš€ Ways to Use maxbody

There are two primary ways to enforce request body size limits in Quick:

maxbody.New() – Enforces a global request body size limit across all middleware layers.

MaxBytesReader() – Adds an extra layer of validation inside a specific request handler

πŸ”Ή Using maxbody.New()

This example applies a global request body limit of 50KB for all incoming requests.

package main

import (
	"log"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/maxbody"
)

func main() {
	q := quick.New()

	// Middleware to enforce a 50KB request body limit globally
	q.Use(maxbody.New(50 * 1024)) // 50KB

	// Define a route that accepts a request body
	q.Post("/v1/user/maxbody/any", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")

		log.Printf("Body received: %s", c.BodyString())
		return c.Status(200).Send(c.Body())
	})

	log.Fatal(q.Listen("0.0.0.0:8080"))
}
πŸ“Œ cURL

Request within limit (Valid request)

$ curl -X POST http://0.0.0.0:8080/v1/user/maxbody/any \
     -H "Content-Type: application/json" \
     --data-binary @<(head -c 48000 </dev/zero | tr '\0' 'A')

Request exceeding limit (Should return 413)

$ curl -X POST http://0.0.0.0:8080/v1/user/maxbody/any \
     -H "Content-Type: application/json" \
     --data-binary @<(head -c 51000 </dev/zero | tr '\0' 'A')

πŸ”Ή Using MaxBytesReader()

This example adds extra protection by applying MaxBytesReader() inside the request handler, ensuring an enforced limit at the application layer.

package main

import (
	"io"
	"log"
	"net/http"

	"github.com/jeffotoni/quick"
)

const maxBodySize = 1024 // 1KB

func main() {
	q := quick.New()

	// Define a route with additional MaxBytesReader validation
	q.Post("/v1/user/maxbody/max", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")

		// Limit request body size to 1KB
		c.Request.Body = quick.MaxBytesReader(c.Response, c.Request.Body, maxBodySize)

		// Securely read the request body
		body, err := io.ReadAll(c.Request.Body)
		if err != nil {
			log.Printf("Error reading request body: %v", err)
			return c.Status(http.StatusRequestEntityTooLarge).String("Request body too large")
		}
		return c.Status(http.StatusOK).Send(body)
	})

	log.Println("Server running at http://0.0.0.0:8080")
	log.Fatal(q.Listen("0.0.0.0:8080"))
}
πŸ“Œ cURL

Request within limit (Valid request)

$ curl -X POST http://0.0.0.0:8080/v1/user/maxbody/max \
     -H "Content-Type: application/json" \
     --data-binary @<(head -c 800 </dev/zero | tr '\0' 'A')

Request exceeding limit (Should return 413)

$ curl -X POST http://0.0.0.0:8080/v1/user/maxbody/max \
     -H "Content-Type: application/json" \
     --data-binary @<(head -c 2048 </dev/zero | tr '\0' 'A')

πŸ“Œ Key Differences Between maxbody.New() and MaxBytesReader()
Implementation Description
maxbody.New() Enforces a global request body size limit before processing the request.
MaxBytesReader() Adds extra validation inside the request handler, restricting only specific endpoints.

πŸ“œ Logger (Request Logging)

The logger middleware captures HTTP request details, helping with monitoring, debugging, and analytics.

πŸš€ Key Features:
  • βœ… Logs request method, path, response time, and status code.
  • βœ… Supports multiple formats: text, json, and slog (structured logging).
  • βœ… Helps track API usage and debugging.
  • βœ… Customizable log patterns and additional fields.
πŸ“ Default Logging

This example applies simple logging.

package main

import (
	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/logger"
)

func main() {

	q := quick.New()
	q.Use(logger.New())

	q.Use(logger.New(logger.Config{
		Level: "DEGUB",
	}))

	q.Use(logger.New(logger.Config{
		Level: "WARN",
	}))

	q.Get("/v1/logger", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")

		return c.Status(200).JSON(quick.M{
			"msg": "Quick ❀️",
		})
	})

	q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL

Text Logging

$ curl -i -XGET http://localhost:8080/v1/logger
Console:

Quick Logger Example


πŸ“ Structured Logging(Text Format)

This example applies logging in text format with custom log fields.

package main

import (
	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/logger"
)

func main() {

	q := quick.New()

	q.Use(logger.New(logger.Config{
		Format:  "text",
		Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
		Level:   "DEBUG",
		CustomFields: map[string]string{
			"user_id": "usr-001",
			"trace":   "trace-debug",
		},
	}))

	q.Use(logger.New(logger.Config{
		Format:  "text",
		Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
		Level:   "INFO",
		CustomFields: map[string]string{
			"user_id": "usr-002",
			"trace":   "trace-info",
		},
	}))

	q.Use(logger.New(logger.Config{
		Format:  "text",
		Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
		Level:   "WARN",
		CustomFields: map[string]string{
			"user_id": "usr-003",
			"trace":   "trace-warn",
		},
	}))

	// Definir rota GET para gerar logs
	q.Get("/v1/logger", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")

		// Retornar resposta JSON
		return c.Status(200).JSON(quick.M{
			"msg": "Quick ❀️",
		})
	})

	// Iniciar o servidor na porta 8080
	q.Listen("0.0.0.0:8080")
}

πŸ“Œ cURL

Text Logging

$ curl -i -XGET http://localhost:8080/v1/logger
Console:

Quick Logger Example


πŸ› οΈ Structured Logging (Slog Format)

This example uses structured logging (slog) for better log parsing.

package main

import (
	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/logger"
)


func main() {

	q := quick.New()

	// Apply the logger middleware with structured logging (slog)
	q.Use(logger.New(logger.Config{
		Format: "slog",
		Level:  "DEBUG",
		Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
			"user=${user_id} trace=${trace}\n",
		CustomFields: map[string]string{
			"user_id": "99999",
			"trace":   "trace-debug",
		},
	}))

	// Apply the logger middleware with structured logging (slog)
	q.Use(logger.New(logger.Config{
		Format: "slog",
		Level:  "INFO",
		Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
			"user=${user_id} trace=${trace}\n",
		CustomFields: map[string]string{
			"user_id": "99999",
			"trace":   "trace-info",
		},
	}))

	// Apply the logger middleware with structured logging (slog)
	q.Use(logger.New(logger.Config{
		Format: "slog",
		Level:  "WARN",
		Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
			"user=${user_id} trace=${trace}\n",
		CustomFields: map[string]string{
			"user_id": "99999",
			"trace":   "trace-warn",
		},
	}))

	// Define a test route
	q.Get("/v1/logger/slog", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")

		return c.Status(200).JSON(quick.M{
			"msg": "Structured logging with slog",
		})
	})

	// Start the server
	q.Listen("0.0.0.0:8080")
}

πŸ“Œ cURL

Structured Logging (Slog)

$ curl -i -XGET http://localhost:8080/v1/logger/slog
Console:

Quick Logger Example


πŸ“¦ JSON Logging (Machine-Readable)

Ideal for log aggregation systems, this example logs in JSON format.

package main

import (
	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/logger"
)

func main() {

	q := quick.New()

	// Apply logger with JSON format
	q.Use(logger.New(logger.Config{
		Format: "json",
		Level:  "INFO",
	}))

	q.Use(logger.New(logger.Config{
		Format:  "json",
		Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
		Level:   "DEBUG",
		CustomFields: map[string]string{
			"user_id": "usr-001",
			"trace":   "trace-debug",
		},
	}))

	// Apply the logger middleware with structured logging (slog)
	q.Use(logger.New(logger.Config{
		Format: "json",
		Level:  "WARN",
		Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
			"user=${user_id} trace=${trace}\n",
		CustomFields: map[string]string{
			"user_id": "usr-001",
			"trace":   "trace-warn",
		},
	}))

	// Define an endpoint that triggers logging
	q.Get("/v1/logger/json", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")

		return c.Status(200).JSON(quick.M{
			"msg": "JSON logging example",
		})
	})

	// Start the server
	q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL

JSON Logging

$ curl -i -XGET http://localhost:8080/v1/logger/json
Console:

Quick Logger Example


πŸ†” MsgUUID Middleware

πŸ“Œ Overview

The MsgUUID Middleware in Quick is responsible for automatically generating a unique identifier (UUID) for each incoming HTTP request. This identifier is added to the response headers, allowing better tracking, debugging, and log correlation in distributed systems.


πŸš€ How It Works

The MsgUUID Middleware works by:

  • Intercepts each incoming HTTP request before processing.
  • Generating a unique UUID for each request.
  • Attaching the generated UUID to the response headers for tracking.
  • Helping log correlation and debugging across distributed systems.

βœ… Key Features
Feature Benefit
πŸ†” Unique Identifier Adds a UUID to each request for tracking and correlation.
πŸ”„ Automatic Generation No need for manual UUID creation, added seamlessly.
πŸ“Š Enhanced Debugging Makes log analysis easier by attaching request identifiers.
πŸš€ Lightweight & Fast Does not impact performance, operates efficiently.

This example generates a unique request identifier with the MsgUUUID middleware.

package main

import (
	"fmt"
	"log"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/msguuid"
)

func main() {
	q := quick.New()

	// Apply MsgUUID Middleware globally
	q.Use(msguuid.New())

	// Define an endpoint that responds with a UUID
	q.Get("/v1/msguuid/default", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")

		// Log headers to validate UUID presence
		fmt.Println("Headers:", c.Response.Header())

		// Return a 200 OK status
		return c.Status(200).JSON(nil)
	})

	log.Fatal(q.Listen("0.0.0.0:8080"))
}
πŸ“Œ cURL
$ curl -i -XGET http://localhost:8080/v1/msguuid/default
πŸ“Œ Response
"Headers":"map"[
   "Content-Type":["application/json"],
   "Msguuid":[5f49cf4d-b62e-4d81-b46e-5125b52058a6]
]

πŸ“© MsgID Middleware - Quick Framework

The MsgID Middleware automatically assigns a unique identifier (MsgID) to each request. This helps with tracking, debugging, and log correlation in distributed systems.

πŸš€ Overview
  • Automatically generates a unique MsgID for every incoming request.
  • Ensures traceability across microservices and distributed applications.
  • Adds the MsgID to both request and response headers.
  • Lightweight & fast, with minimal performance overhead.

βœ… Key Features

Feature Benefit
πŸ†” Unique Identifier Adds a MsgID to each request for tracking and correlation.
πŸ”„ Automatic Generation No need for manual MsgID creation, added seamlessly.
πŸ“Š Enhanced Debugging Makes log analysis easier by attaching request identifiers.
πŸš€ Lightweight & Fast Minimal performance impact, operates efficiently.

βš™οΈ How It Works

The MsgID Middleware intercepts each incoming HTTP request. It checks if the request already has a MsgID in the headers. If not present, it generates a new MsgID and attaches it to:

  • The request headers (Msgid)
  • The response headers (Msgid)

The next middleware or handler processes the request with the assigned MsgID.

Here is an example of how to use the MsgID Middleware with Quick:

package main

import (
	"fmt"
	"log"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/msguuid"
)

func main() {
	q := quick.New()

	// Apply MsgUUID Middleware globally
	q.Use(msguuid.New())

	// Define an endpoint that responds with a UUID
	q.Get("/v1/msguuid/default", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")

		// Log headers to validate UUID presence
		fmt.Println("Headers:", c.Response.Header())

		// Return a 200 OK status
		return c.Status(200).JSON(nil)
	})

	log.Fatal(q.Listen("0.0.0.0:8080"))
}
πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/v1/msguuid/default
πŸ“Œ Response
{
  "msgid": "974562398"
}

πŸ›‘οΈ Helmet

Helmet is a middleware this package provides sensible security defaults while allowing full customization.


βœ… Key Features
  • Sets common security-related HTTP headers
  • Provides secure defaults
  • Easily customizable via Options struct
  • Supports skipping middleware per request

πŸ›‘οΈ Default Headers
Feature Status Notes / Observations
X-XSS-Protection header βœ… Legacy protection, still included
X-Content-Type-Options: nosniff header βœ… Prevents MIME sniffing attacks
X-Frame-Options header βœ… Helps prevent clickjacking
Content-Security-Policy header βœ… Defaults to default-src 'self'
CSPReportOnly support βœ… Optional report-only mode for CSP
Referrer-Policy header βœ… Defaults to no-referrer
Permissions-Policy header βœ… Controls browser features like camera, mic, etc.
Strict-Transport-Security (HSTS) support βœ… Adds HSTS for HTTPS requests
HSTS options: maxAge, includeSubDomains, preload βœ… Fully customizable
Cache-Control header βœ… Defaults to no-cache, improves response integrity
Cross-Origin-Embedder-Policy header βœ… Required for certain advanced browser APIs
Cross-Origin-Opener-Policy header βœ… Isolates browsing contexts
Cross-Origin-Resource-Policy header βœ… Restricts resource access
Origin-Agent-Cluster header βœ… Enables memory isolation in browsers
X-DNS-Prefetch-Control header βœ… Controls browser DNS prefetching
X-Download-Options header βœ… Prevents automatic downloads (IE-specific)
X-Permitted-Cross-Domain-Policies header βœ… Blocks Flash and Silverlight legacy access
Next func(c) to skip middleware dynamically βœ… Allows conditional header injection per route
Secure defaults applied when no options are provided βœ… Based on OWASP and best practices
Option naming compatible with Fiber βœ… Enables easier migration from Fiber to Quick
Built-in TLS simulation support in Qtest βœ… Enables full testing of HTTPS-only behavior
Full HTTP method coverage in Qtest βœ… GET, POST, PUT, PATCH, DELETE, OPTIONS supported
Extended Qtest assertions (headers, body, etc.) βœ… Includes AssertString, AssertNoHeader, and more

Example basic configuration of helmet with header output

package main

import (
	"github.com/jeffotoni/quick"
	"github.com/seuusuario/helmet"
)

func main() {
	q := quick.New()

	// Use Helmet middleware with default security headers
	q.Use(helmet.Helmet())

	// Simple route to test headers
	q.Get("/v1/user", func(c *quick.Ctx) error {

		// list all headers
		headers := make(map[string]string)
		for k, v := range c.Response.Header() {
			if len(v) > 0 {
				headers[k] = v[0]
			}
		}
		return c.Status(200).JSONIN(headers)
	})

	q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL
$ curl -X GET 'http://localhost:8080/v1/user'
πŸ“Œ Response
{
  "Cache-Control": "no-cache, no-store, must-revalidate",
  "Content-Security-Policy": "default-src 'self'",
  "Cross-Origin-Embedder-Policy": "require-corp",
  "Cross-Origin-Opener-Policy": "same-origin",
  "Cross-Origin-Resource-Policy": "same-origin",
  "Origin-Agent-Cluster": "?1",
  "Referrer-Policy": "no-referrer",
  "X-Content-Type-Options": "nosniff",
  "X-DNS-Prefetch-Control": "off",
  "X-Download-Options": "noopen",
  "X-Frame-Options": "SAMEORIGIN",
  "X-Permitted-Cross-Domain-Policies": "none",
  "X-XSS-Protection": "0"
}

✨ Using M as an Alias for map[string]interface{}

The M type is a convenient alias for map[string]interface{} in Quick, making JSON response creation cleaner and more readable.

πŸ”Ή In traditional Go code, you would use a map[string]interface{} explicitly when returning JSON responses:
package main

import (
	"log"

	"github.com/jeffotoni/quick"
)

func main() {
	q := quick.New()

	// Define a GET route at "/ping"
	q.Get("/ping", func(c *quick.Ctx) error {
		c.Status(200) // Set the HTTP status code
		return c.JSON(map[string]interface{}{
			"message": "pong", // JSON response message
		})
	})

	// Start the Quick server
	log.Fatal(q.Listen("0.0.0.0:8080"))
}
πŸ”Ή Using quick.M, you can simplify the JSON response declaration:
package main

import (
    "github.com/jeffotoni/quick"
)

func main() {
    app := quick.New()

    app.Get("/ping", func(c *quick.Context) {
        c.JSON(200, quick.M{
            "message": "pong",
        })
    })

    app.Run()
}

πŸ“Œ Why Use M?
  • Less Boilerplate: Eliminates repetitive map[string]interface{} syntax.
  • Readability: Improves code readability, making JSON responses more intuitive.
  • Convenience: Makes it easier to return JSON responses in handlers.

πŸŽ—οΈ Recover

The Recover middleware provides a robust way to intercept and gracefully handle panics during HTTP request processing.

Instead of allowing your application to crash due to an unexpected panic, this middleware recovers from it, logs the error, and returns a 500 Internal Server Error response. Optionally, it can also print the stack trace to help with debugging.


✨ Features
  • βœ… Recovers from panics without crashing the server
  • 🧠 Optionally logs stack traces for debugging
  • πŸ”Œ Custom error handling via StackTraceHandler
  • πŸ”„ Can be conditionally skipped with Next() function

This example demonstrating how to use the Recover middleware in a Quick application.
This middleware protects your app by recovering from unexpected panics during request handling,
logging the error (optionally with a stack trace), and returning a 500 Internal Server Error to the client.

In this example, the route /v1/recover intentionally triggers a panic.
Thanks to the Recover middleware, the server won't crash β€” instead, it will return a proper error response.

package main

import (
	"errors"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/recover"
)

func main() {
	q := quick.New()

	// Apply the Recover middleware
	q.Use(recover.New(recover.Config{
		App: q,
	}))

	// Define a test route
	q.Get("/v1/recover", func(c *quick.Ctx) error {
		c.Set("Content-Type", "application/json")

		// halt the server
		panic(errors.New("Panicking!"))
	})

	// Start the server
	q.Listen("0.0.0.0:8080")
}
πŸ“Œ cURL
$ curl -i -X GET http://localhost:8080/v1/recover
πŸ“Œ Response
HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8

Internal Server Error

βš™οΈ Configuration Options

You can configure the behavior of the middleware using the recover.Config struct:

Field Type Description
EnableStacktrace bool Whether to print the stack trace to stderr. Defaults to true.
Next func(c *quick.Ctx) bool Skips the middleware if the function returns true. Optional.
StackTraceHandler func(c *quick.Ctx, err interface{}) Custom function to handle the panic. Useful for error tracking/logging.

πŸ› οΈ Healthcheck

The Healthcheck middleware provides a simple and customizable way to monitor your application’s health status.

This is especially useful in cloud-native applications and containerized environments (e.g., Docker, Kubernetes), where automated systems frequently check endpoints to determine if the application is healthy and responsive.


✨ Features
  • βœ… Lightweight and easy to use
  • πŸ” Custom health probe logic (e.g., database ping, cache status)
  • 🌐 Customizable endpoint path (default: /healthcheck)
  • 🎯 Optional Next function to conditionally skip middleware
  • 🧩 Designed for microservices and production-readiness

This basic example demonstrating how to use the Healthcheck middleware with its default configuration.
It registers a /healthcheck endpoint that responds with 200 OK if the app is considered healthy.

package main

import (
	"log"

	"github.com/jeffotoni/quick"
	"github.com/seuusuario/healthcheck"
)

func main() {
	q := quick.New()

	// Use Healthcheck middleware with default endpoint (/healthcheck)
	q.Use(healthcheck.New(
		healthcheck.Options{
			App: q,
		},
	))

	q.Get("/", func(c *quick.Ctx) error {
		return c.Status(200).String("Home page")
	})

	log.Fatalln(q.Listen(":8080"))
}
πŸ“Œ cURL
$ curl -X GET http://localhost:8080/healthcheck
πŸ“Œ Response
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8

OK
βš™οΈ Custom Configuration

You can fully customize the behavior of the middleware by using the Options struct. For example, changing the endpoint:

q.Use(healthcheck.New(
	healthcheck.Options{
		App:      q,
		Endpoint: "/v1/health",
	},
))

You can also define a custom health probe function, which runs whenever the endpoint is called:

q.Use(healthcheck.New(
	healthcheck.Options{
		App: q,
		Probe: func(c *quick.Ctx) bool {
			// Perform custom checks (e.g., database, cache)
			return true // or false if unhealthy
		},
	},
))
πŸ”Ž Advanced Configuration Options
Field Type Description
App *quick.Quick The Quick application instance (required).
Endpoint string The route path to expose the healthcheck. Default: /healthcheck.
Probe func(c *quick.Ctx) bool Optional function to perform custom health checks. Returns true or false.
Next func(c *quick.Ctx) bool Skips the middleware when it returns true. Useful for conditional logic.

πŸ–ΌοΈ Template Engine

This package provides a flexible and extensible template rendering engine for the Quick web framework.
It allows you to build dynamic HTML views using Go's standard html/template package, enriched with features like layout support, custom functions, and file system abstraction.


πŸ“Œ What is a Template?

In web development, a template is a file that defines the structure of the output (usually HTML) with dynamic placeholders.
You can inject data into these placeholders at runtime to render personalized content for each request.


πŸš€ Features
  • βœ… Supports rendering templates with optional layout wrapping
  • πŸ” Nested layouts (base.html wrapping main.html wrapping index.html)
  • πŸ”§ Custom template functions via AddFunc
  • πŸ“ Loads templates from local file system or embedded fs.FS (e.g., embed.FS)
  • πŸ“¦ Fully compatible with Go’s html/template

πŸ—οΈ HTML Engine

The html.Engine is a ready-to-use implementation that supports:

  • File system loading (local or embedded)
  • Custom template functions
  • Layout composition

🧩 Rendering Templates with and without Layouts

The example below shows how to render templates in Quick using:

  • A basic template (/)
  • A template wrapped with a single layout (/layout)
  • A template wrapped with nested layouts (/layout-nested)

It also demonstrates how to register custom template functions (e.g., upper) and how to configure the html.Engine to load .html files from the views/ directory.


πŸ“ Project Structure
.
β”œβ”€β”€ main.go
└── views/
    β”œβ”€β”€ index.html
    └── layouts/
        β”œβ”€β”€ main.html
        └── base.html

package main

import (
	"strings"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/template/html"
)

func main() {
	engine := html.New("./views", ".html")

	// Example of adding a custom function
	engine.AddFunc("upper", strings.ToUpper)
	engine.Load()

	app := quick.New(quick.Config{
		Views: engine,
	})

	app.Get("/", func(c *quick.Ctx) error {
		return c.HTML("index", map[string]interface{}{
			"Title":   "Quick + Templates",
			"Message": "this is your index content in views",
		})
	})

	app.Get("/layout", func(c *quick.Ctx) error {
		return c.HTML("index", map[string]interface{}{
			"Title":   "Quick with Layout",
			"Message": "layout with main.html",
		}, "layouts/main")
	})

	app.Get("/layout-nested", func(c *quick.Ctx) error {
		return c.HTML("index", map[string]interface{}{
			"Title":   "Nested Layouts",
			"Message": "this is nested layout content",
		}, "layouts/main", "layouts/base")
	})

	app.Listen(":8080")
}

πŸ“¦ Rendering Templates with embed.FS (Go 1.16+)

This example demonstrates how to embed templates into your Go binary using the embed package.
This is useful for distributing a single executable without external template files.


πŸ“ Embedded Project Structure
project/
β”œβ”€β”€ main.go
└── views/
    β”œβ”€β”€ index.html
    └── layouts/
        β”œβ”€β”€ main.html
        └── base.html
🧩 Example Using embed.FS
package main

import (
	"embed"
	"strings"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/template/html"
)

//go:embed views/*.html views/layouts/*.html
var viewsFS embed.FS

func main() {
	engine := html.NewFileSystem(viewsFS, ".html")
	engine.Dir = "views" // required for path normalization
	engine.AddFunc("upper", strings.ToUpper)
	engine.Load()

	app := quick.New(quick.Config{
		Views: engine,
	})

	app.Get("/", func(c *quick.Ctx) error {
		return c.HTML("index", map[string]interface{}{
			"Title":   "Quick + Templates (embed)",
			"Message": "this is your index content in views (embedded)",
		})
	})

	app.Get("/layout", func(c *quick.Ctx) error {
		return c.HTML("index", map[string]interface{}{
			"Title":   "Quick with Layout",
			"Message": "layout with main.html",
		}, "layouts/main")
	})

	app.Get("/layout-nested", func(c *quick.Ctx) error {
		return c.HTML("index.html", map[string]interface{}{
			"Title":   "Nested Layouts",
			"Message": "this is nested layout content",
		}, "layouts/main", "layouts/base")
	})

	app.Listen(":8080")
}
πŸ“Œ cURL
curl -i http://localhost:8080/
curl -i http://localhost:8080/layout
curl -i http://localhost:8080/layout-nested

πŸ” Local Filesystem vs embed.FS: When to Use Each?

The html.Engine supports both loading templates from disk (html.New) and embedding them in the binary (html.NewFileSystem).
Here’s a comparison to help you choose the best option for your project:

Feature Local Filesystem (html.New) Embedded Filesystem (html.NewFileSystem)
πŸ“‚ Files stored externally βœ… Yes ❌ No (compiled into binary)
πŸ“¦ Single binary deployment ❌ No (requires template files) βœ… Yes (no external files needed)
πŸ” Changes reflect on restart βœ… Yes ❌ No (requires recompilation)
πŸš€ Ideal for development βœ… Fast iteration and preview ⚠️ Requires rebuild for every change
πŸ”’ Ideal for production ⚠️ Needs extra steps to bundle files βœ… Safer and cleaner deploy
βš™οΈ Config example html.New("./views", ".html") html.NewFileSystem(viewsFS, ".html")

🧠 PPROF

pprof provides profiling endpoints for your Quick application. It helps you to identify performance bottlenecks, monitor resource usage, and ensure that the code runs efficiently


πŸ”» Environment

Profiling is only enabled in development mode (APP_ENV=development).

We strongly recommend to use it only in development mode because in production it can introduce unwanted overhead and potentially degrade performance.


🧩 Example Usage
package main

import (
	"errors"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/pprof"
)

func main() {
	q := quick.New()

	// Apply the Profiling middleware
	q.Use(pprof.New())

	// Define a test route
	q.Get("/", func(c *quick.Ctx) error {
		c.Set("Content-Type", "text/plain")
		return c.Status(quick.StatusOK).String("OK")
	})

	// Start the server
	q.Listen("0.0.0.0:8080")
}

Routes

Profiling middleware registers a set of routes for profiling:

  • /debug/pprof
  • /debug/cmdline
  • /debug/profile
  • /debug/symbol
  • /debug/pprof/trace
  • /debug/goroutine
  • /debug/heap
  • /debug/threadcreate
  • /debug/mutex
  • /debug/allocs
  • /debug/block

Let's test our pprof

So that we can view the graphical form of our pprof in the browser, we will install the package graphviz.

For Mac

$ brew install graphviz

For Linux

$ apt install graphviz

package main

import (
	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/pprof"
)

func main() {
	q := quick.New()

	q.Use(pprof.New())

	q.Get("/busy", func(c *quick.Ctx) error {
		// Simulates a load
		sum := 0
		for i := 0; i < 1e7; i++ {
			sum += i
		}
		return c.String("done")
	})

	// Mandatory route for pprof to work correctly
	q.Get("/debug/pprof*", func(c *quick.Ctx) error {
		return c.Next()
	})

	q.Listen("0.0.0.0:8080")
}

$ go run main.go

Let's generate a small load in our API

$ while true; do curl -s http://localhost:8080/busy > /dev/null; done
Check if pprof is active

In Browser

http://localhost:8080/debug/pprof/
You will see the list:
  • allocs
  • block
  • cmdline
  • goroutine
  • heap
  • mutex
  • profile
  • threadcreate
  • trace

πŸ“ cache

Cache is a quick middleware designed to responses and intercept cache. This middleware will create a http server, using quick framework

Key Features
  • πŸš€ Built-in caching via quick/middleware/cache

  • ⏱️ Automatic cache expiration (default: 1 minute)

  • πŸ“ Cache visibility through X-Cache-Status header (HIT/MISS)

  • πŸ“¦ Supports multiple response types (string, JSON)


🧩 Example Usage
// Example of basic cache middleware usage in Quick
package main

import (
	"fmt"
	"time"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/cache"
)

func main() {
	// Create a new Quick app
	q := quick.New()

	// Use the cache middleware with default settings
	q.Use(cache.New())

	// Route 1: Returns the current time
	q.Get("/time", func(c *quick.Ctx) error {
		return c.String("Current time: " + time.Now().Format(time.RFC1123))
	})

	// Route 2: Returns a random number
	q.Get("/random", func(c *quick.Ctx) error {
		return c.String("Random value: " + time.Now().Format("15:04:05.000"))
	})

	// Route 3: Returns JSON data
	q.Get("/profile", func(c *quick.Ctx) error {
		return c.JSON(quick.M{
			"user":  "jeffotoni",
			"since": time.Now().Format("2006-01-02 15:04:05"),
		})
	})

	// Start the server
	fmt.Println("Server running on http://localhost:3000")
	fmt.Println("Try these endpoints:")
	fmt.Println("  - GET /time (cached for 1 minute)")
	fmt.Println("  - GET /random (cached for 1 minute)")
	fmt.Println("  - GET /profile (cached for 1 minute)")
	fmt.Println("Check the X-Cache-Status header in the response to see if it's a HIT or MISS")
	q.Listen(":3000")
}

Let's test our cache
$ go run main.go
🧩| Custom example
// Example of cache middleware with custom key generation in Quick
package main

import (
	"fmt"
	"time"

	"github.com/jeffotoni/quick"
	"github.com/jeffotoni/quick/middleware/cache"
)

func main() {
	// Create a new Quick q
	q := quick.New()

	// Use the cache middleware with custom key generation
	q.Use(cache.New(cache.Config{
		Expiration: 1 * time.Minute,
		KeyGenerator: func(c *quick.Ctx) string {
			return c.Path() + "?lang=" + c.Query["lang"] + "&user=" + c.Query["user"]
		},
		CacheHeader:          "X-Cache-Status",
		StoreResponseHeaders: true,
		Methods:              []string{quick.MethodGet},
		CacheInvalidator: func(c *quick.Ctx) bool {
			return c.Query["clear"] == "1"
		},
	}))

	// Route that returns a greeting in the requested language
	q.Get("/greeting", func(c *quick.Ctx) error {
		lang := c.Query["lang"]
		user := c.Query["user"]
		if user == "" {
			user = "Guest"
		}

		greeting := "Hello"
		switch lang {
		case "pt":
			greeting = "OlΓ‘"
		case "es":
			greeting = "Hola"
		case "fr":
			greeting = "Bonjour"
		case "it":
			greeting = "Ciao"
		case "de":
			greeting = "Hallo"
		}

		return c.String(fmt.Sprintf("%s, %s! (Generated at %s)",
			greeting, user, time.Now().Format(time.RFC3339)))
	})

	// Start the server
	fmt.Println("Server running on http://localhost:3000")
	fmt.Println("Try these examples:")
	fmt.Println("  - GET /greeting?lang=en&user=John")
	fmt.Println("  - GET /greeting?lang=pt&user=Maria")
	fmt.Println("  - GET /greeting?lang=es&user=Carlos")
	fmt.Println("  - GET /greeting?lang=en&user=John (should be cached)")
	fmt.Println("  - GET /greeting?lang=en&user=John&clear=1 (invalidates cache)")
	fmt.Println("Check the X-Cache-Status header in the response to see if it's a HIT or MISS")
	q.Listen(":3000")
}

Let's test our custom cors
$ go run main.go

πŸ“š| More Examples

This directory contains practical examples of the Quick Framework, a fast and lightweight web framework developed in Go.

The examples are organized in separate folders, each containing a complete example of using the framework in a simple web application.

πŸ“Œ Want to contribute?

If you have some interesting example of using the Quick Framework, feel free to send a Pull Request(PR) with your contribution.

πŸ‘‰ Check out the official example repository:

πŸ”— Quick Framework Examples

🀝| Contributions & Community Support

We already have several examples, and we can already test and play 😁. Of course, we are at the beginning, still has much to do. Feel free to do PR (at risk of winning a Go t-shirt ❀️ and of course recognition as a professional Go 😍 in the labor market).

πŸ‘‹ New to the project? Start here:

Check out our First Contact Improvements Guide to get up and running smoothly!

πŸš€ Quick Project Supporters πŸ™

The Quick Project aims to develop and provide quality software for the developer community. πŸ’» To continue improving our tools, we rely on the support of our sponsors in Patreon. 🀝

We thank all our supporters! πŸ™Œ If you also believe in our work and want to contribute to the advancement of the development community, consider supporting Project Quick on our Patreon click here

Together we can continue to build amazing tools! πŸš€

πŸ‘€ Avatar πŸ”₯ User πŸ’° Donation
@jeffotoni ⭐ x 10
@Crow3442 ⭐ x 5
@Guilherme-De-Marchi ⭐ x 5
@jaquelineabreu ⭐ x 5
@emmadal ⭐ x 5

Documentation ΒΆ

Overview ΒΆ

Ctx represents the context of an HTTP request and response.

It provides access to the request, response, headers, query parameters, body, and other necessary attributes for handling HTTP requests.

Fields:

  • Response: The HTTP response writer.
  • Request: The HTTP request object.
  • resStatus: The HTTP response status code.
  • MoreRequests: Counter for additional requests in a batch processing scenario.
  • bodyByte: The raw body content as a byte slice.
  • JsonStr: The raw body content as a string.
  • Headers: A map containing all request headers.
  • Params: A map containing URL parameters (e.g., /users/:id β†’ id).
  • Query: A map containing query parameters (e.g., ?name=John).
  • uploadFileSize: The maximum allowed upload file size in bytes.
  • App: A reference to the Quick application instance.

Package quick provides a high-performance, minimalistic web framework for building web applications in Go.

This file defines constants for HTTP methods and status codes, as well as a utility function to return human-readable descriptions for status codes. These definitions ensure consistent use of HTTP standards throughout the framework.

πŸš€ Quick is a flexible and extensible route manager for the Go language. It aims to be fast and performant, and 100% net/http compatible. Quick is a project under constant development and is open for collaboration, everyone is welcome to contribute. 😍

Package quick provides a high-performance, lightweight web framework for building modern HTTP applications in Go. It is designed for speed, efficiency, and simplicity.

Features: - Middleware support for request/response processing. - Optimized routing with low overhead. - Built-in support for JSON, XML, and form parsing. - Efficient request handling using sync.Pool for memory optimization. - Customizable response handling with structured output.

Quick is ideal for building RESTful APIs, microservices, and high-performance web applications.

Package quick provides a high-performance HTTP framework for building web applications in Go.

Quick is designed to be lightweight and efficient, offering a simplified API for handling HTTP requests, file uploads, middleware, and routing.

Features:

  • Route management with support for grouped routes.
  • Middleware support for request processing.
  • File handling capabilities, including uploads and size validation.
  • High-performance request handling using Go’s standard `net/http` package.

Qtest is an advanced HTTP testing function designed to facilitate route validation in the Quick framework.

It allows you to test simulated HTTP requests using httptest, supporting:

  • MΓ©todos HTTP personalizados (GET, POST, PUT, DELETE, etc.)
  • CabeΓ§alhos (Headers) personalizados.
  • ParΓ’metros de Query (QueryParams).
  • Corpo da RequisiΓ§Γ£o (Body).
  • Cookies.
  • ValidaΓ§Γ΅es embutidas para status, headers e corpo da resposta.

The Qtest function receives a QuickTestOptions structure containing the request parameters, executes the call and returns a QtestReturn object, which provides methods for analyzing and validating the result.

Index ΒΆ

Examples ΒΆ

Constants ΒΆ

View Source
const (
	// MethodGet represents the HTTP GET method.
	MethodGet = "GET"

	// MethodHead represents the HTTP HEAD method.
	MethodHead = "HEAD"

	// MethodPost represents the HTTP POST method.
	MethodPost = "POST"

	// MethodPut represents the HTTP PUT method.
	MethodPut = "PUT"

	// MethodPatch represents the HTTP PATCH method (RFC 5789).
	MethodPatch = "PATCH"

	// MethodDelete represents the HTTP DELETE method.
	MethodDelete = "DELETE"

	// MethodConnect represents the HTTP CONNECT method.
	MethodConnect = "CONNECT"

	// MethodOptions represents the HTTP OPTIONS method.
	MethodOptions = "OPTIONS"

	// MethodTrace represents the HTTP TRACE method.
	MethodTrace = "TRACE"
)
View Source
const (

	// StatusContinue indicates that the client should continue with its request.
	StatusContinue = 100 // RFC 9110, 15.2.1

	// StatusSwitchingProtocols indicates that the server is switching protocols as requested.
	StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2

	// StatusProcessing indicates that the server has received the request but has not completed processing.
	StatusProcessing = 102 // RFC 2518, 10.1

	// StatusEarlyHints provides early hints to help a client start preloading resources.
	StatusEarlyHints = 103 // RFC 8297

	// StatusOK indicates that the request has succeeded.
	StatusOK = 200 // RFC 9110, 15.3.1

	// StatusCreated indicates that the request has been fulfilled and a new resource is created.
	StatusCreated = 201 // RFC 9110, 15.3.2

	// StatusAccepted indicates that the request has been accepted for processing but is not complete.
	StatusAccepted = 202 // RFC 9110, 15.3.3

	// StatusNonAuthoritativeInfo indicates that the response contains information from another source.
	StatusNonAuthoritativeInfo = 203 // RFC 9110, 15.3.4

	// StatusNoContent indicates that the server successfully processed the request but has no content to return.
	StatusNoContent = 204 // RFC 9110, 15.3.5

	// StatusResetContent indicates that the client should reset the document view.
	StatusResetContent = 205 // RFC 9110, 15.3.6

	// StatusPartialContent indicates that only part of the requested resource is returned.
	StatusPartialContent = 206 // RFC 9110, 15.3.7

	// StatusMultiStatus indicates that multiple status codes might be returned.
	StatusMultiStatus = 207 // RFC 4918, 11.1

	// StatusAlreadyReported indicates that the request has already been reported in a previous response.
	StatusAlreadyReported = 208 // RFC 5842, 7.1

	// StatusIMUsed indicates that the response is a result of an instance manipulation.
	StatusIMUsed = 226 // RFC 3229, 10.4.1

	// StatusMultipleChoices indicates that multiple possible resources could be returned.
	StatusMultipleChoices = 300 // RFC 9110, 15.4.1

	// StatusMovedPermanently indicates that the resource has moved permanently to a new URI.
	StatusMovedPermanently = 301 // RFC 9110, 15.4.2

	// StatusFound indicates that the requested resource has been temporarily moved.
	StatusFound = 302 // RFC 9110, 15.4.3

	// StatusSeeOther indicates that the response is available at a different URI.
	StatusSeeOther = 303 // RFC 9110, 15.4.4

	// StatusNotModified indicates that the resource has not been modified since the last request.
	StatusNotModified = 304 // RFC 9110, 15.4.5

	// StatusUseProxy indicates that the requested resource must be accessed through a proxy.
	StatusUseProxy = 305 // RFC 9110, 15.4.6

	// StatusTemporaryRedirect indicates that the request should be repeated with a different URI.
	StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8

	// StatusPermanentRedirect indicates that the resource has been permanently moved.
	StatusPermanentRedirect = 308 // RFC 9110, 15.4.9

	// StatusBadRequest indicates that the server cannot process the request due to client error.
	StatusBadRequest = 400 // RFC 9110, 15.5.1

	// StatusUnauthorized indicates that authentication is required and has failed or not been provided.
	StatusUnauthorized = 401 // RFC 9110, 15.5.2

	// StatusPaymentRequired is reserved for future use (typically related to digital payments).
	StatusPaymentRequired = 402 // RFC 9110, 15.5.3

	// StatusForbidden indicates that the request is valid, but the server is refusing to process it.
	StatusForbidden = 403 // RFC 9110, 15.5.4

	// StatusNotFound indicates that the requested resource could not be found.
	StatusNotFound = 404 // RFC 9110, 15.5.5

	// StatusMethodNotAllowed indicates that the request method is not allowed for the resource.
	StatusMethodNotAllowed = 405 // RFC 9110, 15.5.6

	// StatusNotAcceptable indicates that the server cannot return a response that meets the client's requirements.
	StatusNotAcceptable = 406 // RFC 9110, 15.5.7

	// StatusProxyAuthRequired indicates that authentication is required for a proxy server.
	StatusProxyAuthRequired = 407 // RFC 9110, 15.5.8

	// StatusRequestTimeout indicates that the server timed out waiting for the request.
	StatusRequestTimeout = 408 // RFC 9110, 15.5.9

	// StatusConflict indicates that the request could not be completed due to a conflict with the current resource state.
	StatusConflict = 409 // RFC 9110, 15.5.10

	// StatusGone indicates that the requested resource is no longer available and will not return.
	StatusGone = 410 // RFC 9110, 15.5.11

	// StatusLengthRequired indicates that the request must include a valid `Content-Length` header.
	StatusLengthRequired = 411 // RFC 9110, 15.5.12

	// StatusPreconditionFailed indicates that a precondition in the request headers was not met.
	StatusPreconditionFailed = 412 // RFC 9110, 15.5.13

	// StatusRequestEntityTooLarge indicates that the request body is too large for the server to process.
	StatusRequestEntityTooLarge = 413 // RFC 9110, 15.5.14

	// StatusRequestURITooLong indicates that the request URI is too long for the server to process.
	StatusRequestURITooLong = 414 // RFC 9110, 15.5.15

	// StatusUnsupportedMediaType indicates that the request body format is not supported by the server.
	StatusUnsupportedMediaType = 415 // RFC 9110, 15.5.16

	// StatusRequestedRangeNotSatisfiable indicates that the range specified in the request cannot be fulfilled.
	StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17

	// StatusExpectationFailed indicates that the server cannot meet the expectations set in the request headers.
	StatusExpectationFailed = 417 // RFC 9110, 15.5.18

	// StatusTeapot is an Easter egg from RFC 9110, originally from April Fools' Day (RFC 2324).
	StatusTeapot = 418 // RFC 9110, 15.5.19 (Unused)

	// StatusMisdirectedRequest indicates that the request was directed to a server that cannot respond appropriately.
	StatusMisdirectedRequest = 421 // RFC 9110, 15.5.20

	// StatusUnprocessableEntity indicates that the request was well-formed but contains semantic errors.
	StatusUnprocessableEntity = 422 // RFC 9110, 15.5.21

	// StatusLocked indicates that the requested resource is currently locked.
	StatusLocked = 423 // RFC 4918, 11.3

	// StatusFailedDependency indicates that the request failed due to a failed dependency.
	StatusFailedDependency = 424 // RFC 4918, 11.4

	// StatusTooEarly indicates that the request was sent too early and should be retried later.
	StatusTooEarly = 425 // RFC 8470, 5.2.

	// StatusUpgradeRequired indicates that the client should switch to a different protocol (e.g., HTTPS).
	StatusUpgradeRequired = 426 // RFC 9110, 15.5.22

	// StatusPreconditionRequired indicates that a precondition header is required for the request.
	StatusPreconditionRequired = 428 // RFC 6585, 3

	// StatusTooManyRequests indicates that the client has sent too many requests in a given period.
	StatusTooManyRequests = 429 // RFC 6585, 4

	// StatusRequestHeaderFieldsTooLarge indicates that the request headers are too large for the server to process.
	StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5

	// StatusUnavailableForLegalReasons indicates that the resource is unavailable for legal reasons (e.g., censorship).
	StatusUnavailableForLegalReasons = 451 // RFC 7725, 3

	// StatusInternalServerError indicates that the server encountered an unexpected condition.
	StatusInternalServerError = 500 // RFC 9110, 15.6.1

	// StatusNotImplemented indicates that the server does not support the requested functionality.
	StatusNotImplemented = 501 // RFC 9110, 15.6.2

	// StatusBadGateway indicates that the server, acting as a gateway or proxy, received an invalid response.
	StatusBadGateway = 502 // RFC 9110, 15.6.3

	// StatusServiceUnavailable indicates that the server is temporarily unable to handle the request (e.g., overloaded or under maintenance).
	StatusServiceUnavailable = 503 // RFC 9110, 15.6.4

	// StatusGatewayTimeout indicates that the server, acting as a gateway or proxy, did not receive a timely response.
	StatusGatewayTimeout = 504 // RFC 9110, 15.6.5

	// StatusHTTPVersionNotSupported indicates that the server does not support the HTTP version used in the request.
	StatusHTTPVersionNotSupported = 505 // RFC 9110, 15.6.6

	// StatusVariantAlsoNegotiates indicates that the server has an internal configuration error preventing negotiation.
	StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1

	// StatusInsufficientStorage indicates that the server cannot store the representation needed to complete the request.
	StatusInsufficientStorage = 507 // RFC 4918, 11.5

	// StatusLoopDetected indicates that the server detected an infinite loop while processing the request.
	StatusLoopDetected = 508 // RFC 5842, 7.2

	// StatusNotExtended indicates that further extensions to the request are required for the server to fulfill it.
	StatusNotExtended = 510 // RFC 2774, 7

	// StatusNetworkAuthenticationRequired indicates that the client must authenticate to gain network access.
	StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
)

HTTP status codes as registered with IANA. See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml

View Source
const (
	ContentTypeAppJSON = `application/json`
	ContentTypeAppXML  = `application/xml`
	ContentTypeTextXML = `text/xml`
)

Content-Type constants used for response headers

View Source
const (
	Reset  = "\033[0m"
	Blue   = "\033[34m"
	Green  = "\033[32m"
	Yellow = "\033[33m"
	Cyan   = "\033[36m"
	Bold   = "\033[1m"
)

ANSI terminal color codes used for banner styling

View Source
const ACCUMULATED_CONTEXT_KEY = "__quick_context_data__"
View Source
const QuickVersion = "v0.0.1"

QuickVersion represents the current version of the Quick framework.

View Source
const SO_REUSEPORT = 0x0F

SO_REUSEPORT is a constant manually defined for Linux systems

Variables ΒΆ

View Source
var ContextDataCallback func(*http.Request, map[string]any)

ContextDataCallback is called when SetContext is invoked

Functions ΒΆ

func MaxBytesReader ΒΆ

func MaxBytesReader(w http.ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser

MaxBytesReader is a thin wrapper around http.MaxBytesReader to limit the size of the request body in Quick applications.

It returns an io.ReadCloser that reads from r but stops with an error after n bytes. The sink just sees an io.EOF.

This is useful to protect against large request bodies.

Example usage:

c.Request.Body = quick.MaxBytesReader(c.Response, c.Request.Body, 10_000) // 10KB
Example ΒΆ

This function is named ExampleMaxBytesReader()

it with the Examples type.
// Start Quick framework instance
q := New()

// Set max request body size to 1KB (1024 bytes)
const maxBodySize = 1024

// Simulate a request using Quick's test utility with a payload exceeding 1KB
oversizedBody := make([]byte, 2048) // 2KB of data to exceed the limit
for i := range oversizedBody {
	oversizedBody[i] = 'A'
}

// Define a route with MaxBytesReader for extra validation
q.Post("/v1/user/maxbody/max", func(c *Ctx) error {
	c.Set("Content-Type", "application/json")
	// Apply MaxBytesReader for additional size enforcement
	c.Request.Body = MaxBytesReader(c.Response, c.Request.Body, maxBodySize)

	// Read request body
	body, err := io.ReadAll(c.Request.Body)
	if err != nil {
		return c.Status(http.StatusRequestEntityTooLarge).String("Request body too large")
	}

	return c.Status(http.StatusOK).Send(body)
})

// Simulate a request using Quick's test utility
res, _ := q.Qtest(QuickTestOptions{
	Method:  MethodPost,
	URI:     "/v1/user/maxbody/max",
	Headers: map[string]string{"Content-Type": "application/json"},
	Body:    oversizedBody, // Convert string to []byte
})

if err := res.AssertStatus(413); err != nil {
	fmt.Println("status error:", err)
}

if err := res.AssertString("Request body too large"); err != nil {
	fmt.Println("body error:", err)
}

// Print response status and body for verification
fmt.Println(res.StatusCode()) // Expecting: 413 (Payload too large)
fmt.Println(res.BodyStr())    // Expecting: "Request body too large"
Output:

413
Request body too large

func NotFound ΒΆ

func NotFound(w http.ResponseWriter, r *http.Request)

NotFound sends a 404 Not Found response with optional custom body. It wraps http.NotFound and provides a Quick-style naming.

func SaveAll ΒΆ

func SaveAll(files []*UploadedFile, destination string) error

SaveAll saves all uploaded files to the specified directory.

This function iterates through a slice of uploaded files and saves them using the `Save()` method.

Parameters:

  • files ([]*UploadedFile): Slice of uploaded files.
  • destination (string): The directory where the files should be saved.

Returns:

  • error: An error if any file fails to save.

Example Usage:

files := []*UploadedFile{file1, file2}
err := SaveAll(files, "./uploads")
if err != nil {
    log.Fatal("Failed to save all files:", err)
}
Example ΒΆ

This function is named ExampleSaveAll() it with the Examples type.

q := New()

// Define a POST route for uploading multiple files
q.Post("/upload", func(c *Ctx) error {
	// Set file upload size limit
	c.FormFileLimit("10MB")

	// Retrieve multiple uploaded files
	files, err := c.FormFiles("file")
	if err != nil {
		return c.Status(400).JSON(Msg{
			Msg:   "Upload error",
			Error: err.Error(),
		})
	}

	// Save all files to a directory (e.g., "uploads")
	err = SaveAll(files, "uploads")
	if err != nil {
		return c.Status(500).JSON(Msg{
			Msg:   "Failed to save files",
			Error: err.Error(),
		})
	}

	// Return JSON response indicating success
	return c.Status(200).JSON(map[string]string{
		"message": "All files saved successfully",
	})
})

// Create a list of UploadedFile instances to simulate multiple uploads
files := []*UploadedFile{
	{
		Info: FileInfo{
			Filename: "file1.txt",
			Bytes:    []byte("File 1 content"),
		},
	},
	{
		Info: FileInfo{
			Filename: "file2.txt",
			Bytes:    []byte("File 2 content"),
		},
	},
}

// Define the target directory for saving the files
targetDir := "test_uploads"

// Save all files using SaveAll function
err := SaveAll(files, targetDir)

// Handle result
if err != nil {
	fmt.Println("Error saving files:", err)
} else {
	fmt.Println("All files saved successfully!")
}
Output:

All files saved successfully!

func StatusText ΒΆ

func StatusText(code int) string

StatusText returns a text for the HTTP status code. It returns the empty string if the code is unknown.

Example ΒΆ

This function is named ExampleStatusText() it with the Examples type.

fmt.Println(StatusText(200))
fmt.Println(StatusText(404))
fmt.Println(StatusText(500))
Output:

OK
Not Found
Internal Server Error

Types ΒΆ

type App ΒΆ

type App = Quick

indeed to Quick

type Config ΒΆ

type Config struct {
	BodyLimit      int64 // Deprecated: Use MaxBodySize instead
	MaxBodySize    int64 // Maximum request body size allowed.
	MaxHeaderBytes int   // Maximum number of bytes allowed in the HTTP headers.

	GOMAXPROCS      int   // defines the maximum number of CPU cores
	GCHeapThreshold int64 // GCHeapThreshold sets the memory threshold (in bytes)
	BufferPoolSize  int   // BufferPoolSize determines the size (in bytes)

	RouteCapacity     int           // Initial capacity of the route slice.
	MoreRequests      int           // Value to set GCPercent. influences the garbage collector performance. 0-1000
	ReadTimeout       time.Duration // Maximum duration for reading the entire request.
	WriteTimeout      time.Duration // Maximum duration before timing out writes of the response.
	IdleTimeout       time.Duration // Maximum amount of time to wait for the next request when keep-alives are enabled.
	ReadHeaderTimeout time.Duration // Amount of time allowed to read request headers.
	GCPercent         int           // Renamed to be more descriptive (0-1000) - influences the garbage collector performance.
	TLSConfig         *tls.Config   // Integrated TLS configuration
	CorsConfig        *CorsConfig   // Specific type for CORS
	Views             template.TemplateEngine

	NoBanner bool // Flag to disable the Quick startup Display.
}

Config defines various configuration options for the Quick server

func GetDefaultConfig ΒΆ

func GetDefaultConfig() Config

GetDefaultConfig returns the default configuration pre-defined for the system.

This function provides a standardized configuration setup, ensuring that new instances use a consistent and optimized set of defaults.

Returns:

  • Config: A struct containing the default system configuration.

Example Usage:

// This function is typically used when initializing a new Quick instance
// to ensure it starts with the default settings if no custom config is provided.
Example ΒΆ

This function is named ExampleGetDefaultConfig() it with the Examples type.

// Get the default configuration settings
result := GetDefaultConfig()

// Print individual configuration values
fmt.Printf("BodyLimit: %d\n", result.BodyLimit)           // Maximum request body size
fmt.Printf("MaxBodySize: %d\n", result.MaxBodySize)       // Maximum allowed body size for requests
fmt.Printf("MaxHeaderBytes: %d\n", result.MaxHeaderBytes) // Maximum size for request headers
fmt.Printf("RouteCapacity: %d\n", result.RouteCapacity)   // Maximum number of registered routes
fmt.Printf("MoreRequests: %d\n", result.MoreRequests)     // Maximum concurrent requests allowed
Output:

BodyLimit: 2097152
MaxBodySize: 2097152
MaxHeaderBytes: 1048576
RouteCapacity: 1000
MoreRequests: 290

type ContextBuilder ΒΆ

type ContextBuilder struct {
	// contains filtered or unexported fields
}

func (*ContextBuilder) Bool ΒΆ

func (cb *ContextBuilder) Bool(key string, value bool) *ContextBuilder

Bool adds a boolean value to the context as a string and returns the builder for chaining.

Parameters:

  • key: Context key name
  • value: Boolean value to store (converted to "true" or "false")

Usage:

c.SetContext().Bool("authenticated", true).Bool("admin", false)

func (*ContextBuilder) Int ΒΆ

func (cb *ContextBuilder) Int(key string, value int) *ContextBuilder

Int adds an integer value to the context as a string and returns the builder for chaining.

Parameters:

  • key: Context key name
  • value: Integer value to store (converted to string)

Usage:

c.SetContext().Int("userID", 12345).Int("attempts", 3)

func (*ContextBuilder) Str ΒΆ

func (cb *ContextBuilder) Str(key, value string) *ContextBuilder

Str adds a string value to the context and returns the builder for chaining.

Parameters:

  • key: Context key name
  • value: String value to store

Usage:

c.SetContext().Str("service", "user-service").Str("function", "createUser")

type CorsConfig ΒΆ

type CorsConfig struct {
	Enabled  bool              // If true, enables CORS support
	Options  map[string]string // Custom CORS options
	AllowAll bool              // If true, allows all origins
}

CorsConfig defines the CORS settings for Quick

type Ctx ΒΆ

type Ctx struct {
	Response http.ResponseWriter // HTTP response writer to send responses
	Request  *http.Request       // Incoming HTTP request object

	MoreRequests int // Counter for batch processing requests

	JsonStr string              // Request body as a string (for JSON handling)
	Headers map[string][]string // Map of request headers
	Params  map[string]string   // Map of URL parameters
	Query   map[string]string   // Map of query parameters

	App *Quick // Reference to the Quick application instance

	Context context.Context // custom context
	// contains filtered or unexported fields
}

Ctx represents the context of an HTTP request and response.

func (*Ctx) Accepts ΒΆ

func (c *Ctx) Accepts(acceptType string) *Ctx

Accepts sets the "Accept" header in the HTTP response.

This function assigns a specific accept type to the HTTP response header "Accept."

Parameters:

  • acceptType: The MIME type to set in the "Accept" header.

Returns:

  • *Ctx: The current context instance for method chaining.
Example ΒΆ

This function is named ExampleCtx_Accepts() it with the Examples type.

q := New()

q.Get("/accepts", func(c *Ctx) error {
	// Set Accept header
	c.Accepts("application/json")
	return c.String("Accept Set")
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/accepts",
})

if err := res.AssertString("Accept Set"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.Response().Header.Get("Accept"))

fmt.Println(res.BodyStr())
Output:

application/json
Accept Set

func (*Ctx) Add ΒΆ

func (c *Ctx) Add(key, value string)

Add defines an HTTP header in the response.

This function sets the specified HTTP response header to the provided value.

Parameters:

  • key: The name of the HTTP header to set.
  • value: The value to assign to the header.

func (*Ctx) Append ΒΆ

func (c *Ctx) Append(key, value string)

Append adds a value to an HTTP response header.

This function appends a new value to an existing HTTP response header.

Parameters:

  • key: The name of the HTTP header.
  • value: The value to append to the header.
Example ΒΆ

This function is named ExampleCtx_Append() it with the Examples type.

q := New()

q.Get("/append-header", func(c *Ctx) error {
	// Append multiple values to a custom header
	c.Append("X-Custom-Header", "Quick")
	return c.String("Header Appended")
})

// Simulate a GET request and retrieve XML response
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/append-header",
})

if err := res.AssertString("Header Appended"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.Response().Header.Get("X-Custom-Header"))

fmt.Println(res.BodyStr())
Output:

Quick
Header Appended

func (*Ctx) Bind ΒΆ

func (c *Ctx) Bind(v interface{}) (err error)

Bind parses and binds the request body to a Go struct.

This function extracts and maps the request body content to the given struct (v). It supports various content types and ensures proper deserialization.

Parameters:

  • v: A pointer to the structure where the request body will be bound.

Returns:

  • error: An error if parsing fails or if the structure is incompatible with the request body.
Example ΒΆ

This function is named ExampleCtx_Bind() it with the Examples type.

q := New()

q.Post("/bind", func(c *Ctx) error {
	// Define a struct to map the JSON request body
	var data struct {
		Name string `json:"name"`
		Age  int    `json:"age"`
	}

	// Parse JSON body into struct
	err := c.Bind(&data)
	if err != nil {
		fmt.Println("Error in Bind:", err)
		return err
	}

	// Print extracted data in desired format
	fmt.Printf("%s %d\n", data.Name, data.Age)
	return nil
})

// Simulate a POST request with JSON data
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodPost,
	URI:    "/bind",
	Headers: map[string]string{
		"Content-Type": "application/json",
	},
	Body: []byte(`{"name": "Quick", "age": 30}`),
})

fmt.Println(res.BodyStr())
Output:

Quick 30

func (*Ctx) Body ΒΆ

func (c *Ctx) Body() []byte

Body retrieves the request body as a byte slice.

This function returns the raw request body as a slice of bytes ([]byte).

Returns:

  • []byte: The request body in its raw byte form.
Example ΒΆ

This function is named ExampleCtx_Body() it with the Examples type.

q := New()

// Create a new context with a simulated request body
q.Post("/body", func(c *Ctx) error {
	// Access the raw body
	body := c.Body()
	fmt.Println(string(body))
	return c.Status(200).String("OK")
})

res, _ := q.Qtest(QuickTestOptions{
	Method: MethodPost,
	URI:    "/body",
	Headers: map[string]string{
		"Content-Type": "application/json",
	},
	Body: []byte(`{"name": "Quick", "age": 28}`),
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}
Output:

{"name": "Quick", "age": 28}

func (*Ctx) BodyParser ΒΆ

func (c *Ctx) BodyParser(v interface{}) error

BodyParser efficiently unmarshals the request body into the provided struct (v) based on the Content-Type header.

Supported content-types: - application/json - application/xml, text/xml

Parameters:

  • v: The target structure to decode the request body into.

Returns:

  • error: An error if decoding fails or if the content-type is unsupported.
Example ΒΆ

This function is named ExampleCtx_BodyParser() it with the Examples type.

q := New()

q.Post("/parse", func(c *Ctx) error {
	// Define a struct for JSON parsing
	var data struct {
		Name string `json:"name"`
		Age  int    `json:"age"`
	}

	// Parse request body into the struct
	err := c.BodyParser(&data)
	if err != nil {
		fmt.Println("Erro ao analisar o corpo:", err)
		return err
	}

	// Print parsed data
	fmt.Printf("%s %d\n", data.Name, data.Age)
	return nil
})

// Simulate a POST request with JSON data
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodPost,
	URI:    "/parse",
	Headers: map[string]string{
		"Content-Type": "application/json",
	},
	Body: []byte(`{"name": "Quick", "age": 28}`),
})

fmt.Println(res.BodyStr())
Output:

Quick 28

func (*Ctx) BodyString ΒΆ

func (c *Ctx) BodyString() string

BodyString retrieves the request body as a string.

This function converts the request body from a byte slice into a string format.

Returns:

  • string: The request body as a string.
Example ΒΆ

This function is named ExampleCtx_Body() it with the Examples type.

q := New()

q.Post("/bodyString", func(c *Ctx) error {
	// Access the raw body
	bodyStr := c.BodyString()
	fmt.Println(bodyStr)
	return c.Status(200).String("OK")
})

res, _ := q.Qtest(QuickTestOptions{
	Method: MethodPost,
	URI:    "/bodyString",
	Headers: map[string]string{
		"Content-Type": "application/json",
	},
	Body: []byte(`{"name": "Quick", "age": 28}`),
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}
Output:

{"name": "Quick", "age": 28}

func (*Ctx) Byte ΒΆ

func (c *Ctx) Byte(b []byte) (err error)

Byte writes a byte slice to the HTTP response.

This function writes raw bytes to the response body using writeResponse().

Parameters:

  • b: The byte slice to be written.

Returns:

  • error: An error if the response write operation fails.
Example ΒΆ

This function is named ExampleCtx_Byte() it with the Examples type.

q := New()

q.Get("/byte", func(c *Ctx) error {
	// Send raw byte array in response
	return c.Byte([]byte("Hello, Quick!"))
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/byte",
})

if err := res.AssertString("Hello, Quick!"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Hello, Quick!

func (*Ctx) Ctx ΒΆ

func (c *Ctx) Ctx() context.Context

Ctx returns the active context for this request. It returns the custom context if SetCtx was used; otherwise defaults to Request.Context()

func (*Ctx) Del ΒΆ

func (c *Ctx) Del(key string)

func (*Ctx) File ΒΆ

func (c *Ctx) File(filePath string) error

File serves a specific file to the client.

This function trims any trailing "/*" from the provided file path, checks if it is a directory, and serves "index.html" if applicable. If the file exists, it is sent as the response.

Parameters:

  • filePath: The path to the file to be served.

Returns:

  • error: Always returns nil, as `http.ServeFile` handles errors internally.
Example ΒΆ

This function is named ExampleCtx_File() it with the Examples type.

// Creating a Quick instance
q := New()

// Defining a route that serves a specific file
q.Get("/file", func(c *Ctx) error {
	return c.File("./test_uploads/quick.txt") // Serves an existing file
})

// Simulating a request to test the route
// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/file",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

fmt.Println("Status:", res.StatusCode())
Output:

Status: 200

func (*Ctx) FormFile ΒΆ

func (c *Ctx) FormFile(fieldName string) (*UploadedFile, error)

FormFile processes an uploaded file and returns its details.

This function retrieves the first uploaded file for the specified form field.

Parameters:

  • fieldName: The name of the form field containing the uploaded file.

Returns:

  • *UploadedFile: A struct containing the uploaded file details.
  • error: An error if no file is found or the retrieval fails.
Example ΒΆ

This function is named ExampleCtx_FormFile() it with the Examples type.

// Simulated uploaded file
uploadedFile := &UploadedFile{
	Info: FileInfo{
		Filename:    "quick.txt",
		Size:        1024,
		ContentType: "text/plain",
		Bytes:       []byte("File content"),
	},
}

// Mocking the FormFiles function externally
mockFormFiles := func(fieldName string) ([]*UploadedFile, error) {
	if fieldName == "file" {
		return []*UploadedFile{uploadedFile}, nil
	}
	return nil, errors.New("file not found")
}

// Calling the mocked function instead of modifying `ctx`
files, err := mockFormFiles("file")

// Handling the result
if err != nil {
	fmt.Println("Error:", err)
} else if len(files) > 0 {
	fmt.Println("Received file:", files[0].FileName())
}
Output:

Received file: quick.txt

func (*Ctx) FormFileLimit ΒΆ

func (c *Ctx) FormFileLimit(limit string) error

FormFileLimit sets the maximum allowed upload size.

This function configures the maximum file upload size for multipart form-data requests.

Parameters:

  • limit: A string representing the maximum file size (e.g., "10MB").

Returns:

  • error: An error if the limit value is invalid.
Example ΒΆ

This function is named ExampleCtx_FormFileLimit() it with the Examples type.

// Creating a new context
ctx := &Ctx{}

// Setting a file upload limit to 5MB
err := ctx.FormFileLimit("5MB")

// Checking if an error occurred while setting the limit
if err != nil {
	fmt.Println("Error:", err)
} else {
	fmt.Println("Upload limit set to:", ctx.uploadFileSize)
}
Output:

Upload limit set to: 5242880

func (*Ctx) FormFiles ΒΆ

func (c *Ctx) FormFiles(fieldName string) ([]*UploadedFile, error)

FormFiles retrieves all uploaded files for the given field name.

This function extracts all files uploaded in a multipart form request.

Parameters:

  • fieldName: The name of the form field containing the uploaded files.

Returns:

  • []*UploadedFile: A slice containing details of the uploaded files.
  • error: An error if no files are found or the retrieval fails.
Example ΒΆ

This function is named ExampleCtx_FormFiles() it with the Examples type.

// Simulating multiple uploaded files
uploadedFiles := []*UploadedFile{
	{
		Info: FileInfo{
			Filename:    "file1.txt",
			Size:        1024,
			ContentType: "text/plain",
			Bytes:       []byte("File 1 content"),
		},
	},
	{
		Info: FileInfo{
			Filename:    "file2.txt",
			Size:        2048,
			ContentType: "text/plain",
			Bytes:       []byte("File 2 content"),
		},
	},
}

// Mocking the FormFiles function externally
mockFormFiles := func(fieldName string) ([]*UploadedFile, error) {
	if fieldName == "files" {
		return uploadedFiles, nil
	}
	return nil, errors.New("files not found")
}

// Calling the mocked function instead of modifying `ctx`
files, err := mockFormFiles("files")

// Handling the result
if err != nil {
	fmt.Println("Error:", err)
} else {
	fmt.Println("Received files:")
	for _, file := range files {
		fmt.Printf("- %s (%d bytes)\n", file.FileName(), file.Size())
	}
}
Output:

Received files:
- file1.txt (1024 bytes)
- file2.txt (2048 bytes)

func (*Ctx) FormValue ΒΆ

func (c *Ctx) FormValue(key string) string

FormValue retrieves a form value by its key.

This function parses the form data and returns the value of the specified field.

Parameters:

  • key: The name of the form field.

Returns:

  • string: The value of the requested field, or an empty string if not found.

func (*Ctx) FormValues ΒΆ

func (c *Ctx) FormValues() map[string][]string

FormValues retrieves all form values as a map.

This function parses the form data and returns all form values.

Returns:

  • map[string][]string: A map of form field names to their corresponding values.

func (*Ctx) Get ΒΆ

func (c *Ctx) Get(key string) string

Get retrieves a specific header value from the request.

Parameters:

  • key: The name of the header to retrieve.

Returns:

  • string: The value of the specified header, or an empty string if not found.

func (*Ctx) GetAllContextData ΒΆ

func (c *Ctx) GetAllContextData() map[string]any

GetAllContextData returns all accumulated context data.

Usage:

allData := c.GetAllContextData()
fmt.Printf("Context: %+v", allData)

func (*Ctx) GetHeader ΒΆ

func (c *Ctx) GetHeader(key string) string

GetHeader retrieves a specific header value from the request.

Parameters:

  • key: The name of the header to retrieve.

Returns:

  • string: The value of the specified header, or an empty string if not found.
Example ΒΆ

This function is named ExampleCtx_GetHeader() it with the Examples type.

q := New()

// Defining a route that serves a specific file
q.Get("/header", func(c *Ctx) error {
	// Retrieve the "User-Agent" header
	header := c.GetHeader("User-Agent")
	c.Set("User-Agent", header)
	fmt.Println(header)
	return nil
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method:  MethodGet,
	URI:     "/header",
	Headers: map[string]string{"User-Agent": "Go-Test-Agent"},
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

if err := res.AssertHeader("User-Agent", "Go-Test-Agent"); err != nil {
	fmt.Println("Header error:", err)
}

fmt.Println("Status:", res.StatusCode())
Output:

Go-Test-Agent
Status: 200

func (*Ctx) GetHeaders ΒΆ

func (c *Ctx) GetHeaders() http.Header

GetHeaders retrieves all headers from the incoming HTTP request.

This method provides direct access to the request headers, allowing middleware and handlers to inspect and modify header values.

Example Usage:

q.Get("/", func(c *quick.Ctx) error {
    headers := c.GetHeaders()
    return c.Status(200).JSON(headers)
})

Returns:

  • http.Header: A map containing all request headers.
Example ΒΆ

This function is named ExampleCtx_GetHeaders() it with the Examples type.

q := New()

q.Get("/headers", func(c *Ctx) error {
	// Retrieve all request headers
	headers := c.GetHeaders()

	c.Set("Content-Type", headers.Get("Content-Type"))
	c.Set("Accept", headers.Get("Accept"))

	// Print specific headers for demonstration
	fmt.Println(headers.Get("Content-Type")) // Expected output: "application/json"
	fmt.Println(headers.Get("Accept"))       // Expected output: "application/xml"
	return nil
})

res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/headers",
	Headers: map[string]string{
		"Content-Type": "application/json",
		"Accept":       "application/xml",
	},
})

if err := res.AssertHeader("Content-Type", "application/json"); err != nil {
	fmt.Println("Header error:", err)
}

if err := res.AssertHeader("Accept", "application/xml"); err != nil {
	fmt.Println("Header error:", err)
}

fmt.Println("Status:", res.StatusCode())
Output:

application/json
application/xml
Status: 200

func (*Ctx) GetHeadersAll ΒΆ

func (c *Ctx) GetHeadersAll() map[string][]string

GetHeadersAll returns all HTTP response headers stored in the context.

Returns:

  • map[string][]string: A map containing all response headers with their values.
Example ΒΆ

This function is named ExampleCtx_GetHeadersAll() it with the Examples type.

q := New()

q.Get("/headers", func(c *Ctx) error {
	// Retrieve all headers from the request
	headers := c.GetHeadersAll()
	fmt.Println(headers["Content-Type"]) // Expected: application/json
	fmt.Println(headers["Accept"])       // Expected: application/xml
	return nil
})

// Simulate a GET request with headers
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/headers",
	Headers: map[string]string{
		"Content-Type": "application/json",
		"Accept":       "application/xml"},
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}
Output:

[application/json]
[application/xml]

func (*Ctx) GetReqHeadersAll ΒΆ

func (c *Ctx) GetReqHeadersAll() map[string][]string

QueryParam retrieves a query parameter value from the URL.

Parameters:

  • key: The name of the query parameter to retrieve.

Returns:

  • string: The value of the specified query parameter, or an empty string if not found.
Example ΒΆ

This function is named ExampleCtx_GetReqHeadersAll() it with the Examples type.

q := New()

q.Get("/headers", func(c *Ctx) error {
	// Retrieve all request headers
	headers := c.GetReqHeadersAll()

	// Print specific headers for demonstration
	fmt.Println(headers["Content-Type"]) // Expected: application/json
	fmt.Println(headers["Accept"])       // Expected: application/xml
	return nil
})

// Simulate a GET request with headers
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/headers",
	Headers: map[string]string{
		"Content-Type": "application/json",
		"Accept":       "application/xml"},
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

fmt.Println("Status:", res.StatusCode())
Output:

[application/json]
[application/xml]
Status: 200

func (*Ctx) GetTraceID ΒΆ

func (c *Ctx) GetTraceID(nameTraceID string) (traceID string)

GetTraceID retrieves or generates a trace ID for the current request.

This method first attempts to get an existing trace ID from the request headers. If no trace ID is found, it automatically generates a new one using a random trace ID generator. This ensures that every request has a unique trace ID for distributed tracing purposes.

Parameters:

  • nameTraceID: The header name to look for the trace ID (e.g., "X-Trace-ID", "Trace-ID")

Returns:

  • traceID: The existing trace ID from headers, or a newly generated one if none exists

This method is typically used at the beginning of request processing to ensure traceability across your application stack. The generated trace ID can then be used with SetTraceContext or forwarded to downstream services.

Usage:

func myHandler(c *quick.Ctx) error {
    traceID := c.GetTraceID("X-Trace-ID")
    c.SetTraceContext("X-Trace-ID", traceID, "user-service", "createUser")

    // Continue with your handler logic
   return c.Status(200).String(traceID)
}

func (*Ctx) HTML ΒΆ

func (c *Ctx) HTML(name string, data interface{}, layouts ...string) error

func (*Ctx) Host ΒΆ

func (c *Ctx) Host() string

Host returns the host name from the HTTP request.

This method extracts the host from `c.Request.Host`. If the request includes a port number (e.g., "localhost:3000"), it returns the full host including the port.

Example Usage:

q.Get("/", func(c *quick.Ctx) error {
    return c.Status(200).SendString("Host: " + c.Host())
})

Returns:

  • string: The host name from the request.

func (*Ctx) IP ΒΆ

func (c *Ctx) IP() string

IP is alias RemoteIP

func (*Ctx) JSON ΒΆ

func (c *Ctx) JSON(v interface{}) error

JSON encodes the provided interface (v) as JSON, sets the Content-Type header, and writes the response efficiently using buffer pooling.

Parameters:

  • v: The data structure to encode as JSON.

Returns:

  • error: An error if JSON encoding fails or if writing the response fails.
Example ΒΆ

This function is named ExampleCtx_JSON() it with the Examples type.

q := New()

q.Get("/json", func(c *Ctx) error {
	// Define JSON response data
	data := map[string]string{"message": "Hello, Quick!"}
	return c.JSON(data)
})

// Simulate a GET request and retrieve JSON response
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/json",
	Headers: map[string]string{
		"Content-Type": "application/json",
	},
})

if err := res.AssertString("{\"message\":\"Hello, Quick!\"}"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.BodyStr())
Output:

{"message":"Hello, Quick!"}

func (*Ctx) JSONIN ΒΆ

func (c *Ctx) JSONIN(v interface{}, params ...string) error

JSONIN encodes the given interface as JSON with indentation and writes it to the HTTP response. Allows optional parameters to define the indentation format.

ATTENTION use only for debugging, very slow

Parameters:

  • v: The data structure to encode as JSON.
  • params (optional): Defines the indentation settings.
  • If params[0] is provided, it will be used as the prefix.
  • If params[1] is provided, it will be used as the indentation string.

Returns:

  • error: An error if JSON encoding fails or if writing to the ResponseWriter fails.
Example ΒΆ

This function is named ExampleCtx_JSONIN() it with the Examples type.

// Creating a Quick instance
q := New()

// Defining a route that serves a specific file
q.Get("/json", func(c *Ctx) error {
	data := map[string]string{"message": "Hello, Quick!"}
	return c.JSONIN(data)
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/json",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

if err := res.AssertString("{\n  \"message\": \"Hello, Quick!\"\n}\n"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println("Status:", res.StatusCode())

fmt.Println("Body:", res.BodyStr())
Output:

Status: 200
Body: {
  "message": "Hello, Quick!"
}

func (*Ctx) Logger ΒΆ

func (c *Ctx) Logger() *ContextBuilder

Logger creates a new context builder for adding key-value pairs.

Usage:

c.Logger().Str("service", "user-service").Int("userID", 123)

func (*Ctx) Method ΒΆ

func (c *Ctx) Method() string

Method retrieves the HTTP method of the current request.

This method returns the HTTP method as a string, such as "GET", "POST", "PUT", "DELETE", etc. It is useful for middleware and route handlers to differentiate between request types.

Example Usage:

q.Use(func(c *quick.Ctx) error {
    if c.Method() == "POST" {
        return c.Status(403).SendString("POST requests are not allowed")
    }
    return c.Next()
})

Returns:

  • string: The HTTP method (e.g., "GET", "POST", "PUT").
Example ΒΆ

This function is named ExampleCtx_Method() it with the Examples type.

q := New()

q.Post("/method", func(c *Ctx) error {
	fmt.Println(c.Method()) // Expected output: "POST"
	return nil
})

// Simulate a POST request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodPost,
	URI:    "/method",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}
fmt.Println(res.BodyStr())
Output:

POST

func (*Ctx) MultipartForm ΒΆ

func (c *Ctx) MultipartForm() (*multipart.Form, error)

MultipartForm provides access to the raw multipart form data.

This function parses and retrieves the multipart form from the request.

Returns:

  • *multipart.Form: A pointer to the multipart form data.
  • error: An error if parsing fails.
Example ΒΆ

This function is named ExampleCtx_MultipartForm() it with the Examples type.

// Create a multipart/form-data body using a buffer
var body bytes.Buffer
writer := multipart.NewWriter(&body)

// Add a sample form field "username" with value "quickuser"
_ = writer.WriteField("username", "quickuser")

// Close the writer to finalize the form body (writes the boundary end)
writer.Close()

// Create a new Quick context manually
ctx := &Ctx{}

// Construct a fake HTTP request with method POST and the correct headers
ctx.Request = &http.Request{
	Method: "POST",
	Header: http.Header{
		// Set the Content-Type to multipart/form-data with the proper boundary
		"Content-Type": []string{writer.FormDataContentType()},
	},
	Body:          http.NoBody, // Temporary placeholder, will be replaced below
	ContentLength: int64(body.Len()),
}

// Set the actual body (must be an io.ReadCloser)
ctx.Request.Body = io.NopCloser(&body)

// Attempt to parse the multipart form from the request
form, err := ctx.MultipartForm()

// Print the result
if err != nil {
	fmt.Println("Error processing form:", err)
} else {
	fmt.Println("Form processed successfully:", form.Value)
}
Output:

Form processed successfully: map[username:[quickuser]]

func (*Ctx) Next ΒΆ

func (c *Ctx) Next() error

Next executes the next handler in the chain.

If no more handlers are available and no response has been written, it automatically sends a 404 response.

Usage:

func myMiddleware(c *quick.Ctx) error {
    err := c.Next()
    return err
}
Example ΒΆ

This function is named ExampleCtx_Next() it with the Examples type.

q := New()

// Middleware that uses Next
q.Use(func(c *Ctx) error {
	err := c.Next()
	return err
})

// Final route handler
q.Get("/", func(c *Ctx) error {
	fmt.Println("Quick")
	return c.String("done")
})

// Simulate request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/",
})

// Ensure response is what we expect
if err := res.AssertString("done"); err != nil {
	fmt.Println("Body error:", err)
}

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}
Output:

Quick

func (*Ctx) OriginalURI ΒΆ

func (c *Ctx) OriginalURI() string

OriginalURI retrieves the original request URI sent by the client.

This method returns the full unprocessed request URI, including the path and optional query string, exactly as it was sent by the client. It can be useful for logging, debugging, or routing decisions that depend on the raw URI.

Example Usage:

q.Use(func(c *quick.Ctx) error {
    uri := c.OriginalURI()
    if strings.HasPrefix(uri, "/admin") {
        return c.Status(401).SendString("Unauthorized access to admin area")
    }
    return c.Next()
})

Returns:

  • string: The raw request URI (e.g., "/api/v1/resource?id=123").

func (*Ctx) Param ΒΆ

func (c *Ctx) Param(key string) string

Param retrieves the value of a URL parameter corresponding to the given key.

This function searches for a parameter in the request's URL path and returns its value. If the parameter is not found, an empty string is returned.

Parameters:

  • key: The name of the URL parameter to retrieve.

Returns:

  • string: The value of the requested parameter or an empty string if not found.
Example ΒΆ

This function is named ExampleCtx_Param() it with the Examples type.

q := New()

q.Get("/user/:id", func(c *Ctx) error {
	// Retrieve "id" parameter from the URL path
	id := c.Param("id")
	return c.SendString(id)
})

// Simulate a GET request with a path parameter
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/user/42",
	Headers: map[string]string{
		"Content-Type": "application/json",
	},
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

fmt.Println(res.BodyStr())
Output:

42

func (*Ctx) Path ΒΆ

func (c *Ctx) Path() string

Path retrieves the URL path of the incoming HTTP request.

This method extracts the path component from the request URL, which is useful for routing and request handling.

Example Usage:

q.Get("/info", func(c *quick.Ctx) error {
    return c.Status(200).SendString("Requested Path: " + c.Path())
})

Returns:

  • string: The path component of the request URL (e.g., "/v1/user").
Example ΒΆ

This function is named ExampleCtx_Path() it with the Examples type.

q := New()

q.Get("/path/to/resource", func(c *Ctx) error {
	fmt.Println(c.Path()) // Expected output: "/path/to/resource"
	return nil
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/path/to/resource",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

fmt.Println(res.BodyStr())
Output:

/path/to/resource

func (*Ctx) QueryParam ΒΆ

func (c *Ctx) QueryParam(key string) string

QueryParam retrieves a query parameter value from the URL.

Parameters:

  • key: The name of the query parameter to retrieve.

Returns:

  • string: The value of the specified query parameter, or an empty string if not found.
Example ΒΆ

This function is named ExampleCtx_QueryParam() it with the Examples type.

q := New()

q.Get("/search", func(c *Ctx) error {
	fmt.Println(c.QueryParam("query")) // Expected output: "quick"
	return nil
})

// Simulate a GET request with query parameters
res, _ := q.Qtest(QuickTestOptions{
	Method:      MethodGet,
	URI:         "/search?query=quick",
	QueryParams: map[string]string{"query": "quick"},
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

fmt.Println(res.BodyStr())
Output:

quick

func (*Ctx) Redirect ΒΆ

func (c *Ctx) Redirect(location string, code ...int) error

Redirect sends an HTTP redirect response to the client.

It sets the "Location" header to the given URL and returns a plain text message indicating the redirection. By default, it uses HTTP status 302 (Found), but an optional custom status code can be provided (e.g., 301, 307, 308).

Example usage:

c.Redirect("https://quick.com")
c.Redirect("/new-path", 301)
Example ΒΆ

This function is named ExampleCtx_Redirect() it with the Examples type.

q := New()

q.Get("/old-path", func(c *Ctx) error {
	return c.Redirect("/new-path")
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/old-path",
})

if err := res.AssertStatus(StatusFound); err != nil {
	fmt.Println("Status error:", err)
}

fmt.Println("Location header:", res.Response().Header.Get("Location"))
fmt.Println("Status:", res.StatusCode())
fmt.Println("Body:", res.BodyStr())
Output:

Location header: /new-path
Status: 302
Body: Redirecting to /new-path

func (*Ctx) RemoteIP ΒΆ

func (c *Ctx) RemoteIP() string

RemoteIP extracts the client's IP address from the request.

If the request's `RemoteAddr` contains a port (e.g., "192.168.1.100:54321"), this method extracts only the IP part. If extraction fails, it returns the full `RemoteAddr` as a fallback.

Example Usage:

q.Get("/", func(c *quick.Ctx) error {
    return c.Status(200).SendString("Client IP: " + c.RemoteIP())
})

Returns:

  • string: The client's IP address. If extraction fails, returns `RemoteAddr`.
Example ΒΆ

This function is named ExampleCtx_RemoteIP() it with the Examples type.

q := New()

q.Get("/ip", func(c *Ctx) error {
	// Retrieve the client's IP address
	clientIP := c.RemoteIP()

	// Print the IP address for demonstration purposes
	fmt.Println(clientIP)
	return nil
})

// Simulate a GET request setting a fixed IP in RemoteAddr
req := httptest.NewRequest("GET", "/ip", nil)
req.RemoteAddr = "192.168.1.100:54321" // Setting a fixed IP for testing
rec := httptest.NewRecorder()

// Serve the request
q.ServeHTTP(rec, req)

// Capture and print the response
fmt.Println(strings.TrimSpace(rec.Body.String()))
Output:

192.168.1.100

func (*Ctx) Reset ΒΆ

func (c *Ctx) Reset(w http.ResponseWriter, r *http.Request)

Reset clears Ctx data for safe reuse

func (*Ctx) Send ΒΆ

func (c *Ctx) Send(b []byte) (err error)

Send writes a byte slice to the HTTP response.

This function writes raw bytes to the response body using writeResponse().

Parameters:

  • b: The byte slice to be written.

Returns:

  • error: An error if the response write operation fails.
Example ΒΆ

This function is named ExampleCtx_Send() it with the Examples type.

q := New()

q.Get("/send", func(c *Ctx) error {
	// Send raw bytes in response
	return c.Send([]byte("Hello, Quick!"))
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/send",
})

if err := res.AssertString("Hello, Quick!"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Hello, Quick!

func (*Ctx) SendFile ΒΆ

func (c *Ctx) SendFile(file []byte) error

SendFile writes a file to the HTTP response as a byte slice.

This function writes the provided byte slice (representing a file) to the response body.

Parameters:

  • file: The file content as a byte slice.

Returns:

  • error: An error if the response write operation fails.
Example ΒΆ

This function is named ExampleCtx_SendFile() it with the Examples type.

q := New()

q.Get("/sendfile", func(c *Ctx) error {
	// Simulate sending a file as a response
	fileContent := []byte("file contents")
	return c.SendFile(fileContent)
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/sendfile",
})

if err := res.AssertString("file contents"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.BodyStr())
Output:

file contents

func (*Ctx) SendString ΒΆ

func (c *Ctx) SendString(s string) error

SendString writes a string to the HTTP response.

This function converts the given string into a byte slice and writes it to the response body.

Parameters:

  • s: The string to be written.

Returns:

  • error: An error if the response write operation fails.
Example ΒΆ

This function is named ExampleCtx_SendString() it with the Examples type.

q := New()

q.Get("/sendstring", func(c *Ctx) error {
	// Send string response
	return c.SendString("Hello, Quick!")
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/sendstring",
})

if err := res.AssertString("Hello, Quick!"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.BodyStr())
Output:

	Hello, Quick!

func (*Ctx) Set ΒΆ

func (c *Ctx) Set(key, value string)

Set defines an HTTP header in the response.

This function sets the specified HTTP response header to the provided value.

Parameters:

  • key: The name of the HTTP header to set.
  • value: The value to assign to the header.
Example ΒΆ

This function is named ExampleCtx_Set() it with the Examples type.

q := New()

q.Get("/set-header", func(c *Ctx) error {
	// Set a custom response header
	c.Set("X-Custom-Header", "Quick")
	return c.String("Header Set")
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/set-header",
})

if err := res.AssertString("Header Set"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.Response().Header.Get("X-Custom-Header"))

fmt.Println(res.BodyStr())
Output:

Quick
Header Set

func (*Ctx) SetContext ΒΆ

func (c *Ctx) SetContext() *ContextBuilder

SetContext creates a new context builder for adding key-value pairs.

Usage:

c.SetContext().Str("service", "user-service").Int("userID", 123)

func (*Ctx) SetCtx ΒΆ

func (c *Ctx) SetCtx(ctx context.Context)

SetCtx allows you to override the default context used in c.Ctx()

func (*Ctx) SetStatus ΒΆ

func (c *Ctx) SetStatus(status int)

SetStatus sets the HTTP response status code.

Parameters:

  • status: The HTTP status code to be set.

func (*Ctx) SetTraceID ΒΆ

func (c *Ctx) SetTraceID(key, val string) *ContextBuilder

SetTraceID sets a trace ID header and adds it to the context.

Parameters:

  • key: Header name (e.g., "X-Trace-ID")
  • val: Trace ID value

Returns a ContextBuilder for chaining additional context data.

Usage:

c.SetTraceID("X-Trace-ID", traceID).Str("service", "user-service")

func (*Ctx) Status ΒΆ

func (c *Ctx) Status(status int) *Ctx

Status sets the HTTP status code of the response.

This function assigns a specific HTTP status code to the response.

Parameters:

  • status: The HTTP status code to set.

Returns:

  • *Ctx: The current context instance for method chaining.
Example ΒΆ

This function is named ExampleCtx_Status() it with the Examples type.

q := New()

q.Get("/status", func(c *Ctx) error {
	// Set status code to 404
	c.Status(404)
	return c.String("Not Found")
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/status",
})

if err := res.AssertString("Not Found"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.Response().StatusCode)
Output:

404

func (*Ctx) String ΒΆ

func (c *Ctx) String(s string) error

String writes a string to the HTTP response.

This function converts the given string into a byte slice and writes it to the response body.

Parameters:

  • s: The string to be written.

Returns:

  • error: An error if the response write operation fails.
Example ΒΆ

This function is named ExampleCtx_String() it with the Examples type.

q := New()

q.Get("/string", func(c *Ctx) error {
	// Return a simple string response
	return c.String("Hello, Quick!")
})

// Simulate a GET request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/string",
})

if err := res.AssertString("Hello, Quick!"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Hello, Quick!

func (*Ctx) XML ΒΆ

func (c *Ctx) XML(v interface{}) error

XML serializes the given value to XML and writes it to the HTTP response. It avoids unnecessary memory allocations by using buffer pooling and ensures that no extra newline is appended.

Parameters:

  • v: The data structure to encode as XML.

Returns:

  • error: An error if XML encoding fails or if writing to the ResponseWriter fails.
Example ΒΆ

This function is named ExampleCtx_XML() it with the Examples type.

q := New()

q.Get("/xml", func(c *Ctx) error {
	type XMLResponse struct {
		XMLName xml.Name `xml:"message"`
		Text    string   `xml:",chardata"`
	}

	data := XMLResponse{Text: "Hello, Quick!"}
	xmlBytes, err := xml.Marshal(data)
	if err != nil {
		return c.Status(500).String("Failed to marshal XML")
	}

	c.Set("Content-Type", "application/xml")
	return c.Status(200).Send(xmlBytes)
})

// Simulate a GET request and retrieve XML response
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/xml",
	Headers: map[string]string{
		"Accept": "application/xml",
	},
})

if err := res.AssertString(`<message>Hello, Quick!</message>`); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.BodyStr())
Output:

<message>Hello, Quick!</message>

type Error ΒΆ

type Error struct {
	Message string `json:"message"` // Error message to be returned to the client.
	Code    int    `json:"code"`    // HTTP status code associated with the error.
}

Error represents a custom HTTP error that can be returned from a handler. It implements the error interface and is designed to work seamlessly with Quick's global error handler.

Example usage:

return quick.NewError(404, "Resource not found")

func NewError ΒΆ

func NewError(code int, message ...string) *Error

NewError creates a new instance of Error with the specified HTTP status code. An optional custom message can be provided. If no message is provided, the default HTTP status text for the given code will be used.

Example usage:

err := quick.NewError(400, "Invalid input")
err := quick.NewError(500) // Uses "Internal Server Error" as message

func (*Error) Error ΒΆ

func (e *Error) Error() string

Error implements the error interface for the Error type. It returns the error message.

type FileInfo ΒΆ

type FileInfo struct {
	Filename    string
	Size        int64
	ContentType string
	Bytes       []byte
}

FileInfo contains metadata about an uploaded file.

Fields:

  • Filename: The original name of the uploaded file.
  • Size: The file size in bytes.
  • ContentType: The MIME type of the file (e.g., "image/png").
  • Bytes: The raw file content as a byte slice.

type Group ΒΆ

type Group struct {
	// contains filtered or unexported fields
}

Group represents a collection of routes that share a common prefix.

Fields:

  • prefix: The URL prefix shared by all routes in the group.
  • routes: A list of registered routes within the group.
  • middlewares: A list of middleware functions applied to the group.
  • quick: A reference to the Quick router.

func (*Group) Delete ΒΆ

func (g *Group) Delete(pattern string, handlerFunc HandleFunc)

Delete registers a new DELETE route.

Parameters:

  • pattern: The route pattern.
  • handlerFunc: The function handling the request.

Example:

g.Delete("/users/:id", deleteUserHandler)
Example ΒΆ

This function is named ExampleGroup_Delete() it with the Examples type.

q := New()

// Create a route group with prefix "/api"
apiGroup := q.Group("/api")

// Define a DELETE route inside the group
apiGroup.Delete("/users/:id", func(c *Ctx) error {
	// Return a success message
	return c.Status(200).String("User deleted")
})

// Simulate a DELETE request to "/api/users/42"
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodDelete,
	URI:    "/api/users/42",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

if err := res.AssertString("User deleted"); err != nil {
	fmt.Println("Body error:", err)
}

// Print the response body
fmt.Println(res.BodyStr())
Output:

User deleted

func (*Group) Get ΒΆ

func (g *Group) Get(pattern string, handlerFunc HandleFunc)

Get registers a new GET route.

Parameters:

  • pattern: The route pattern.
  • handlerFunc: The function handling the request.

Example:

g.Get("/users", listUsersHandler)
Example ΒΆ

This function is named ExampleGroup_Get() it with the Examples type.

q := New()

// Create a route group with prefix "/api"
apiGroup := q.Group("/api")

// Define a GET route inside the group
apiGroup.Get("/users", func(c *Ctx) error {
	// Return a success message
	return c.Status(200).String("List of users")
})

// Simulate a GET request to "/api/users"
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/api/users",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

if err := res.AssertString("List of users"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.BodyStr())
Output:

List of users

func (*Group) Handle ΒΆ

func (g *Group) Handle(method, pattern string, handlerFunc HandleFunc, paramExtractor any)

Handle registers a new route dynamically.

Parameters:

  • method: The HTTP method (GET, POST, etc.).
  • pattern: The route pattern.
  • handlerFunc: The function handling the request.
  • paramExtractor: The function to extract parameters.

Example:

g.Handle("GET", "/users/:id", userHandler, extractParamsGet)

func (*Group) Options ΒΆ

func (g *Group) Options(pattern string, handlerFunc HandleFunc)

Options registers a new OPTIONS route.

Parameters:

  • pattern: The route pattern.
  • handlerFunc: The function handling the request.

Example:

g.Options("/users", optionsHandler)
Example ΒΆ

This function is named ExampleGroup_Options() it with the Examples type.

// Create a new Quick instance
q := New()

// Create a new group with a common prefix
api := q.Group("/api")

// Register an OPTIONS route dynamically
api.Options("/resource", func(c *Ctx) error {
	c.Set("Allow", "GET, POST, OPTIONS")
	return c.Status(204).Send(nil) // No Content response
})

// Simulate an OPTIONS request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodOptions,
	URI:    "/api/resource",
})

if err := res.AssertStatus(204); err != nil {
	fmt.Println("Status error:", err)
}

// Print the response status
fmt.Println("Status:", res.StatusCode())
Output:

Status: 204

func (*Group) Patch ΒΆ

func (g *Group) Patch(pattern string, handlerFunc HandleFunc)

Patch registers a new PATCH route.

Parameters:

  • pattern: The route pattern.
  • handlerFunc: The function handling the request.

Example:

g.Patch("/users/:id", partialUpdateHandler)
Example ΒΆ

This function is named ExampleGroup_Patch() it with the Examples type.

// Create a new Quick instance
q := New()

// Create a new group with a common prefix
api := q.Group("/api")

// Register a PATCH route dynamically
api.Patch("/update", func(c *Ctx) error {
	return c.Status(200).String("PATCH request received")
})

// Simulate a PATCH request
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodPatch,
	URI:    "/api/update",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

if err := res.AssertString("PATCH request received"); err != nil {
	fmt.Println("Body error:", err)
}

// Print the response body
fmt.Println(res.BodyStr())
Output:

PATCH request received

func (*Group) Post ΒΆ

func (g *Group) Post(pattern string, handlerFunc HandleFunc)

Post registers a new POST route.

Parameters:

  • pattern: The route pattern.
  • handlerFunc: The function handling the request.

Example:

g.Post("/users", createUserHandler)
Example ΒΆ

This function is named ExampleGroup_Post() it with the Examples type.

q := New()

// Create a route group with prefix "/api"
apiGroup := q.Group("/api")

// Define a POST route inside the group
apiGroup.Post("/users", func(c *Ctx) error {
	// Return a success message
	return c.Status(201).String("User created")
})

// Simulate a POST request to "/api/users"
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodPost,
	URI:    "/api/users",
})

if err := res.AssertStatus(201); err != nil {
	fmt.Println("Status error:", err)
}

if err := res.AssertString("User created"); err != nil {
	fmt.Println("Body error:", err)
}

// Print the response body
fmt.Println(res.BodyStr())
Output:

User created

func (*Group) Put ΒΆ

func (g *Group) Put(pattern string, handlerFunc HandleFunc)

Put registers a new PUT route.

Parameters:

  • pattern: The route pattern.
  • handlerFunc: The function handling the request.

Example:

g.Put("/users/:id", updateUserHandler)
Example ΒΆ

This function is named ExampleGroup_Put() it with the Examples type.

q := New()

// Create a route group with prefix "/api"
apiGroup := q.Group("/api")

// Define a PUT route inside the group
apiGroup.Put("/users/:id", func(c *Ctx) error {
	// Return a success message
	return c.Status(200).String("User updated")
})

// Simulate a PUT request to "/api/users/42"
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodPut,
	URI:    "/api/users/42",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

if err := res.AssertString("User updated"); err != nil {
	fmt.Println("Body error:", err)
}

// Print the response body
fmt.Println(res.BodyStr())
Output:

User updated

func (*Group) Use ΒΆ

func (g *Group) Use(mw func(http.Handler) http.Handler)

Use adds middleware to the group.

This function allows adding middleware to a specific group of routes. Middleware functions are executed **before** the route handlers, allowing for request modifications, logging, authentication, etc.

Parameters:

  • mw: A middleware function that modifies the HTTP handler.

Example Usage:

	q := quick.New()

 group := q.Group("/v1")

	group.Get("/user", func(c *quick.Ctx) error {
		return c.Status(200).SendString("[GET] [GROUP] /v1/user ok!!!")
	})
Example ΒΆ

This function is named ExampleGroup_Delete() it with the Examples type.

// Create a new Quick instance
q := New()

// Create a new group with a common prefix
api := q.Group("/api")

// Define a simple middleware that logs requests
logMiddleware := func(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Middleware activated for:", r.URL.Path)
		next.ServeHTTP(w, r)
	})
}

// Apply middleware to the group
api.Use(logMiddleware)

// Define a GET route inside the group
api.Get("/hello", func(c *Ctx) error {
	return c.Status(200).String("Hello from API group Quick")
})

// Simulate a request to test middleware activation
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/api/hello",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

if err := res.AssertString("Hello from API group Quick"); err != nil {
	fmt.Println("Body error:", err)
}

// Print the response body
fmt.Println(res.BodyStr())
Output:

Middleware activated for: /api/hello
Hello from API group Quick

type HandleFunc ΒΆ

type HandleFunc func(*Ctx) error

HandleFunc represents a function signature for route handlers in Quick.

This function type is used for defining request handlers within Quick's routing system. It receives a pointer to `Ctx`, which encapsulates request and response data.

Example Usage:

q.Get("/example", func(c *quick.Ctx) error {
    return c.Status(quick.StatusOK).SendString("Hello, Quick!")
})

type Handler ΒΆ

type Handler interface {
	// ServeQuick processes an HTTP request in the Quick framework.
	//
	// Parameters:
	//   - c *Ctx: The request context containing request and response details.
	//
	// Returns:
	//   - error: Any error encountered while processing the request.
	ServeQuick(*Ctx) error
}

Handler defines an interface that wraps the ServeQuick method.

Any type implementing `ServeQuick(*Ctx) error` can be used as a request handler in Quick. This abstraction allows for more flexible middleware and handler implementations, including struct-based handlers.

Example Usage:

type MyHandler struct{}

func (h MyHandler) ServeQuick(c *quick.Ctx) error {
    return c.Status(quick.StatusOK).SendString("Struct-based handler")
}

q.Use(MyHandler{})

type HandlerFunc ΒΆ

type HandlerFunc func(c *Ctx) error

HandlerFunc defines the function signature for request handlers in Quick.

This type provides a way to implement request handlers as standalone functions while still conforming to the `Handler` interface. It allows functions of type `HandlerFunc` to be passed as middleware or endpoint handlers.

Example Usage:

func myHandler(c *quick.Ctx) error {
    return c.Status(quick.StatusOK).SendString("HandlerFunc example")
}

q.Use(quick.HandlerFunc(myHandler))

func (HandlerFunc) ServeQuick ΒΆ

func (h HandlerFunc) ServeQuick(c *Ctx) error

ServeQuick allows a HandlerFunc to satisfy the Handler interface.

This method enables `HandlerFunc` to be used wherever a `Handler` is required by implementing the `ServeQuick` method.

Example Usage:

q.Use(quick.HandlerFunc(func(c *quick.Ctx) error {
    return c.Status(quick.StatusOK).SendString("Hello from HandlerFunc!")
}))

func (HandlerFunc) Server ΒΆ

func (h HandlerFunc) Server(c *Ctx) error

Serve allows a HandlerFunc to satisfy the Handler interface.

This method enables `HandlerFunc` to be used wherever a `Handler` is required by implementing the `Serve` method.

Example Usage:

q.Use(quick.HandlerFunc(func(c *quick.Ctx) error {
    return c.Status(quick.StatusOK).SendString("Hello from HandlerFunc!")
}))

type M ΒΆ

type M map[string]interface{}

M is a shortcut for map[string]interface{}, allowing `c.M{}`

type MiddlewareFunc ΒΆ

type MiddlewareFunc func(next HandlerFunc) HandlerFunc

MiddlewareFunc defines the signature for middleware functions in Quick. A middleware function receives the next HandlerFunc in the chain and returns a new HandlerFunc. Middleware can perform actions before and/or after calling the next handler.

Example:

func LoggingMiddleware() quick.MiddlewareFunc {
	return func(next quick.HandlerFunc) quick.HandlerFunc {
		return func(c *quick.Ctx) error {
			// Before handler logic (e.g., logging request details)
			log.Printf("Request received: %s %s", c.Request.Method, c.Request.URL)

			err := next(c) // Call the next handler

			// After handler logic (e.g., logging response status)
			log.Printf("Response sent with status: %d", c.ResponseWriter.Status())

			return err
		}
	}
}

type QTestPlus ΒΆ

type QTestPlus struct {
	// contains filtered or unexported fields
}

QTestPlus implements QtestReturn, encapsulating HTTP response details for testing.

func (*QTestPlus) AssertBodyContains ΒΆ

func (qt *QTestPlus) AssertBodyContains(expected any) error

AssertBodyContains checks if the response body contains the expected content.

Example Usage:

err := resp.AssertBodyContains("Success")

Returns:

  • error: Returns an error if the expected content is not found in the body.
Example ΒΆ

ExampleQTestPlus_AssertBodyContains demonstrates how to validate the response body.

The "/json" route returns a JSON response, and the test verifies the presence of the "message" key.

// Creating a Quick instance
q := New()

// Defining a route that returns JSON
q.Get("/json", func(c *Ctx) error {
	data := map[string]string{"message": "Hello, Quick!"}
	return c.JSON(data)
})

// Performing the HTTP test
opts := QuickTestOptions{
	Method: "GET",
	URI:    "/json",
}

res, err := q.Qtest(opts)
if err != nil {
	fmt.Println("Error:", err)
	return
}

// Validating the response body
err = res.AssertBodyContains(`"message":"Hello, Quick!"`)
if err != nil {
	fmt.Println("Body assertion failed:", err)
} else {
	fmt.Println("Body contains expected content")
}
Output:

Body contains expected content

func (*QTestPlus) AssertHeader ΒΆ

func (qt *QTestPlus) AssertHeader(key, expectedValue string) error

AssertHeader verifies if the specified header has the expected value.

Example Usage:

err := resp.AssertHeader("Content-Type", "application/json")

Returns:

  • error: Returns an error if the header does not match the expected value.
Example ΒΆ

ExampleQTestPlus_AssertHeader demonstrates how to validate an HTTP response header.

The "/header" route sets an "X-Custom-Header", which is then validated.

// Creating a Quick instance
q := New()

// Defining a route with a custom header
q.Get("/header", func(c *Ctx) error {
	c.Set("X-Custom-Header", "QuickFramework")
	return c.Status(200).String("OK")
})

// Performing the HTTP test
opts := QuickTestOptions{
	Method: "GET",
	URI:    "/header",
}

res, err := q.Qtest(opts)
if err != nil {
	fmt.Println("Error:", err)
	return
}

// Validating the header
err = res.AssertHeader("X-Custom-Header", "QuickFramework")
if err != nil {
	fmt.Println("Header assertion failed:", err)
} else {
	fmt.Println("Header is correct")
}
Output:

Header is correct

func (*QTestPlus) AssertHeaderContains ΒΆ

func (qt *QTestPlus) AssertHeaderContains(key, substring string) error

AssertHeaderContains checks if the specified header contains the expected substring.

Example Usage:

err := resp.AssertHeaderContains("Powered","ered")

Returns:

  • error: Returns an error if the header does not match the expected value.
Example ΒΆ

ExampleQTestPlus_AssertHeaderContains demonstrates how to verify if a header contains a substring.

The simulated HTTP response includes "X-Custom" header with value "PoweredByQuick".

q := New()

q.Get("/header", func(c *Ctx) error {
	c.Set("X-Custom", "PoweredByQuick")
	return c.String("OK")
})

res, _ := q.Qtest(QuickTestOptions{
	Method: "GET",
	URI:    "/header",
})

err := res.AssertHeaderContains("X-Custom", "Quick")
if err != nil {
	fmt.Println("Assertion failed:", err)
} else {
	fmt.Println("Header contains expected substring")
}
Output:

Header contains expected substring

func (*QTestPlus) AssertHeaderHasPrefix ΒΆ

func (qt *QTestPlus) AssertHeaderHasPrefix(key, prefix string) error

AssertHeaderHasPrefix checks if the specified header starts with the expected prefix.

Example Usage:

err := resp.AssertHeaderHasPrefix("Powered")

Returns:

  • error: Returns an error if the header does not match the expected value.
Example ΒΆ

ExampleQTestPlus_AssertHeaderHasPrefix demonstrates how to verify if a header starts with a prefix.

The simulated HTTP response includes "X-Version" header with value "v1.2.3".

q := New()

q.Get("/prefix", func(c *Ctx) error {
	c.Set("X-Version", "v1.2.3")
	return c.String("OK")
})

res, _ := q.Qtest(QuickTestOptions{
	Method: "GET",
	URI:    "/prefix",
})

err := res.AssertHeaderHasPrefix("X-Version", "v1")
if err != nil {
	fmt.Println("Assertion failed:", err)
} else {
	fmt.Println("Header has expected prefix")
}
Output:

Header has expected prefix

func (*QTestPlus) AssertHeaderHasValueInSet ΒΆ

func (qt *QTestPlus) AssertHeaderHasValueInSet(key string, allowed []string) error

AssertHeaderHasValueInSet checks if the specified header value is one of the allowed values.

Example Usage:

err := resp.AssertHeaderHasValueInSet("Powered",[]string{""})

Returns:

  • error: Returns an error if the header does not match the expected value.
Example ΒΆ

ExampleQTestPlus_AssertHeaderHasValueInSet demonstrates how to verify if a header value matches one of the allowed values.

The simulated HTTP response includes "X-Env" header with value "staging".

q := New()

q.Get("/variant", func(c *Ctx) error {
	c.Set("X-Env", "staging")
	return c.String("OK")
})

res, _ := q.Qtest(QuickTestOptions{
	Method: "GET",
	URI:    "/variant",
})

err := res.AssertHeaderHasValueInSet("X-Env", []string{"dev", "staging", "prod"})
if err != nil {
	fmt.Println("Assertion failed:", err)
} else {
	fmt.Println("Header value is in allowed set")
}
Output:

Header value is in allowed set

func (*QTestPlus) AssertNoHeader ΒΆ

func (qt *QTestPlus) AssertNoHeader(key string) error

AssertNoHeader checks if the specified header is not present or empty.

Example Usage:

err := resp.AssertNoHeader("X-Powered-By")

Returns:

  • error: Returns an error if the header does not match the expected value.
Example ΒΆ

ExampleQTestPlus_AssertNoHeader demonstrates how to check that a header is not present in the response.

The simulated HTTP response does not include the "X-Powered-By" header.

q := New()

q.Get("/no-header", func(c *Ctx) error {
	return c.String("No custom header here.")
})

res, err := q.Qtest(QuickTestOptions{
	Method: "GET",
	URI:    "/no-header",
})
if err != nil {
	fmt.Println("Error:", err)
	return
}

err = res.AssertNoHeader("X-Powered-By")
if err != nil {
	fmt.Println("Assertion failed:", err)
} else {
	fmt.Println("Header is not present as expected")
}
Output:

Header is not present as expected

func (*QTestPlus) AssertStatus ΒΆ

func (qt *QTestPlus) AssertStatus(expected int) error

AssertStatus verifies if the response status matches the expected status.

Example Usage:

err := resp.AssertStatus(200)
if err != nil {
    t.Errorf("Unexpected status: %v", err)
}

Returns:

  • error: Returns an error if the expected status does not match the actual status.
Example ΒΆ

ExampleQTestPlus_AssertStatus demonstrates how to verify the response status code.

The "/notfound" route returns a 404 status, which is validated in the assertion.

// Creating a Quick instance
q := New()

// Defining a route that returns 404
q.Get("/notfound", func(c *Ctx) error {
	return c.Status(404).String("Not Found")
})

// Performing the HTTP test
opts := QuickTestOptions{
	Method: "GET",
	URI:    "/notfound",
}

res, err := q.Qtest(opts)
if err != nil {
	fmt.Println("Error:", err)
	return
}

// Validating the response status
err = res.AssertStatus(404)
if err != nil {
	fmt.Println("Assertion failed:", err)
} else {
	fmt.Println("Status code is correct")
}
Output:

Status code is correct

func (*QTestPlus) AssertString ΒΆ

func (qt *QTestPlus) AssertString(expected string) error

AssertString compares the response body to the expected string.

Example Usage:

err := resp.AssertString("pong")

Returns:

  • error: Returns an error if the header does not match the expected value.
Example ΒΆ

ExampleQTestPlus_AssertString demonstrates how to compare the response body with an expected string.

The simulated HTTP response body contains "pong".

q := New()

q.Get("/ping", func(c *Ctx) error {
	return c.String("pong")
})

res, _ := q.Qtest(QuickTestOptions{
	Method: "GET",
	URI:    "/ping",
})

err := res.AssertString("pong")
if err != nil {
	fmt.Println("Assertion failed:", err)
} else {
	fmt.Println("Body matches expected string")
}
Output:

Body matches expected string

func (*QTestPlus) Body ΒΆ

func (qt *QTestPlus) Body() []byte

Body returns the response body as a byte slice.

Returns:

  • []byte: The response body.
Example ΒΆ

ExampleQTestPlus_Body demonstrates how to retrieve the response body as a byte slice.

The simulated response object contains "Hello, Quick!" as its body content.

// Simulating a response object with a predefined body
res := &QTestPlus{
	body: []byte("Hello, Quick!"),
}

// Retrieving and printing the body content
fmt.Println(string(res.Body()))
Output:

Hello, Quick!

func (*QTestPlus) BodyStr ΒΆ

func (qt *QTestPlus) BodyStr() string

BodyStr returns the response body as a string.

Returns:

  • string: The response body as a string.
Example ΒΆ

ExampleQTestPlus_BodyStr demonstrates how to retrieve the response body as a string.

The simulated response object contains "Hello, Quick!" as its body content.

// Simulating a response object with a predefined body
res := &QTestPlus{
	bodyStr: "Hello, Quick!",
}

// Retrieving and printing the body content as a string
fmt.Println(res.BodyStr())
Output:

Hello, Quick!

func (*QTestPlus) Response ΒΆ

func (qt *QTestPlus) Response() *http.Response

Response returns the raw *http.Response for advanced validation.

Returns:

  • *http.Response: The full HTTP response object.
Example ΒΆ

ExampleQTestPlus_Response demonstrates how to retrieve the complete HTTP response object.

The simulated HTTP response object contains a status of "200 OK".

// Simulating an HTTP response object
httpResponse := &http.Response{
	Status:     "200 OK",
	StatusCode: 200,
}

// Simulating a response object containing the HTTP response
res := &QTestPlus{
	response: httpResponse,
}

// Retrieving and printing the response status
fmt.Println("Response Status:", res.Response().Status)
Output:

Response Status: 200 OK

func (*QTestPlus) StatusCode ΒΆ

func (qt *QTestPlus) StatusCode() int

StatusCode retrieves the HTTP status code of the response.

Returns:

  • int: The HTTP status code.
Example ΒΆ

ExampleQTestPlus_StatusCode demonstrates how to retrieve the response status code.

The simulated response object contains a status code of 200.

res := &QTestPlus{
	statusCode: 200,
}

// Retrieving and printing the response status code
fmt.Println("Status Code:", res.StatusCode())
Output:

Status Code: 200

type QtestReturn ΒΆ

type QtestReturn interface {
	Body() []byte
	BodyStr() string
	StatusCode() int
	Response() *http.Response
	AssertStatus(expected int) error
	AssertHeader(key, expectedValue string) error
	AssertNoHeader(key string) error
	AssertString(expected string) error
	AssertBodyContains(expected any) error
	AssertHeaderHasValueInSet(key string, allowed []string) error
	AssertHeaderHasPrefix(key, prefix string) error
	AssertHeaderContains(key, substring string) error
}

QtestReturn defines an interface for validating HTTP test responses.

This interface provides methods to retrieve response details such as body, status code, headers, and to perform assertions for testing HTTP responses.

Example Usage:

resp, err := q.Qtest(quick.QuickTestOptions{
    Method: quick.MethodGet,
    URI:    "/test",
})

if err != nil {
    log.Fatal(err)
}

if err := resp.AssertStatus(200); err != nil {
    log.Fatal(err)
}

type Quick ΒΆ

type Quick struct {
	Cors bool // Indicates if CORS is enabled.

	CorsSet     func(http.Handler) http.Handler // CORS middleware handler function.
	CorsOptions map[string]string               // CORS options map
	// contains filtered or unexported fields
}

Quick is the main structure of the framework, holding routes and configurations.

func New ΒΆ

func New(c ...Config) *Quick

New creates a new instance of the Quick structure to manage HTTP routes and handlers.

This function initializes a Quick instance with optional configurations provided through the `Config` parameter. If no configuration is provided, it uses the `defaultConfig`.

Parameters:

  • c ...Config: (Optional) Configuration settings for customizing the Quick instance.

Returns:

  • *Quick: A pointer to the initialized Quick instance.

Example Usage:

// Basic usage - Create a default Quick instance
q := quick.New()

// Custom usage - Create a Quick instance with specific configurations
q := quick.New(quick.Config{
	RouteCapacity: 500,
})

q.Get("/", func(c quick.Ctx) error {
	return c.SendString("Hello, Quick!")
})
Example ΒΆ

This function is named ExampleNew() it with the Examples type.

// Start Quick instance
q := New()

// Define a simple GET route
q.Get("/", func(c *Ctx) error {
	// Set response header
	c.Set("Content-Type", "text/plain")

	// Return a text response
	return c.Status(200).String("Quick in action ❀️!")
})

// Simulate a request using Quick's test utility
res, _ := q.Qtest(QuickTestOptions{
	Method:  MethodGet,
	URI:     "/",
	Headers: map[string]string{"Content-Type": "application/json"},
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("status error:", err)
}

if err := res.AssertString("Quick in action ❀️!"); err != nil {
	fmt.Println("body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Quick in action ❀️!

func (*Quick) Any ΒΆ

func (q *Quick) Any(path string, handlerFunc HandleFunc)

Any registers the same handlerFunc for all standard HTTP methods (GET, POST, PUT, etc.).

This is useful when you want to attach a single handler to a path regardless of the HTTP method, and handle method-based logic inside the handler itself (e.g., returning 405 if not GET).

Example:

app := quick.New()
app.Any("/health", func(c *quick.Ctx) error {
	if c.Method() != quick.MethodGet {
		return c.Status(quick.StatusMethodNotAllowed).SendString("Method Not Allowed")
	}
	return c.Status(quick.StatusOK).SendString("OK")
})

Note: The handlerFunc will be registered individually for each method listed in allMethods.

func (*Quick) Delete ΒΆ

func (q *Quick) Delete(pattern string, handlerFunc HandleFunc)

Delete registers an HTTP route with the DELETE method on the Quick server.

This function associates a DELETE request with a specific route pattern and handler function. It is typically used for deleting existing resources.

Parameters:

  • pattern string: The route pattern (e.g., "/users/:id").
  • handlerFunc HandleFunc: The function that will handle the DELETE request.

Example Usage:

// This function is automatically triggered when defining a DELETE route in Quick.
Example ΒΆ

This function is named ExampleQuick_Delete() it with the Examples type.

// Start Quick instance
q := New()

// Define a DELETE route
q.Delete("/delete", func(c *Ctx) error {
	// Return response indicating resource deletion
	return c.Status(200).String("Deleted resource!")
})

// Simulate a DELETE request for testing
res, _ := q.Qtest(QuickTestOptions{
	Method:  MethodDelete,
	URI:     "/delete",
	Headers: map[string]string{"Content-Type": "application/json"},
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("status error:", err)
}

if err := res.AssertString("Deleted resource!"); err != nil {
	fmt.Println("body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Deleted resource!

func (*Quick) Display ΒΆ

func (q *Quick) Display(scheme, addr string)

Display prints a styled startup banner to the console showing essential information about the Quick server.

It includes the following details:

  • The current version of Quick
  • Host and port where the server is running
  • The total number of registered routes

The banner is only printed if the `NoBanner` option is set to false in the configuration.

Parameters:

  • scheme: The protocol used by the server (e.g., "http" or "https").
  • addr: The address the server is bound to (in the format "host:port").

func (*Quick) Get ΒΆ

func (q *Quick) Get(pattern string, handlerFunc HandleFunc)

Get registers an HTTP route with the GET method on the Quick server.

This function associates a GET request with a specific route pattern and handler function. It ensures that the request is properly processed when received.

Parameters:

  • pattern string: The route pattern (e.g., "/users/:id").
  • handlerFunc HandleFunc: The function that will handle the GET request.

Example Usage:

// This function is automatically triggered when defining a GET route in Quick.
Example ΒΆ

This function is named ExampleQuick_Get() it with the Examples type.

// Start Quick instance
q := New()

// Define a GET route with a handler function
q.Get("/hello", func(c *Ctx) error {
	// Return a simple text response
	return c.Status(200).String("Hello, world!")
})

// Simulate a GET request to the route
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/hello",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("status error:", err)
}

if err := res.AssertString("Hello, world!"); err != nil {
	fmt.Println("body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Hello, world!

func (*Quick) GetConfig ΒΆ

func (q *Quick) GetConfig() Config
(q *Quick) Config() Config

example: c.App.GetConfig().Views

func (*Quick) GetRoute ΒΆ

func (q *Quick) GetRoute() []*Route

GetRoute retrieves all registered routes in the Quick framework.

This function returns a slice containing all the routes that have been registered in the Quick instance. It is useful for debugging, logging, or dynamically inspecting available routes.

Example Usage:

routes := q.GetRoute()
for _, route := range routes {
    fmt.Println("Method:", route.Method, "Path:", route.Path)
}

Returns:

  • []*Route: A slice of pointers to the registered Route instances.
Example ΒΆ

This function is named ExampleQuick_GetRoute() it with the Examples type.

// Start Quick instance
q := New()

// Define multiple routes
q.Get("/users/:id", func(c *Ctx) error {
	return c.Status(200).String("User ID: " + c.Params["id"])
})
q.Post("/users", func(c *Ctx) error {
	return c.Status(201).String("User created")
})

// Get a list of all registered routes
routes := q.GetRoute()

// Print the total number of routes
fmt.Println(len(routes))

// Iterate over the routes and print their method and pattern
for _, route := range routes {
	fmt.Println(route.Method, route.Pattern)
}
Output:

2
GET /users/:id
POST

func (*Quick) Group ΒΆ

func (q *Quick) Group(prefix string) *Group

Group creates a new route group with a shared prefix.

This function allows organizing routes under a common prefix, making it easier to manage related endpoints (e.g., `/api`, `/v1`, `/admin`). All routes registered within this group will automatically inherit the specified prefix.

Grouping routes is useful for:

  • API versioning (`/v1`, `/v2`).
  • Organizing authentication-protected routes (`/auth`, `/admin`).
  • Applying shared middlewares to a set of routes.

Parameters:

  • prefix: The common prefix for all routes in this group.

Returns:

  • *Group: A new Group instance that can be used to define related routes.

Example Usage:

group := q.Group("/api")

group.Get("/user", func(c *quick.Ctx) error {
	return c.Status(200).SendString("[GET] [GROUP] /v1/user ok!!!")
})
Example ΒΆ

This function is named ExampleQuick_Group() it with the Examples type.

q := New()

// Create a route group with prefix "/api"
apiGroup := q.Group("/api")

// As the prefix field is unexported, we test by registering a route and calling it
apiGroup.Get("/check", func(c *Ctx) error {
	return c.Status(200).String("Prefix OK")
})

res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/api/check",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("Status error:", err)
}

if err := res.AssertString("Prefix OK"); err != nil {
	fmt.Println("Body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Prefix OK

func (*Quick) Handler ΒΆ

func (q *Quick) Handler() http.Handler

Handler returns the main HTTP handler for Quick, allowing integration with standard http.Server and testing frameworks.

func (*Quick) HandlerFunc ΒΆ

func (q *Quick) HandlerFunc(h HandlerFunc) http.HandlerFunc

HandlerFunc adapts a quick.HandlerFunc to a standard http.HandlerFunc. It creates a new Quick context (Ctx) for each HTTP request, allowing Quick handlers to access request and response objects seamlessly.

Usage Example:

http.HandleFunc(\"/\", app.HandlerFunc(func(c *quick.Ctx) error {
	return c.Status(200).JSON(map[string]string{\"message\": \"Hello, Quick!\"})
}))

func (*Quick) HandlersCount ΒΆ

func (q *Quick) HandlersCount() uint32

GetRoute retrieves all registered routes in the Quick framework.

This function returns a slice containing all the routes that have been registered in the Quick instance. It is useful for debugging, logging, or dynamically inspecting available routes.

Example Usage:

routes := q.GetRoute()
for _, route := range routes {
    fmt.Println("Method:", route.Method, "Path:", route.Path)
}

Returns:

  • []*Route: A slice of pointers to the registered Route instances.

func (*Quick) Head ΒΆ

func (q *Quick) Head(pattern string, handlerFunc HandleFunc)

Head registers an HTTP route with the HEAD method on the Quick server.

This function associates a HEAD request with a specific route pattern and handler function. It ensures that the request is properly processed according to the HTTP HEAD specification, meaning that only headers and the status code are sent in the response, with no response body.

Parameters:

  • pattern string: The route pattern (e.g., "/users/:id").
  • handlerFunc HandleFunc: The function that will handle the HEAD request.

Example Usage:

// This function is automatically triggered when defining a HEAD route in Quick.

func (*Quick) Listen ΒΆ

func (q *Quick) Listen(addr string, handler ...http.Handler) error

Listen starts the Quick server and blocks indefinitely.

This function initializes the HTTP server and prevents the application from exiting.

Example Usage:

q.Listen(":8080")

Parameters:

  • addr: The address on which the server should listen (e.g., ":8080").
  • handler: (Optional) Custom HTTP handlers.

Returns:

  • error: Any errors encountered while starting the server.
Example ΒΆ

This function is named ExampleQuick_Listen() it with the Examples type.

// Start Quick instance
q := New()

// Define a simple route
q.Get("/", func(c *Ctx) error {
	return c.Status(200).String("Hello, Quick!")
})

// Start the server and listen on port 8080
err := q.Listen(":8080")
if err != nil {
	fmt.Println("Error starting server:", err)
}

// (This function starts a server and does not return an output directly)

func (*Quick) ListenTLS ΒΆ

func (q *Quick) ListenTLS(addr, certFile, keyFile string, useHTTP2 bool, handler ...http.Handler) error

ListenTLS starts an HTTPS server on the specified address using the provided certificate and key files. It allows enabling or disabling HTTP/2 support. It also configures basic modern TLS settings, sets up a listener with SO_REUSEPORT (when possible), and applies a graceful shutdown procedure.

Parameters:

  • addr: the TCP network address to listen on (e.g., ":443")
  • certFile: the path to the SSL certificate file
  • keyFile: the path to the SSL private key file
  • useHTTP2: whether or not to enable HTTP/2
  • handler: optional HTTP handlers. If none is provided, the default handler is used.

Returns:

  • error: an error if something goes wrong creating the listener or starting the server.

func (*Quick) ListenWithShutdown ΒΆ

func (q *Quick) ListenWithShutdown(addr string, handler ...http.Handler) (*http.Server, func(), error)

ListenWithShutdown starts an HTTP server and returns both the server instance and a shutdown function.

This method initializes performance tuning settings, creates a TCP listener, and starts the server in a background goroutine. The returned shutdown function allows for a graceful termination of the server.

Parameters:

  • addr: The address (host:port) where the server should listen.
  • handler: Optional HTTP handlers that can be provided to the server.

Returns:

  • *http.Server: A reference to the initialized HTTP server.
  • func(): A shutdown function to gracefully stop the server.
  • error: Any error encountered during the server setup.

func (*Quick) Options ΒΆ

func (q *Quick) Options(pattern string, handlerFunc HandleFunc)

Options registers an HTTP route with the OPTIONS method on the Quick server.

This function associates an OPTIONS request with a specific route pattern and handler function. OPTIONS requests are typically used to determine the allowed HTTP methods for a resource.

Parameters:

  • pattern string: The route pattern (e.g., "/users").
  • handlerFunc HandleFunc: The function that will handle the OPTIONS request.

Example Usage:

// This function is automatically triggered when defining an OPTIONS route in Quick.
Example ΒΆ

This function is named ExampleQuick_Options()

it with the Examples type.
// Start Quick instance
q := New()

// Define an OPTIONS route
q.Options("/resource", func(c *Ctx) error {
	c.Set("Allow", "GET, POST, OPTIONS")
	return c.Status(204).Send(nil) // No Content response
})

// Simulate an OPTIONS request to "/resource"
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodOptions,
	URI:    "/resource",
})

// Print the response status
fmt.Println("Status:", res.StatusCode())
Output:

Status: 204

func (*Quick) Patch ΒΆ

func (q *Quick) Patch(pattern string, handlerFunc HandleFunc)

Patch registers an HTTP route with the PATCH method on the Quick server.

This function associates a PATCH request with a specific route pattern and handler function. It is typically used for applying partial updates to an existing resource.

Parameters:

  • pattern string: The route pattern (e.g., "/users/:id").
  • handlerFunc HandleFunc: The function that will handle the PATCH request.

Example Usage:

// This function is automatically triggered when defining a PATCH route in Quick.
Example ΒΆ

This function is named ExampleQuick_Patch()

it with the Examples type.
// Start Quick instance
q := New()

// Define a PATCH route
q.Patch("/update", func(c *Ctx) error {
	return c.Status(200).String("PATCH request received")
})

// Simulate a PATCH request to "/update"
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodPatch,
	URI:    "/update",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("status error:", err)
}

if err := res.AssertString("PATCH request received"); err != nil {
	fmt.Println("body error:", err)
}

fmt.Println(res.BodyStr())
Output:

PATCH request received

func (*Quick) Post ΒΆ

func (q *Quick) Post(pattern string, handlerFunc HandleFunc)

Post registers an HTTP route with the POST method on the Quick server.

This function associates a POST request with a specific route pattern and handler function. It is typically used for handling form submissions, JSON payloads, or data creation.

Parameters:

  • pattern string: The route pattern (e.g., "/users").
  • handlerFunc HandleFunc: The function that will handle the POST request.

Example Usage:

// This function is automatically triggered when defining a POST route in Quick.
Example ΒΆ

This function is named ExampleQuick_Post() it with the Examples type.

// Start Quick instance
q := New()

// Define a POST route
q.Post("/create", func(c *Ctx) error {
	// Return response indicating resource creation
	return c.Status(201).String("Resource created!")
})

// Simulate a POST request for testing
res, _ := q.Qtest(QuickTestOptions{
	Method:  MethodPost,
	URI:     "/create",
	Headers: map[string]string{"Content-Type": "application/json"},
})

if err := res.AssertStatus(201); err != nil {
	fmt.Println("status error:", err)
}

if err := res.AssertString("Resource created!"); err != nil {
	fmt.Println("body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Resource created!

func (*Quick) Put ΒΆ

func (q *Quick) Put(pattern string, handlerFunc HandleFunc)

Put registers an HTTP route with the PUT method on the Quick server.

This function associates a PUT request with a specific route pattern and handler function. It is typically used for updating existing resources.

Parameters:

  • pattern string: The route pattern (e.g., "/users/:id").
  • handlerFunc HandleFunc: The function that will handle the PUT request.

Example Usage:

// This function is automatically triggered when defining a PUT route in Quick.
Example ΒΆ

This function is named ExampleQuick_Put() it with the Examples type.

// Start Quick instance
q := New()

// Define a PUT route
q.Put("/update", func(c *Ctx) error {
	// Return response indicating resource update
	return c.Status(200).String("Update resource!")
})

// Simulate a PUT request for testing
res, _ := q.Qtest(QuickTestOptions{
	Method:  MethodPut,
	URI:     "/update",
	Headers: map[string]string{"Content-Type": "application/json"},
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("status error:", err)
}

if err := res.AssertString("Update resource!"); err != nil {
	fmt.Println("body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Update resource!

func (Quick) Qtest ΒΆ

func (q Quick) Qtest(opts QuickTestOptions) (QtestReturn, error)

Qtest performs an HTTP request using QuickTestOptions and returns a response handler.

This method executes a test HTTP request within the Quick framework, allowing validation of the response status, headers, and body.

Example Usage:

resp, err := q.Qtest(quick.QuickTestOptions{
    Method: quick.MethodGet,
    URI:    "/test",
})

if err != nil {
    log.Fatal(err)
}

if err := resp.AssertStatus(200); err != nil {
    log.Fatal(err)
}

Returns:

  • QtestReturn: An interface for response validation
  • error: Error encountered during request execution, if any.
Example ΒΆ

ExampleQuick_Qtest demonstrates how to use Qtest to test a simple GET route.

The "/hello" route returns a response with status 200 and body "Hello, Quick!".

// Creating a Quick instance
q := New()

// Defining a simple GET route
q.Get("/hello", func(c *Ctx) error {
	return c.Status(200).String("Hello, Quick!")
})

// Defining the request parameters
opts := QuickTestOptions{
	Method: "GET",
	URI:    "/hello",
}

// Performing the HTTP test using the Quick instance
res, err := q.Qtest(opts)

// Handling errors
if err != nil {
	fmt.Println("Error:", err)
	return
}

// Printing response details
fmt.Println("Status Code:", res.StatusCode())
fmt.Println("Body:", res.BodyStr())
Output:

Status Code: 200
Body: Hello, Quick!

func (*Quick) ServeHTTP ΒΆ

func (q *Quick) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP processes incoming HTTP requests and matches them to registered routes.

This function efficiently routes HTTP requests to the appropriate handler while leveraging a **pooled response writer** and **context pooling** to minimize memory allocations and improve performance.

If the request method is `OPTIONS`, it is handled separately via `handleOptions`. If no matching route is found, the function responds with `404 Not Found`.

Example Usage: This function is automatically invoked by the `http.Server` when a request reaches the Quick router.

Example ΒΆ

This function is named ExampleQuick_ServeHTTP() it with the Examples type.

// Start Quick instance
q := New()

// Define a route with a dynamic parameter
q.Get("/users/:id", func(c *Ctx) error {
	// Retrieve the parameter and return it in the response
	return c.Status(200).String("User Id: " + c.Params["id"])
})

// Simulate a request with a user ID
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/users/42",
})
if err := res.AssertStatus(200); err != nil {
	fmt.Println("status error:", err)
}

if err := res.AssertString("User Id: 42"); err != nil {
	fmt.Println("body error:", err)
}

// Print the response body
fmt.Println(res.BodyStr())
Output:

User Id: 42

func (*Quick) Shutdown ΒΆ

func (q *Quick) Shutdown() error

Shutdown gracefully shuts down the Quick server without interrupting active connections.

This function ensures that all ongoing requests are completed before shutting down, preventing abrupt connection termination.

Example Usage:

	q := quick.New()

    q.Get("/", func(c *quick.Ctx) error {
        return c.SendString("Server is running!")
    })

	q.Shutdown()

Returns:

  • error: Any errors encountered during shutdown.
Example ΒΆ

This function is named ExampleQuick_Shutdown()

it with the Examples type.
// Create a new Quick instance
q := New()

// Define a GET route with a handler function
q.Get("/", func(c *Ctx) error {
	// Return a simple text response
	return c.SendString("Server is running!")
})

// Simulate a GET request to the route and capture the response
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("status error:", err)
}

// Print only the response body to match GoDoc expectations
fmt.Println(res.BodyStr())

// Simulate server shutdown immediately after starting
err := q.Shutdown()

// Print the shutdown status
if err == nil {
	fmt.Println("Server shut down successfully.")
} else {
	fmt.Println("Error shutting down server:", err)
}
Output:

Server is running!
Server shut down successfully.

func (*Quick) Static ΒΆ

func (q *Quick) Static(route string, dirOrFS any)

Static serves static files (HTML, CSS, JS, images, etc.) from a directory or embedded filesystem.

This function allows you to register a static file server in the Quick framework, either using a local directory (`string`) or an embedded filesystem (`embed.FS`). By embedding files, they become part of the binary at compile time, eliminating the need for external file access.

Example Usage: This function is useful for serving front-end assets or static resources directly from the Go application. It supports both local directories and embedded files.

Parameters:

  • route: The base path where static files will be served (e.g., "/static").
  • dirOrFS: The source of the static files. It accepts either:
  • `string`: A local directory path (e.g., `"./static"`).
  • `embed.FS`: An embedded file system for compiled-in assets.

Returns:

  • None (void function).

Notes:

  • The function automatically trims trailing slashes from `route`.
  • If an invalid parameter is provided, the function panics.
  • When using an embedded filesystem, files are served directly from memory.
Example ΒΆ
// Quick Start
q := New()

q.Static("/static", staticFilesExample)

q.Get("/", func(c *Ctx) error {
	c.File("static/index.html")
	return nil
})

// Simulates a request for "/"
res, _ := q.Qtest(QuickTestOptions{
	Method: MethodGet,
	URI:    "/",
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("status error:", err)
}

fmt.Println("Status:", res.StatusCode())
Output:

Status: 200

func (*Quick) Use ΒΆ

func (q *Quick) Use(mw any)

Use function adds middleware to the Quick server, with special treatment for CORS.

This method allows adding custom middleware functions to process requests before they reach the final handler. If a CORS middleware is detected, it is automatically applied.

Parameters:

  • mw any: Middleware function to be added. It must be of type `func(http.Handler) http.Handler`.

Example Usage:

q := quick.New()

q.Use(maxbody.New(50000))

q.Post("/v1/user/maxbody/any", func(c *quick.Ctx) error {
    c.Set("Content-Type", "application/json")//
    return c.Status(200).Send(c.Body())
})
Example ΒΆ

This function is named ExampleQuick_Use() it with the Examples type.

// Start Quick instance
q := New()

// Apply CORS middleware to allow cross-origin requests
q.Use(cors.New())

// Define a route that will be affected by the middleware
q.Get("/use", func(c *Ctx) error {
	// Set response header
	c.Set("Content-Type", "text/plain")

	// Return response with middleware applied
	return c.Status(200).String("Quick in action com middleware ❀️!")
})

// Simulate a request for testing
res, _ := q.Qtest(QuickTestOptions{
	Method:  MethodGet,
	URI:     "/use",
	Headers: map[string]string{"Content-Type": "application/json"},
})

if err := res.AssertStatus(200); err != nil {
	fmt.Println("status error:", err)
}

if err := res.AssertString("Quick in action com middleware ❀️!"); err != nil {
	fmt.Println("body error:", err)
}

fmt.Println(res.BodyStr())
Output:

Quick in action com middleware ❀️!

type QuickMockCtx ΒΆ

type QuickMockCtx interface {
	Get(URI string) error
	Post(URI string, body []byte) error
	Put(URI string, body []byte) error
	Delete(URI string) error
}

QuickMockCtx defines the interface for mocking HTTP methods in Quick.

func QuickMockCtxJSON ΒΆ

func QuickMockCtxJSON(ctx *Ctx, params map[string]string) QuickMockCtx

QuickMockCtxJSON creates a new mock context for JSON content type.

func QuickMockCtxXML ΒΆ

func QuickMockCtxXML(ctx *Ctx, params map[string]string, contentType string) QuickMockCtx

QuickMockCtxXML creates a new mock context for XML content type.

type QuickMockTestServer ΒΆ

type QuickMockTestServer struct {
	Client  *http.Client      // HTTP client to interact with the mock server.
	Port    int               // Port on which the mock server runs.
	URI     string            // The request URI for the test.
	Method  string            // The HTTP method (GET, POST, etc.).
	Headers map[string]string // Headers to be included in the request.
	Body    []byte            // Request body content.
}

QuickMockTestServer defines a mock server configuration for testing.

type QuickTestOptions ΒΆ

type QuickTestOptions struct {
	Method      string            // HTTP method (e.g., "GET", "POST")
	URI         string            // Target URI for the request
	Headers     map[string]string // Request headers
	QueryParams map[string]string // Query parameters to append to the URI
	Body        []byte            // Request body payload
	Cookies     []*http.Cookie    // Cookies to include in the request
	LogDetails  bool              // Enable logging of request and response details
	TLS         bool              // Enable tls connects
}

QuickTestOptions defines configuration options for executing an HTTP test request.

Example Usage:

opts := quick.QuickTestOptions{
    Method:      quick.MethodPost,
    URI:         "/submit",
    Headers:     map[string]string{"Content-Type": "application/json"},
    QueryParams: map[string]string{"id": "123"},
    Body:        []byte(`{"name":"John Doe"}`),
}

resp, err := q.Qtest(opts)

type QuickTestReturn ΒΆ

type QuickTestReturn interface {
	Body() []byte             // Returns the raw response body as a byte slice.
	BodyStr() string          // Returns the response body as a string.
	StatusCode() int          // Returns the HTTP status code.
	Response() *http.Response // Returns the full HTTP response object.
}

QuickTestReturn defines the interface for handling HTTP test responses.

type Route ΒΆ

type Route struct {
	Group   string // Route group for organization
	Pattern string // URL pattern associated with the route
	Path    string // The registered path for the route
	Params  string // Parameters extracted from the URL
	Method  string // HTTP method associated with the route (GET, POST, etc.)
	// contains filtered or unexported fields
}

Route represents a registered HTTP route in the Quick framework

type UploadedFile ΒΆ

type UploadedFile struct {
	File      multipart.File
	Multipart *multipart.FileHeader
	Info      FileInfo
}

UploadedFile holds details of an uploaded file.

Fields:

  • File: The uploaded file as a multipart.File.
  • Multipart: The file header containing metadata about the uploaded file.
  • Info: Additional information about the file, including filename, size, and content type.

func (*UploadedFile) Bytes ΒΆ

func (uf *UploadedFile) Bytes() []byte

Bytes returns the raw content of the uploaded file as a byte slice.

This function allows access to the file data in memory before saving it.

Returns:

  • []byte: The raw bytes of the uploaded file.
Example ΒΆ

This function is named ExampleUploadedFile_Bytes() it with the Examples type.

// Start a new Quick instance
q := New()

// Define a POST route for file upload
q.Post("/upload", func(c *Ctx) error {
	// Set file upload size limit
	c.FormFileLimit("10MB")

	// Retrieve the uploaded file
	uploadedFile, err := c.FormFile("file")
	if err != nil {
		return c.Status(400).JSON(Msg{
			Msg:   "Upload error",
			Error: err.Error(),
		})
	}

	// Print only the file name
	fmt.Println(uploadedFile.FileName())

	// Return JSON response with the file name
	return c.Status(200).JSON(map[string]string{
		"name": uploadedFile.FileName(),
	})
})

// Creating an UploadedFile object with content as bytes
uploadedFile := &UploadedFile{
	Info: FileInfo{
		Bytes: []byte("Hello, Quick!"),
	},
}

// Converting bytes to a string to display the content
fmt.Println(string(uploadedFile.Bytes()))
Output:

Hello, Quick!

func (*UploadedFile) ContentType ΒΆ

func (uf *UploadedFile) ContentType() string

ContentType returns the MIME type of the uploaded file.

This function retrieves the detected content type of the uploaded file.

Returns:

  • string: The MIME type of the file.
Example ΒΆ

This function is named ExampleUploadedFile_ContentType() it with the Examples type.

// Start a new Quick instance
q := New()

// Define a POST route for file upload
q.Post("/upload", func(c *Ctx) error {
	// Set file upload size limit
	c.FormFileLimit("10MB")

	// Retrieve the uploaded file
	uploadedFile, err := c.FormFile("file")
	if err != nil {
		return c.Status(400).JSON(Msg{
			Msg:   "Upload error",
			Error: err.Error(),
		})
	}

	// Print only the file name
	fmt.Println(uploadedFile.FileName())

	// Return JSON response with the file name
	return c.Status(200).JSON(map[string]string{
		"name": uploadedFile.FileName(),
	})
})

// Creating an UploadedFile object simulating an uploaded PNG file
uploadedFile := &UploadedFile{
	Info: FileInfo{
		ContentType: "image/png",
	},
}

// Retrieving the content type
fmt.Println(uploadedFile.ContentType())
Output:

image/png

func (*UploadedFile) FileName ΒΆ

func (uf *UploadedFile) FileName() string

FileName returns the name of the uploaded file.

This function retrieves the original filename as provided during the upload.

Returns:

  • string: The name of the uploaded file.
Example ΒΆ

This function is named ExampleUploadedFile_FileName() it with the Examples type.

// Start a new Quick instance
q := New()

// Define a POST route for file upload
q.Post("/upload", func(c *Ctx) error {
	// Set file upload size limit
	c.FormFileLimit("10MB")

	// Retrieve the uploaded file
	uploadedFile, err := c.FormFile("file")
	if err != nil {
		return c.Status(400).JSON(Msg{
			Msg:   "Upload error",
			Error: err.Error(),
		})
	}

	// Print only the file name
	fmt.Println(uploadedFile.FileName())

	// Return JSON response with the file name
	return c.Status(200).JSON(map[string]string{
		"name": uploadedFile.FileName(),
	})
})

// Simulate an UploadedFile manually (standalone example)
uploadedFile := &UploadedFile{
	Info: FileInfo{
		Filename: "quick.txt",
	},
}

// Print only the file name
fmt.Println(uploadedFile.FileName())
Output:

quick.txt

func (*UploadedFile) Save ΒΆ

func (uf *UploadedFile) Save(destination string, nameFile ...string) error

Save stores the uploaded file in the specified directory.

If a filename is provided, the file will be saved with that name. Otherwise, the original filename is used.

Parameters:

  • destination (string): The directory where the file should be saved.
  • nameFile (optional []string): Custom filename (optional).

Returns:

  • error: An error if the save operation fails.

Example Usage:

err := uploadedFile.Save("./uploads", "newfile.png")
if err != nil {
    log.Fatal("Failed to save file:", err)
}
Example ΒΆ

This function is named ExampleUploadedFile_Save() it with the Examples type.

// Start a new Quick instance
q := New()

// Define a POST route for file upload
q.Post("/upload", func(c *Ctx) error {
	// Set file upload size limit
	c.FormFileLimit("10MB")

	// Retrieve the uploaded file
	uploadedFile, err := c.FormFile("file")
	if err != nil {
		return c.Status(400).JSON(Msg{
			Msg:   "Upload error",
			Error: err.Error(),
		})
	}

	// Print only the file name
	fmt.Println(uploadedFile.FileName())

	// Return JSON response with the file name
	return c.Status(200).JSON(map[string]string{
		"name": uploadedFile.FileName(),
	})
})

// Creating an UploadedFile object simulating an uploaded file
uploadedFile := &UploadedFile{
	Info: FileInfo{
		Filename: "quick.txt",
		Bytes:    []byte("Hello, Quick!"),
	},
}

// Saving the file to the "uploads" directory
err := uploadedFile.Save("uploads")

// Checking for errors
if err != nil {
	fmt.Println("Error saving file:", err)
} else {
	fmt.Println("File saved successfully!")
}
Output:

File saved successfully!

func (*UploadedFile) Size ΒΆ

func (uf *UploadedFile) Size() int64

Size returns the size of the uploaded file in bytes.

This function retrieves the file size based on the uploaded file metadata.

Returns:

  • int64: The size of the file in bytes.
Example ΒΆ

This function is named ExampleUploadedFile_Size() it with the Examples type.

// Start a new Quick instance
q := New()

// Define a POST route for file upload
q.Post("/upload", func(c *Ctx) error {
	// Set file upload size limit
	c.FormFileLimit("10MB")

	// Retrieve the uploaded file
	uploadedFile, err := c.FormFile("file")
	if err != nil {
		return c.Status(400).JSON(Msg{
			Msg:   "Upload error",
			Error: err.Error(),
		})
	}

	// Print only the file name
	fmt.Println(uploadedFile.FileName())

	// Return JSON response with the file name
	return c.Status(200).JSON(map[string]string{
		"name": uploadedFile.FileName(),
	})
})

// Creating an UploadedFile object simulating an uploaded file of 2048 bytes
uploadedFile := &UploadedFile{
	Info: FileInfo{
		Size: 2048,
	},
}

// Retrieving the file size
fmt.Println(uploadedFile.Size())
Output:

2048

type Zeroth ΒΆ

type Zeroth int

Zeroth is a custom type for zero-value constants

const (
	Zero Zeroth = 0
)

Zero is a predefined constant of type Zeroth

Directories ΒΆ

Path Synopsis
example
basic auth
middleware/cache/basic
Example of basic cache middleware usage in Quick
Example of basic cache middleware usage in Quick
middleware/cache/custom-key
Example of cache middleware with custom key generation in Quick
Example of cache middleware with custom key generation in Quick
middleware/cache/redis
Example of cache middleware with real Redis storage in Quick
Example of cache middleware with real Redis storage in Quick
middleware/cache/redis.mock
Example of cache middleware with Redis storage in Quick
Example of cache middleware with Redis storage in Quick
static/quick.embed
Embed.FS allows you to include files directly into the binary during compilation, eliminating the need to load files from the file system at runtime.
Embed.FS allows you to include files directly into the binary during compilation, eliminating the need to load files from the file system at runtime.
upload.multipart/quick/file
Uploading multiple files using Quick is simple, and much more minimalistic than the patterns we've seen in other frameworks.
Uploading multiple files using Quick is simple, and much more minimalistic than the patterns we've seen in other frameworks.
upload.multipart/quick/file.copy
Uploading multiple files using Quick is simple, and much more minimalistic than the patterns we've seen in other frameworks.
Uploading multiple files using Quick is simple, and much more minimalistic than the patterns we've seen in other frameworks.
upload.multipart/quick/files
Uploading multiple files using Quick is simple, and much more minimalistic than the patterns we've seen in other frameworks.
Uploading multiple files using Quick is simple, and much more minimalistic than the patterns we've seen in other frameworks.
Package gcolor provides a fluent and expressive API for styling terminal output using ANSI escape codes.
Package gcolor provides a fluent and expressive API for styling terminal output using ANSI escape codes.
Basic usage:
Basic usage:
http
client
Package client provides an advanced HTTP client for making HTTP requests.
Package client provides an advanced HTTP client for making HTTP requests.
internal
log
qos
middleware
cache
Package cache provides a middleware for the Quick web framework that implements an in-memory caching system for HTTP responses.
Package cache provides a middleware for the Quick web framework that implements an in-memory caching system for HTTP responses.
compress
Package compress provides middleware for compressing HTTP responses using gzip.
Package compress provides middleware for compressing HTTP responses using gzip.
cors
Package cors provides middleware for handling Cross-Origin Resource Sharing (CORS) in HTTP servers.
Package cors provides middleware for handling Cross-Origin Resource Sharing (CORS) in HTTP servers.
healthcheck
Package healthcheck provides a middleware and endpoint for application health monitoring.
Package healthcheck provides a middleware and endpoint for application health monitoring.
helmet
Package helmet provides middleware for the Quick framework that sets various HTTP headers to help secure your application.
Package helmet provides middleware for the Quick framework that sets various HTTP headers to help secure your application.
limiter
Package limiter provides middleware for rate limiting HTTP requests in Quick.
Package limiter provides middleware for rate limiting HTTP requests in Quick.
logger
Package logger provides a middleware for structured logging in Quick.
Package logger provides a middleware for structured logging in Quick.
maxbody
Package maxbody provides middleware to enforce request body size limits.
Package maxbody provides middleware to enforce request body size limits.
msgid
Package msgid provides middleware for automatically generating and assigning a unique Message ID (MsgID) to incoming HTTP requests.
Package msgid provides middleware for automatically generating and assigning a unique Message ID (MsgID) to incoming HTTP requests.
msguuid
Package msguuid provides a middleware for assigning a UUID to incoming HTTP requests.
Package msguuid provides a middleware for assigning a UUID to incoming HTTP requests.
pprof
Package pprof provides a Quick middleware that integrates Go's built-in net/http/pprof profiler for runtime analysis, performance debugging, and memory/cpu profiling.
Package pprof provides a Quick middleware that integrates Go's built-in net/http/pprof profiler for runtime analysis, performance debugging, and memory/cpu profiling.
recover
Package recover provides a middleware for the Quick web framework that gracefully handles panics during request processing.
Package recover provides a middleware for the Quick web framework that gracefully handles panics during request processing.
Package rand provides utility functions for generating random values using only the Go standard library.
Package rand provides utility functions for generating random values using only the Go standard library.
html
Package html provides a powerful and extensible HTML template engine for the Quick web framework, built on top of Go's html/template.
Package html provides a powerful and extensible HTML template engine for the Quick web framework, built on top of Go's html/template.
Package uuid generates and inspects UUIDs.
Package uuid generates and inspects UUIDs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL