Skip to main content

Quick start

ogen is a powerful and fast OpenAPI v3 code generator for Go.

Prepare environment

Create Go module.

go mod init <project>

Installation

Then, install the ogen tool.

go install -v github.com/ogen-go/ogen/cmd/ogen@latest

Generate code

Setup generator

Download petstore example.

wget https://raw.githubusercontent.com/ogen-go/web/main/examples/petstore.yml

Create generate.go file:

generate.go
package project

//go:generate go run github.com/ogen-go/ogen/cmd/ogen@latest --target petstore --clean petstore.yml

Then go to the root directory of your module, and run:

go generate ./...

ogen will generate new folder with several files

petstore
├── oas_cfg_gen.go
├── oas_client_gen.go
├── oas_handlers_gen.go
├── oas_interfaces_gen.go
├── oas_json_gen.go
├── oas_middleware_gen.go
├── oas_parameters_gen.go
├── oas_request_decoders_gen.go
├── oas_request_encoders_gen.go
├── oas_response_decoders_gen.go
├── oas_response_encoders_gen.go
├── oas_router_gen.go
├── oas_schemas_gen.go
├── oas_server_gen.go
├── oas_unimplemented_gen.go
├── oas_validators_gen.go

Using generated server

To get started, implement Handler interface generated by ogen tool.

// Handler handles operations described by OpenAPI v3 specification.
type Handler interface {
// AddPet implements addPet operation.
//
// Add a new pet to the store.
//
// POST /pet
AddPet(ctx context.Context, req *Pet) (*Pet, error)
// DeletePet implements deletePet operation.
//
// Deletes a pet.
//
// DELETE /pet/{petId}
DeletePet(ctx context.Context, params DeletePetParams) error
// GetPetById implements getPetById operation.
//
// Returns a single pet.
//
// GET /pet/{petId}
GetPetById(ctx context.Context, params GetPetByIdParams) (GetPetByIdRes, error)
// UpdatePet implements updatePet operation.
//
// Updates a pet in the store.
//
// POST /pet/{petId}
UpdatePet(ctx context.Context, params UpdatePetParams) error
}
package main

import (
"context"
"sync"

petstore "<project>/petstore"
)

type petsService struct {
pets map[int64]petstore.Pet
id int64
mux sync.Mutex
}

func (p *petsService) AddPet(ctx context.Context, req *petstore.Pet) (*petstore.Pet, error) {
p.mux.Lock()
defer p.mux.Unlock()

p.pets[p.id] = *req
p.id++
return req, nil
}

func (p *petsService) DeletePet(ctx context.Context, params petstore.DeletePetParams) error {
p.mux.Lock()
defer p.mux.Unlock()

delete(p.pets, params.PetId)
return nil
}

func (p *petsService) GetPetById(ctx context.Context, params petstore.GetPetByIdParams) (petstore.GetPetByIdRes, error) {
p.mux.Lock()
defer p.mux.Unlock()

pet, ok := p.pets[params.PetId]
if !ok {
// Return Not Found.
return &petstore.GetPetByIdNotFound{}, nil
}
return &pet, nil
}

func (p *petsService) UpdatePet(ctx context.Context, params petstore.UpdatePetParams) error {
p.mux.Lock()
defer p.mux.Unlock()

pet := p.pets[params.PetId]
pet.Status = params.Status
if val, ok := params.Name.Get(); ok {
pet.Name = val
}
p.pets[params.PetId] = pet

return nil
}

Run server

Now, we are ready to run our server!

main.go
package main

import (
"log"
"net/http"

petstore "<project>/petstore"
)

func main() {
// Create service instance.
service := &petsService{
pets: map[int64]petstore.Pet{},
}
// Create generated server.
srv, err := petstore.NewServer(service)
if err != nil {
log.Fatal(err)
}
if err := http.ListenAndServe(":8080", srv); err != nil {
log.Fatal(err)
}
}

Check that server started by creating pet

curl -X "POST" -H "Content-Type: application/json" --data "{\"name\":\"Cat\"}" http://localhost:8080/pet
{"name":"Cat"}

Using generated client

Let's make a simple app that gets the pet by id.

main.go
package main

import (
"bytes"
"context"
"encoding/json"
"flag"
"os"

"github.com/fatih/color"

petstore "<project>/petstore"
)

func run(ctx context.Context) error {
var arg struct {
BaseURL string
ID int64
}
flag.StringVar(&arg.BaseURL, "url", "http://localhost:8080", "target server url")
flag.Int64Var(&arg.ID, "id", 1, "pet id to request")
flag.Parse()

client, err := petstore.NewClient(arg.BaseURL)
if err != nil {
return fmt.Errorf("create client: %w", err)
}

res, err := client.GetPetById(ctx, petstore.GetPetByIdParams{
PetId: arg.ID,
})
if err != nil {
return fmt.Errorf("get pet %d: %w", arg.ID, err)
}

switch p := res.(type) {
case *petstore.Pet:
data, err := p.MarshalJSON()
if err != nil {
return err
}
var out bytes.Buffer
if err := json.Indent(&out, data, "", " "); err != nil {
return err
}
color.New(color.FgGreen).Println(out.String())
case *petstore.GetPetByIdNotFound:
return errors.New("not found")
}

return nil
}

func main() {
if err := run(context.Background()); err != nil {
color.New(color.FgRed).Printf("%+v\n", err)
os.Exit(2)
}
}