79578319

Date: 2025-04-17 01:39:57
Score: 0.5
Natty:
Report link

Reference: https://github.com/oapi-codegen/oapi-codegen https://github.com/oapi-codegen/oapi-codegen/tree/main/examples/petstore-expanded/gin

Environment: go1.24.2, https://go.dev/dl/

  1. Init go module

    mkdir petstore
    cd petstore
    go mod init petstore
    
  2. Get tool oapi-codegen

    go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
    

    After this step, the tool is added in the go.mod

  3. Prepare the config for oapi-codegen The full config options can be referenced in this schema is https://github.com/oapi-codegen/oapi-codegen/blob/main/configuration-schema.json

    server.cfg.yaml

    package: main
    generate:
    gin-server: true
    output: petstore-server.gen.go
    

    types.cfg.yaml

    package: main
    generate:
    models: true
    output: petstore-types.gen.go
    
  4. Prepare the openapi file petstore-expanded.yaml

    openapi: "3.0.0"
    info:
    version: 1.0.0
    title: Swagger Petstore
    description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification
    termsOfService: https://swagger.io/terms/
    contact:
        name: Swagger API Team
        email: [email protected]
        url: https://swagger.io
    license:
        name: Apache 2.0
        url: https://www.apache.org/licenses/LICENSE-2.0.html
    servers:
    - url: https://petstore.swagger.io/api
    paths:
    /pets:
        get:
        summary: Returns all pets
        description: |
            Returns all pets from the system that the user has access to
            Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia.
    
            Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien.
        operationId: findPets
        parameters:
            - name: tags
            in: query
            description: tags to filter by
            required: false
            style: form
            schema:
                type: array
                items:
                type: string
            - name: limit
            in: query
            description: maximum number of results to return
            required: false
            schema:
                type: integer
                format: int32
        responses:
            '200':
            description: pet response
            content:
                application/json:
                schema:
                    type: array
                    items:
                    $ref: '#/components/schemas/Pet'
            default:
            description: unexpected error
            content:
                application/json:
                schema:
                    $ref: '#/components/schemas/Error'
        post:
        summary: Creates a new pet
        description: Creates a new pet in the store. Duplicates are allowed
        operationId: addPet
        requestBody:
            description: Pet to add to the store
            required: true
            content:
            application/json:
                schema:
                $ref: '#/components/schemas/NewPet'
        responses:
            '200':
            description: pet response
            content:
                application/json:
                schema:
                    $ref: '#/components/schemas/Pet'
            default:
            description: unexpected error
            content:
                application/json:
                schema:
                    $ref: '#/components/schemas/Error'
    /pets/{id}:
        get:
        summary: Returns a pet by ID
        description: Returns a pet based on a single ID
        operationId: findPetByID
        parameters:
            - name: id
            in: path
            description: ID of pet to fetch
            required: true
            schema:
                type: integer
                format: int64
        responses:
            '200':
            description: pet response
            content:
                application/json:
                schema:
                    $ref: '#/components/schemas/Pet'
            default:
            description: unexpected error
            content:
                application/json:
                schema:
                    $ref: '#/components/schemas/Error'
        delete:
        summary: Deletes a pet by ID
        description: deletes a single pet based on the ID supplied
        operationId: deletePet
        parameters:
            - name: id
            in: path
            description: ID of pet to delete
            required: true
            schema:
                type: integer
                format: int64
        responses:
            '204':
            description: pet deleted
            default:
            description: unexpected error
            content:
                application/json:
                schema:
                    $ref: '#/components/schemas/Error'
    components:
    schemas:
        Pet:
        allOf:
            - $ref: '#/components/schemas/NewPet'
            - required:
                - id
            properties:
                id:
                type: integer
                format: int64
                description: Unique id of the pet
    
        NewPet:
        required:
            - name
        properties:
            name:
            type: string
            description: Name of the pet
            tag:
            type: string
            description: Type of the pet
    
        Error:
        required:
            - code
            - message
        properties:
            code:
            type: integer
            format: int32
            description: Error code
            message:
            type: string
            description: Error message
    
  5. Generate the source code

    generate server code

    go tool oapi-codegen -config server.cfg.yaml petstore-expanded.yaml
    

    generate models code

    go tool oapi-codegen -config types.cfg.yaml  petstore-expanded.yaml
    
  6. add missing dependencies

    go mod tidy
    
  7. implement petstore.go based on the generated interface

    package main
    
    import (
        "fmt"
        "net/http"
        "sync"
    
        "github.com/gin-gonic/gin"
    )
    
    type PetStore struct {
        Pets   map[int64]Pet
        NextId int64
        Lock   sync.Mutex
    }
    
    func NewPetStore() *PetStore {
        return &PetStore{
            Pets:   make(map[int64]Pet),
            NextId: 1000,
        }
    }
    
    // sendPetStoreError wraps sending of an error in the Error format, and
    // handling the failure to marshal that.
    func sendPetStoreError(c *gin.Context, code int, message string) {
        petErr := Error{
            Code:    int32(code),
            Message: message,
        }
        c.JSON(code, petErr)
    }
    
    // FindPets implements all the handlers in the ServerInterface
    func (p *PetStore) FindPets(c *gin.Context, params FindPetsParams) {
        p.Lock.Lock()
        defer p.Lock.Unlock()
    
        var result []Pet
    
        for _, pet := range p.Pets {
            if params.Tags != nil {
                // If we have tags,  filter pets by tag
                for _, t := range *params.Tags {
                    if pet.Tag != nil && (*pet.Tag == t) {
                        result = append(result, pet)
                    }
                }
            } else {
                // Add all pets if we're not filtering
                result = append(result, pet)
            }
    
            if params.Limit != nil {
                l := int(*params.Limit)
                if len(result) >= l {
                    // We're at the limit
                    break
                }
            }
        }
        c.JSON(http.StatusOK, result)
    }
    
    func (p *PetStore) AddPet(c *gin.Context) {
        // We expect a NewPet object in the request body.
        var newPet NewPet
        err := c.Bind(&newPet)
        if err != nil {
            sendPetStoreError(c, http.StatusBadRequest, "Invalid format for NewPet")
            return
        }
        // We now have a pet, let's add it to our "database".
    
        // We're always asynchronous, so lock unsafe operations below
        p.Lock.Lock()
        defer p.Lock.Unlock()
    
        // We handle pets, not NewPets, which have an additional ID field
        var pet Pet
        pet.Name = newPet.Name
        pet.Tag = newPet.Tag
        pet.Id = p.NextId
        p.NextId++
    
        // Insert into map
        p.Pets[pet.Id] = pet
    
        // Now, we have to return the NewPet
        c.JSON(http.StatusCreated, pet)
    }
    
    func (p *PetStore) FindPetByID(c *gin.Context, petId int64) {
        p.Lock.Lock()
        defer p.Lock.Unlock()
    
        pet, found := p.Pets[petId]
        if !found {
            sendPetStoreError(c, http.StatusNotFound, fmt.Sprintf("Could not find pet with ID %d", petId))
            return
        }
        c.JSON(http.StatusOK, pet)
    }
    
    func (p *PetStore) DeletePet(c *gin.Context, id int64) {
        p.Lock.Lock()
        defer p.Lock.Unlock()
    
        _, found := p.Pets[id]
        if !found {
            sendPetStoreError(c, http.StatusNotFound, fmt.Sprintf("Could not find pet with ID %d", id))
        }
        delete(p.Pets, id)
        c.Status(http.StatusNoContent)
    }
    
    
  8. Implement the main.go

    package main
    
    import (
        "log"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        petStoreAPI := NewPetStore()
        router := gin.Default()
        RegisterHandlers(router, petStoreAPI)
        log.Println("Starting server on :8080")
        if err := router.Run(":8080"); err != nil {
            log.Fatalf("Failed to start server: %v", err)
        }
    }
    
    
  9. Start the server

    go run .
    
  10. curl the api

    curl -I -X GET localhost:8080/pets
    
    HTTP/1.1 200 OK
    Content-Type: application/json; charset=utf-8
    Date: Thu, 17 Apr 2025 01:30:45 GMT
    Content-Length: 4
    
Reasons:
  • Probably link only (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (1):
Posted by: Guilin Liang