Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Go-Swagger in production
Search
Ilya Kaznacheev
June 25, 2020
Programming
580
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Go-Swagger in production
Ilya Kaznacheev
June 25, 2020
More Decks by Ilya Kaznacheev
See All by Ilya Kaznacheev
Road to four nines
dreamworm
0
53
Many Layers of Availability
dreamworm
0
120
Stateful Solutions: A Hands-On Guide to FSM in Golang
dreamworm
0
230
CQRS
dreamworm
0
210
Building a Cloud-Native PaaS
dreamworm
0
180
Distributed System State Management: When Transactions Are Long and SLA Is High
dreamworm
0
180
How To Create Saga-Free Distributed Transactions
dreamworm
0
98
Architectural decisions in building distributed systems
dreamworm
0
54
Распределенные транзакции без саг
dreamworm
0
230
Other Decks in Programming
See All in Programming
setup-vp GitLab対応の裏側
naokihaba
0
150
プロダクトコードからライブラリの境界を見つける
elmetal
PRO
0
110
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
370
カツオ、ご期待ください
suneo3476
0
150
大喜利で理解するLLM as a Judge / Understanding LLM-as-a-Judge through Ogiri
rockname
0
190
SREの越境 / SRE Collaboration
y0hgi
2
300
Java 27新機能 / Java 27 new features
kishida
2
200
スマートフォンでモールス信号を送受信する 〜スマートフォンのLEDとカメラで作る光通信の設計と実装〜
atsuki_seo
0
230
Kubernetes祭り #2 OP/ED
bells17
0
100
技術的負債を組織課題として解く-増えすぎたマイクロサービスとの戦い-
reimaru
1
2.9k
APNsからLive Activityを開始する話
yumnumm
0
150
FreeBSDでZabbixを動かす
kenkino
0
360
Featured
See All Featured
Marketing to machines
jonoalderson
1
5.9k
The Curse of the Amulet
leimatthew05
3
15k
4 Signs Your Business is Dying
shpigford
187
23k
The Cost Of JavaScript in 2023
addyosmani
55
10k
How to build a perfect <img>
jonoalderson
1
6.1k
Git: the NoSQL Database
bkeepers
PRO
433
67k
GitHub's CSS Performance
jonrohan
1033
470k
SEO for Brand Visibility & Recognition
aleyda
0
4.8k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.6k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.6k
Unsuck your backbone
ammeep
672
58k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
1
450
Transcript
Go-Swagger in production wins and pitfalls
Ilya Kaznacheev Remote Backend SWE Founder of Golang Voronezh Host
of Z-Namespace podcast Organizer of conference and meetups Coffee geek
what swagger is?
None
SOAP JSON-PRC GraphQL gRPC OData REST
Representational state transfer (REST) is a software architectural style that
defines a set of constraints to be used for creating Web services Wikipedia
None
swagger: "2.0" info: title: Pet API version: "1.0.0" basePath: /api
schemes: - http paths: /pets: get: summary: List all pets parameters: - name: limit in: query description: "How many items to return at one time" required: true type: integer responses: 200: description: an paged array of pets 400: description: unexpected error
None
None
why do we use swagger?
my team trying to sync API changes...
None
go-swagger
code generation swagger generate server -t internal/api --exclude-main
generated code structure internal/api ├ models │ └ ... └
restapi ├ operations │ └ ... ├ configure_<your_service_name>.go ├ doc.go ├ embedded_spec.go └ server.go
our code generation rm -rf internal/api && mkdir -p internal/api
swagger generate server -t internal/api --exclude-main go mod tidy
? and we're all set
NO
there are some problems - go-swagger is a framework, not
a library - plenty of generated types for everything - incompatible with popular http-libraries
let’s fix ’em all!
serving net/http handlers type CustomResponder func(http.ResponseWriter, runtime.Producer) func (c CustomResponder)
WriteResponse(w http.ResponseWriter, p runtime.Producer) { c(w, p) } func MetricsHandler(p instruments.GetMetricsParams) middleware.Responder { return CustomResponder(func(w http.ResponseWriter, _ runtime.Producer) { promhttp.Handler().ServeHTTP(w, p.HTTPRequest) }) }
simple middleware api := operations.NewSwaggerPetstoreAPI(swaggerSpec) api.InstrumentsGetMetricsHandler = instruments.GetMetricsHandlerFunc(MetricsHandler) api.AddMiddlewareFor("GET", "/metrics",
SomeMiddleware) srv := restapi.NewServer(api) srv.Serve()
middleware with custom handler h := api.Serve(nil) r := chi.NewRouter()
r.Use( middleware.Recoverer, ) r.With(AuthMiddleware).Group(func(r chi.Router) { r.Handle("/user/*", h) }) r.Mount("/", h) srv.ConfigureAPI() srv.SetHandler(r) srv.Serve()
setup outside of configure_<your_service_name>.go api.Logger = log.Printf api.HTMLProducer = runtime.TextProducer()
srv := restapi.NewServer(api) srv.EnabledListeners = []string{"http"} srv.Port = conf.HTTPPort srv.Host = conf.HTTPAddr
custom method names /store/order/{orderId}/items: get: tags: - store summary: Find
purchase order items parameters: - name: orderId in: path required: true type: integer func GetOrderItems( param store.GetStoreOrderOrderIDItemsParams, ) middleware.Responder { items, err := getOrderItems(param.OrderID) if err != nil { return store.NewGetStoreOrderOrderIDItemsNotFound() } res := &models.OrderItems{} // // fill resopnse // return store.NewGetStoreOrderOrderIDItemsOK(). WithPayload(res) }
custom method names /store/order/{orderId}/items: get: tags: - store summary: Find
purchase order items operationId: getOrderItems parameters: - name: orderId in: path required: true type: integer func GetOrderItems( param store.GetOrderItemsParams, ) middleware.Responder { items, err := getOrderItems(param.OrderID) if err != nil { return store.NewGetOrderItemsNotFound() } res := &models.OrderItems{} // // fill resopnse // return store.NewGetOrderItemsOK(). WithPayload(res) }
validity checks OrderItems: type: object properties: message: type: string maximum:
3 # swg/internal/api/models internal/api/models/order_items.go:45:55: cannot convert m.Message (type string) to type float64
validity check cheat sheet numbers and integers - multipleOf -
maximum - minimum - exclusiveMaximum - exclusiveMinimum strings - maxLength - minLength - pattern arrays - maxItems - minItems - uniqueItems - maxContains - minContains objects - maxProperties - minProperties - required - dependentRequired any type - type - enum - const
extensions (tricks) x-omitempty x-nullable x-isnullable x-order x-go-custom-tag x-schemes x-go-name x-go-type
x-go-json-string x-go-enum-ci
Shortcuts Error: type: object required: - code - message properties:
code: type: integer message: type: string type APIError struct { code int Payload *models.Error `json:"body,omitempty"` } func (e *APIError) WriteResponse( rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(e.code) producer.Produce(rw, e.Payload) } func RespondError(code int, err error) *APIError { return &APIError{ code: code, Payload: &models.Error{code, err.Error()}, } }
unit tests func GetOrderByID(param store.GetOrderByIDParams) middleware.Responder { order := models.Order{
ID: 123, PetID: 456, Quantity: 20, Status: "approved", } if param.OrderID != order.ID { return store.NewGetOrderByIDNotFound().WithPayload(&models.ErrorMessage{ Code: http.StatusNotFound, Message: http.StatusText(http.StatusNotFound), }) } return store.NewGetOrderByIDOK().WithPayload(&order) }
unit tests tests := []struct { name string req store.GetOrderByIDParams
code int want string }{ { name: "good test", req: store.GetOrderByIDParams{OrderID: 123}, code: 200, want: `{"id":123,"petId":456,"quantity":20,"status":"approved"}`, }, { name: "bad test", req: store.GetOrderByIDParams{OrderID: 456}, code: 404, want: `{"message":"Not Found", "code":404}`, }, }
unit tests for _, tt := range tests { t.Run(tt.name,
func(t *testing.T) { rr := httptest.NewRecorder() GetOrderByID(tt.req).WriteResponse(rr, runtime.JSONProducer()) assert.JSONEq(t, tt.want, rr.Body.String(), "wrong response body") assert.Equal(t, tt.code, rr.Code, "wrong response code") }) }
None
helpful links json-schema.org/specification.html swagger.io/docs/specification/2-0 goswagger.io bit.ly/go-swagger-in-production
ilyakaznacheev