goswagger เบื้องต้น
Published: 2024-02-08 08:47:44

คนที่เขียน API ด้วย Go ถ้าอยากจะทำ swagger ให้คนเข้ามากดๆเล่นได้ มันมีวิธีทำหลายท่ามากๆ วันนี้จะมาแนะนำท่าของคนขี้เกียจเขียน annotation ซึ่งผมพยายามลองมาหลายท่า(ซึ่งอาจจะยังไม่หมด) แต่ก็เจอท่านี้ ซึ่งเข้าใจได้ไม่ยาก ใช้ง่ายโดยใช้ 

[https://goswagger.io/](https://goswagger.io/)

ก่อนอื่นก็ไปดูการติดตั้งในเพจเขาก่อน [https://goswagger.io/install.html](https://goswagger.io/install.html) ซึ่งผมเลือกใช้วิธี **Installing from source** นะครับ

ถ้าเราอ่าน docs เข้าก็จะมี blog อ้างอิงที่ [Generate swagger specification from Go source code](https://medium.com/@pedram.esmaeeli/generate-swagger-specification-from-go-source-code-648615f7b9d9)

แต่เดี๋ยวผมจะเล่าให้อ่านกันในแบบของผมดูว่าจะเข้าใจไหมนะครับ

หลังจากติดตั้งตามคู่มือเรียบร้อย เราจะได้คำสั่ง `swagger` มาให้ใช้ โดยคำสั่งนี้มันจะสามารถ auto generate ไฟล์ swagger.yaml หรือ json ก็ได้จาก source code เพียงแต่ว่า เราอาจจะต้องไปเขียน aanotation (อีกแล้วเหรอ) แต่ว่าเขียนไม่เยอะเท่า [swaggo](https://github.com/swaggo/swag)

ก่อนอื่นให้เราไปสร้าง meta ของ docs(swagger) ไว้ที่ `/docs/docs.go` ก็คือไปสร้างไดเร็คทอรี่ชื่อ `docs` ไว้ที่ root ของ project แล้วสร้างไฟล์ docs.go ไว้ในนั้นนั่นเอง

ใน docs.go มีตัวอย่างให้แบบนี้ครับ

```go
// Package classification MyApplication.
//
// Documentation of our MyApplication API.
//
//		Schemes: http
//		BasePath: /
//		Version: 1.0.0
//		Host: localhost:8080
//
//		Consumes:
//		- application/json
//
//		Produces:
//		- application/json
//
//		Security:
//	 	- key: []
//
//		SecurityDefinitions:
//		key:
//		  type: apiKey
//		  in: header
//		  name: Authorization
//
// swagger:meta
package docs
```

ตัวอย่างนี้ผมแก้ไขนิดหน่อยให้เหมาะกับที่ผมใช้ประจำ โดยเป็นการกำหนด basepath ให้เป็น localhost:8080 เพราะเวลาเรารันที่เครื่องเราจะได้ยิง API บนเครื่องเราได้เลย

ตรงส่วน security ปกติผมจะใช้ Authorization ใน header ก็เลยกำหนดลักษณะนี้ไว้ครับ

นี่เป็นส่วนของ meta ซึ่งจะเป็นเหมือนหัวเอกสารของเราว่ามันเป็น API ของอะไร(ในตัวอย่างคือชื่อ MyApplication)

จากนั้น เราจะไปเขียน route พร้อมกับ request/response กัน ซึ่งเราสามารถจะไปเขียนไว้ที่ไหนก็ได้ใน codebase ของเรา แล้วเดี๋ยวเราจะใช้คำส่ง `swagger` ไป scan หามาให้เองครับ

ยกตัวอย่างเช่น ผมจะไปเขียน Handler ไว้ที่ ./app/user
ผมใช้วิธีไปสร้างไฟล์ docs.go ไว้ในนั้นอีกที เพื่อจะได้รู้ว่านี่คือส่วนของ docs โดยผมเขียนแบบนี้

```go
package user

// swagger:route GET /users user getUserEndpointID
// responses:
//
//	200: userResponse
//	400: userBadRequestResponse
//	500: userErrorResponse

// swagger:response userResponse
type swaggerUserResponseWrapper struct {
	// in:body
	Body User
}

type User struct {
    Name string `json:"name"`
    Email string `json:"email"`
}

// swagger:response userBadRequestResponse
type swaggerUserBadRequestResponseWrapper struct {
	// in:body
	Body ErrorResponse
}

// swagger:response userErrorResponse
type swaggerUserErrorResponseWrapper struct {
	// in:body
	Body ErrorResponse
}

type ErrorResponse struct {
    Message string `json:"message"`
}
```

อธิบายแบบนี้นะครับ

บรรทัด swagger:route syntax คือ
> swagger:route [method] [path pattern] [?tag1 tag2 tag3] [operation id]

**tag** ก็คล้ายๆกับการจัดกลุ่ม โดยเราสามารถมีหลายๆ route ที่มี tag ชื่อเดียวกัน เราจะเห็นการจัดกลุ่มตาม tag ตอนที่เปิดหน้า swagger ขึ้นมา
**operation id** เป็น id ที่จะใช้ให้ input(parameter) refer ถึง จะได้รู้ว่าเป็น input ของ route ไหน

บรรทัด response: มันจะมีรายการของ response ว่า API ของเราจะมี response ได้กี่แบบ โดยจะกำหนดเป็น http status code จับคู่กับ response

บรรทัด swagger:response จะตามด้วยชื่อที่เราตั้ง โดยชื่อนี้จะถูกเอาไปจับคู่กับ status code ของ responses ด้านบน

บรรทัด //in:body เป็นการบอกว่า response ของเรานั้นเป็นแบบ Object โดยจะมี type User เป็นตัว represent ง่ายๆเลย เดี๋ยวมันจะสร้าง json ตาม struct ของ User ให้เอง

เอาแค่นี้ก่อน สมมุติว่า API ของเราเป็แบบ GET method แบบไม่ต้องส่งอะไรมาเลย แล้วจะได้ response ออกไปนะครับ หลังจากนี้เราจะไปใช้คำสั่ง `swagger` กัน

```zsh
swagger generate spec -o ./docs/swagger.yaml --scan-models
```

ผมใช้คำสั่งนี้เพื่อให้มัน scan หา annotation กับ model ต่างๆแล้วสร้างไฟล์ swagger.yaml ไปวางไว้ใน ./docs นะครับ

จากนั้นใช้คำสั่ง

```zsh
swagger serve -F=swagger ./docs/swagger.yaml
```

มันจะ render Swagger แล้วเปิด browser ให้เราเลย

ทีนี้ โดยปกติ API เราก็อาจจะมีแบบ get โดยส่ง path parameter เข้ามา เราก็จะทำแบบนี้ครับ

```go
// swagger:route GET /users/{id} user getUserEndpointID

// swagger:parameters getUserEndpointID
type swaggerUserParamsWrapper struct {
	// Id of a User
	//
	// In: path
	ID string `json:"id"`
}
```

แบบนี้ก็คือ เราจะรับค่า id เข้ามาเป็น path parameter

ต่อมา ถ้า request เข้ามาเป็น json ล่ะจะทำแบบไหน

```go
// swagger:route POST /users user postUserEndpointID
// responses:
//
//	201:

// swagger:parameters postUserEndpointID
type swaggerAddUserParamsWrapper struct {
	// in:body
	Body User
}
```

ที่บรรทัด // swagger:parameters postUserEndpointID เราจับคู่ postUserEndpointID กับ route ที่ ID เดียวกัน

นี่เป็นตัวอย่างเบื้องต้นนะครับ เมื่อเราได้ swagger.yaml มาแล้ว สำหรับคนที่ใช้ vscode ถ้ามี extension ก็จะสามารถ try ใน vscode ได้เลย ไม่ต้องให้ swagger serve ด้วย สะดวกสุดๆ

สำหรับรายละเอียดเพิ่มเติมต่างๆ อ่านใน document ของ goswagger ได้เลย ซึ่งก็มีไม่ครบ 🤣 ถ้าอยากได้อะไร google หาเพิ่มเองด้วยก็ดีครับ

หวังว่าจะเป็นประโยชน์นะครับ