# Unveiling: How to Build Elegant API Interfaces with Gin Framework

## From Repetitive Code to Elegant Encapsulation

In Gin framework development, have you ever encountered this scenario: each API endpoint requires repetitive code for parameter binding, error handling, and response formatting? This not only increases code volume but also reduces development efficiency and code maintainability.

Let's look at a typical example:

```go
func getUser(ctx *gin.Context) {
    var in UserInput
    if err := ctx.ShouldBindQuery(&in); err != nil {
        ctx.JSON(400, gin.H{"error": err.Error()})
        return
    }
    user, err := userService.GetUser(in.ID)
    if err != nil {
        ctx.JSON(500, gin.H{"error": "Server error"})
        return
    }
    ctx.JSON(200, user)
}
```

This code pattern repeatedly appears in projects, leading to:
1. Large amounts of boilerplate code
2. Scattered error handling logic
3. Inconsistent response formats
4. Business logic buried in technical details

This article will introduce how to solve these problems through the elegant encapsulation solution of `web.WrapH`.

## Elegant Solution

`web.WrapH` is a generic-based encapsulation function that solves the above problems through:

1. Automatic parameter binding
2. Unified error handling
3. Standardized response formatting
4. Allowing developers to focus on business logic

### Core Implementation

`web.WrapH`'s implementation is based on the generic feature introduced in Go 1.18, using type parameters `I` and `O` to represent input and output types respectively:

```go
func WrapH[I any, O any](fn func(*gin.Context, *I) (O, error)) gin.HandlerFunc {
    return func(c *gin.Context) {
        var in I
        if unsafe.Sizeof(in) != 0 {
            switch c.Request.Method {
            case http.MethodGet:
                if err := c.ShouldBindQuery(&in); err != nil {
                    Fail(c, ErrBadRequest.With(HanddleJSONErr(err).Error()))
                    return
                }
            case http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch:
                if c.Request.ContentLength > 0 {
                    if err := c.ShouldBindJSON(&in); err != nil {
                        Fail(c, ErrBadRequest.With(HanddleJSONErr(err).Error()))
                        return
                    }
                }
            }
        }
        out, err := fn(c, &in)
        if err != nil {
            Fail(c, err)
            return
        }
        Success(c, out)
    }
}
```

Key points of this implementation:
1. Using generics to support arbitrary input and output types
2. Automatically selecting parameter binding methods based on request method
3. Unified error handling and response formatting
4. Skipping binding for zero-value parameters

### Usage Example

With `web.WrapH`, the code becomes exceptionally concise:

```go
func getUser(ctx *gin.Context, in *UserInput) (*UserOutput, error) {
    return userService.GetUser(in.ID)
}
```

### Advantage Analysis

`web.WrapH` allows developers to focus on business logic implementation rather than repetitive boilerplate code. Through generics and unified error handling mechanisms, it achieves code conciseness and maintainability. At compile time, generics ensure type safety, prevent runtime type errors, and provide better IDE support.

## Practical Application Scenarios

### Scenario 1: Query Single User

```go
// Route definition
router.GET("/users/:id", web.WrapH(getUser))

// Handler function, using `*struct{}` empty value to avoid parameter binding
func getUser(ctx *gin.Context, in *struct{}) (*UserOutput, error) {
    id := ctx.Param("id")
    return userService.GetUser(id)
}
```

This scenario demonstrates how to handle GET requests without request parameters, using `ctx.Param` to get path parameters.

### Scenario 2: Query User List

```go
// Route definition
router.GET("/users", web.WrapH(listUsers))

// Request parameters (defined in user package)
type FindUsersInput struct {
    Page int    `form:"page"`
    Size int    `form:"size"`
    Name string `form:"name"`
}

// Handler function
func findUsers(ctx *gin.Context, in *FindUsersInput) (*FindUsersOutput, error) {
    return userService.ListUsers(in)
}
```

This scenario demonstrates how to use `form` tags to handle query parameters.

### Scenario 3: Update User Information

```go
// Route definition
router.PUT("/users/:id", web.WrapH(updateUser))

// Request parameters
type UpdateUserInput struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

// Handler function
func updateUser(ctx *gin.Context, in *UpdateUserInput) (*UserOutput, error) {
    id := ctx.Param("id")
    return userService.UpdateUser(in, id)
}
```

This scenario demonstrates how to handle PUT requests with both path parameters and request body parameters.

### Scenario 4: Delete User

```go
// Route definition
router.DELETE("/users/:id", web.WrapH(deleteUser))

// Handler function
func deleteUser(ctx *gin.Context, in *struct{}) (any, error) {
    id := ctx.Param("id")
    return userService.DeleteUser(id)
}
```

This scenario demonstrates how to handle DELETE requests and handle cases with no return value.

For complex scenarios like file downloads or non-CRUD operations, you can use `gin.HandlerFunc` instead of `web.WrapH`.

## Best Practices

- Use structs to define clear input and output parameters, improving code readability
- Use tags appropriately, such as `json`, `form`, etc.
- Add necessary parameter validation
- Use pointer types to avoid unnecessary memory allocation
- Design APIs following RESTful style

## Summary

By using `web.WrapH`, we can:
1. Significantly reduce repetitive code
2. Improve code maintainability
3. Unify error handling
4. Enhance development efficiency

This encapsulation approach is particularly suitable for team collaboration, helping teams quickly build high-quality API services.

## About goddd

The `web.WrapH` introduced in this article is a core component of the [goddd](https://github.com/ixugo/goddd) project. goddd is a Go project based on DDD (Domain-Driven Design) principles, providing a series of tools and best practices to help developers build maintainable and extensible applications.

If you're interested in the content introduced in this article, welcome to visit the [goddd project](https://github.com/ixugo/goddd) for more details. The project provides complete example code and detailed documentation to help you get started quickly.

## Related Resources

- [Gin Framework Official Documentation](https://gin-gonic.com/docs/)
- [Go Generics Tutorial](https://go.dev/doc/tutorial/generics)
- [goddd Project Source Code](https://github.com/ixugo/goddd)