reply-go

module
v1.0.5 Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2025 License: MIT

README

Reply-Go

A lightweight HTTP response helper library that simplifies response handling across multiple Go web frameworks. Write once, run anywhere.

Why Reply?

  • Framework agnostic - Works seamlessly with Gin, Echo, Fiber, and net/http
  • Consistent response format - Standardized response structure across your API
  • Chainable API - Clean and readable method chaining
  • Multiple formats - JSON, XML, HTML, Binary, Text, and Streaming support
  • Smart error handling - Code aliases and custom transformers

Installation

go get github.com/chesta132/reply-go

For specific framework adapters:

# Gin
go get github.com/gin-gonic/gin github.com/chesta132/reply-go/adapter/gin

# Echo
go get github.com/labstack/echo/v4 github.com/chesta132/reply-go/adapter/echo

# Fiber
go get github.com/gofiber/fiber/v2 github.com/chesta132/reply-go/adapter/fiber

Quick Start

Setup Client
import (
    "github.com/chesta132/reply-go/reply"
    "github.com/chesta132/reply-go/adapter/nethttp"
)

var client = reply.NewClient(reply.Client{
    CodeAliases: map[string]int{
        "NOT_FOUND":    404,
        "BAD_REQUEST":  400,
        "SERVER_ERROR": 500,
    },
    DefaultHeaders: map[string]string{
        "Content-Type": "application/json",
    },
})
Basic Usage (net/http)
func handler(w http.ResponseWriter, r *http.Request) {
    rp := client.New(adapter.AdaptHttp(w))

    // Success response
    rp.Success(map[string]string{
        "message": "Hello World!",
    }).OkJSON()
}

Output:

{
  "meta": {
    "status": "SUCCESS"
  },
  "data": {
    "message": "Hello World!"
  }
}
With Gin
import "github.com/chesta132/reply-go/adapter/gin"

func handler(c *gin.Context) {
    rp := client.New(adapter.AdaptGin(c))

    users := []User{
        {ID: 1, Name: "Alice"},
        {ID: 2, Name: "Bob"},
    }

    rp.Success(users).OkJSON()
}
With Echo
import "github.com/chesta132/reply-go/adapter/echo"

func handler(c echo.Context) error {
    rp := client.New(adapter.AdaptEcho(c))

    rp.Success("Hello from Echo!").OkText()
    return nil
}
With Fiber
import "github.com/chesta132/reply-go/adapter/fiber"

func handler(c *fiber.Ctx) error {
    rp := client.New(adapter.AdaptFiber(c))

    rp.Success("<h1>Hello Fiber!</h1>").OkHTML()
    return nil
}

Features

Error Response
// Simple error
rp.Error("NOT_FOUND", "User not found").FailJSON()

// Error with details
rp.Error("VALIDATION_ERROR", "Invalid input", reply.OptErrorPayload{
    Details: "Email format is invalid",
    Field:   "email",
}).FailJSON(400)

Output:

{
  "meta": {
    "status": "ERROR"
  },
  "data": {
    "code": "NOT_FOUND",
    "message": "User not found",
    "details": "Email format is invalid",
    "field": "email"
  }
}
Pagination
users := fetchUsers(limit, offset)

rp.Success(users).
    Paginate(limit, offset, len(users)).
    OkJSON()

Output:

{
  "meta": {
    "status": "SUCCESS",
    "pagination": {
      "nextOffset": 20,
      "hasNext": true
    }
  },
  "data": [...]
}
accessToken := createAccessToken()
refreshToken := createRefreshToken()

rp.SetCookies(
  http.Cookie{Name: "access_token", Value: accessToken},
  http.Cookie{Name: "refresh_token", Value: refreshToken},
)
Multiple Deferred Functions
rp.Defer(
    func() { cleanupTempFiles() },
    func() { closeConnection() },
    func() { log.Println("Request completed") },
)
Response Formats
JSON
rp.Success(data).OkJSON()           // 200
rp.Success(data).CreatedJSON()      // 201
rp.Error("ERR", "msg").FailJSON()   // from CodeAliases or 500
XML
rp.Success(data).OkXML()            // 200
rp.Success(data).CreatedXML()       // 201
rp.Error("ERR", "msg").FailXML(400) // 400
Text
rp.Success("Plain text").OkText()
rp.Success("Created!").CreatedText()
HTML
rp.Success("<h1>Hello</h1>").OkHTML()
rp.Success("<p>Created</p>").CreatedHTML()
Binary
imageData := []byte{...}
rp.Success(imageData).OkBinary()
Streaming
file, _ := os.Open("video.mp4")
defer file.Close()

rp.Success(reply.Stream{
    Data:        file,
    ContentType: "video/mp4",
}).OkStream()

Advanced Usage

Custom Transformer

Transform the response structure before sending:

client := reply.NewClient(reply.Client{
    Transformer: func(data any, meta reply.Meta) any {
        return map[string]any{
            "success": meta.Status == "SUCCESS",
            "payload": data,
            "timestamp": time.Now().Unix(),
        }
    },
})
Finalizer Hook

Execute custom logic before sending responses:

client := reply.NewClient(reply.Client{
    Finalizer: func(data any, meta reply.Meta) {
        // Log response before sending
        log.Printf("Sending response: status=%s", meta.Status)
    },
})
Code Aliases

Map error codes to HTTP status codes:

client := reply.NewClient(reply.Client{
    CodeAliases: map[string]int{
        "USER_NOT_FOUND":      404,
        "INVALID_CREDENTIALS": 401,
        "RATE_LIMITED":        429,
        "MAINTENANCE":         503,
    },
})

// Automatically uses status code from alias
rp.Error("RATE_LIMITED", "Too many requests").FailJSON()
// Response with status 429
Default Headers

Set headers that will be applied to all responses:

client := reply.NewClient(reply.Client{
    DefaultHeaders: map[string]string{
        "X-API-Version": "v1.0",
        "X-Powered-By":  "Reply-Go",
    },
})

Response Structure

Success Response
{
  "meta": {
    "status": "SUCCESS",
    "information": "optional info",
    "pagination": {
      "nextOffset": 20,
      "hasNext": true
    }
  },
  "data": {...}
}
Error Response
{
  "meta": {
    "status": "ERROR"
  },
  "data": {
    "code": "ERROR_CODE",
    "message": "Human readable message",
    "details": "Optional debug info",
    "field": "fieldName"
  }
}

API Reference

Core Methods
  • NewClient(config Client) - Create a new client with configuration
  • Success(data any) - Set success response
  • Error(code, message string, opt ...OptErrorPayload) - Set error response
  • Paginate(limit, offset, total int) - Add pagination information
  • Defer(funcs ...func()) - Register functions to execute before sending response
  • SetCookies(cookies ...http.Cookie) - Add Set-Cookie header by http.Cookie
Response Methods
Method Status Code Format
NoContent() 204 -
ReplyJSON() Custom JSON
OkJSON() 200 JSON
CreatedJSON() 201 JSON
FailJSON(code ...int) Custom/500 JSON
ReplyXML() Custom XML
OkXML() 200 XML
CreatedXML() 201 XML
FailXML(code ...int) Custom/500 XML
ReplyText() Custom Text
OkText() 200 Text
CreatedText() 201 Text
ReplyHTML() Custom HTML
OkHTML() 200 HTML
CreatedHTML() 201 HTML
ReplyBinary() Custom Binary
OkBinary() 200 Binary
CreatedBinary() 201 Binary
ReplyStream() Custom Stream
OkStream() 200 Stream
CreatedStream() 201 Stream

Framework Support

Framework Import Path Adapter Function
net/http adapter/nethttp AdaptHttp(w)
Gin adapter/gin AdaptGin(c)
Echo adapter/echo AdaptEcho(c)
Fiber adapter/fiber AdaptFiber(c)

Real-World Examples

User API with Pagination And Defer

type FindManyPayload struct {
   Ids []string `json:"ids"`
}
func FindUsers(c *gin.Context) {
    rp := client.New(adapter.AdaptGin(c))

    limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
    offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
    payload := FindManyPayload{}
    c.ShouldBindJSON(&payload)

    tx := db.Begin()

    // Defer cleanup - will run before response is sent
    rp.Defer(func() {
        if tx != nil {
            tx.Close()
        }
    })

    users, err := findUsers(tx, payload.Ids, limit, offset)
    if err != nil {
        rp.Error("FIND_FAILED", err.Error()).FailJSON()
        // tx is closed
        return
    }

    rp.Success(users).
        Paginate(limit, offset, len(users)).
        OkJSON()
}
File Upload Handler
func UploadFile(c echo.Context) error {
    rp := client.New(echoadapter.AdaptEcho(c))

    file, err := c.FormFile("file")
    if err != nil {
        rp.Error("INVALID_FILE", "No file uploaded").FailJSON(400)
        return nil
    }

    // Process file...

    rp.Success(map[string]any{
        "filename": file.Filename,
        "size":     file.Size,
    }).CreatedJSON()

    return nil
}
Video Streaming
func StreamVideo(w http.ResponseWriter, r *http.Request) {
    rp := client.New(nethttpadapter.AdaptHttp(w))

    file, err := os.Open("video.mp4")
    if err != nil {
        rp.Error("NOT_FOUND", "Video not found").FailJSON(404)
        return
    }
    defer file.Close()

    rp.Success(reply.Stream{
        Data:        file,
        ContentType: "video/mp4",
    }).OkStream()
}

Contributing

Pull requests are welcome! Feel free to contribute by adding support for more frameworks or new features.

License

MIT

Directories

Path Synopsis
Package adapter provides a unified interface for sending HTTP responses across different web frameworks (Gin, Echo, Fiber) and standard net/http.
Package adapter provides a unified interface for sending HTTP responses across different web frameworks (Gin, Echo, Fiber) and standard net/http.
echo module
gin module
echo module
gin module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL