1
/
5

GolangでREST(その3)

前回は

全体の設計方針からはじめて、RESTリソース毎に行う実装まで説明しました。


実装の続き

main


package main


import (
"fmt"
"log"
"net/http"
"os"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"

"github.com/take0a/go-rest-sample/module/customers"
"github.com/take0a/go-rest-sample/module/orders"
"github.com/take0a/go-rest-sample/module/products"
)

func main() {
dsn := os.Getenv("DSN")
db, err := NewDB(dsn)
if err != nil {
log.Printf("NewDB error %s", err)
}

r := chi.NewRouter()
r.Use(middleware.Logger)

r.Mount("/customers", customers.NewRouter(db))
r.Mount("/orders", orders.NewRouter(db))
r.Mount("/products", products.NewRouter(db))

port := os.Getenv("PORT")
addr := fmt.Sprintf(":%s", port)
err = http.ListenAndServe(addr, r)
if err != nil {
log.Printf("ListenAndServe error %s", err)
}
}

RESTリソースのパッケージをインポートして、リソース名のパスにそれぞれのRouterをMountします。データベースの接続文字列やWebサーバーのポート番号は環境変数で指定しています。

データベースの接続については、下記のとおりなので、最初に初期化して各Contorollerで共有します。

DB is a database handle representing a pool of zero or more underlying connections. It's safe for concurrent use by multiple goroutines.

sql
Package sql provides a generic interface around SQL (or SQL-like) databases.
https://pkg.go.dev/database/sql#DB


package main


import (
"database/sql"

_ "github.com/lib/pq"
)

// NewDB は、DBを生成します。
func NewDB(dsn string) (*sql.DB, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, err
}
return db, nil
}

最後に

今回のソースコードは、以下のリポジトリで公開しています。

GitHub - take0a/go-rest-sample: REST API Sample with Golang
REST API Sample with Golang. Contribute to take0a/go-rest-sample development by creating an account on GitHub.
https://github.com/take0a/go-rest-sample


おまけ

折角、複数プラットフォームで、同じ人が同じお題で実装したので、簡単な(非常に雑な)性能比較をしてみます。

Golang

2023/04/17 04:46:24 

SELECT
CUSTOMER_ID, NAME, ADDRESS
FROM CUSTOMER
WHERE CUSTOMER_ID = $1 Query sql: no rows in result set
2023/04/17 04:46:24 Invalid URL Parameter 1 sql: no rows in result set
2023/04/17 04:46:24 "GET http://localhost:3000/customers/1 HTTP/1.1" from 127.0.0.1:50922 - 404 50B in 28.368497ms
2023/04/17 04:46:38 "POST http://localhost:3000/customers HTTP/1.1" from 127.0.0.1:50926 - 200 71B in 8.150732ms
2023/04/17 04:46:41 "GET http://localhost:3000/customers/1 HTTP/1.1" from 127.0.0.1:50930 - 200 71B in 7.527611ms
2023/04/17 04:46:43 "PUT http://localhost:3000/customers/1 HTTP/1.1" from 127.0.0.1:50932 - 200 69B in 8.352679ms
2023/04/17 04:46:47 "DELETE http://localhost:3000/customers/1 HTTP/1.1" from 127.0.0.1:50934 - 200 0B in 8.23396ms

FastAPI

INFO:     Application startup complete.

INFO: process_time = 64.69178199768066 ms
INFO: 127.0.0.1:33852 - "GET /customers/1 HTTP/1.1" 404 Not Found
INFO: process_time = 17.406940460205078 ms
INFO: 127.0.0.1:33856 - "POST /customers HTTP/1.1" 200 OK
INFO: process_time = 5.305290222167969 ms
INFO: 127.0.0.1:33858 - "GET /customers/1 HTTP/1.1" 200 OK
INFO: process_time = 17.63439178466797 ms
INFO: 127.0.0.1:33860 - "PUT /customers/1 HTTP/1.1" 200 OK
INFO: process_time = 12.27569580078125 ms
INFO: 127.0.0.1:33862 - "DELETE /customers/1 HTTP/1.1" 200 OK

NestJS

[Nest] 22906  - 04/17/2023, 7:53:48 AM     LOG [NestApplication] Nest application successfully started +5ms

GET /customers/1
POST /customers
16.509488999843597 ms
GET /customers/1
4.531806997954845 ms
PUT /customers/1
7.968879997730255 ms
DELETE /customers/1
4.6412380039691925 ms

まとめ

早いですね。


おまけ2

コード量も比較しておきます。

Golang

$ find . -name '*.go' | xargs wc -l -m

37 751 ./server/main.go
16 223 ./server/db.go
50 1264 ./module/orders/dto.go
41 948 ./module/orders/entity.go
157 3282 ./module/orders/controller.go
87 2163 ./module/orders/service.go
20 318 ./module/orders/router.go
201 4695 ./module/orders/dao.go
156 3275 ./module/products/controller.go
17 318 ./module/products/entity.go
29 525 ./module/products/dto.go
118 2388 ./module/products/dao.go
52 1112 ./module/products/service.go
20 326 ./module/products/router.go
17 307 ./module/customers/entity.go
29 516 ./module/customers/dto.go
156 3282 ./module/customers/controller.go
52 1113 ./module/customers/service.go
118 2373 ./module/customers/dao.go
20 330 ./module/customers/router.go
35 896 ./utils/controller.go
6 105 ./utils/error.go
1434 30510 total

FastAPI

$ find src -name '*.py' | xargs  wc -l -m

24 682 src/main.py
38 1088 src/customers/router.py
0 0 src/customers/__init__.py
16 429 src/customers/models.py
10 154 src/customers/schemas.py
43 1272 src/customers/service.py
0 0 src/orders/__init__.py
38 1393 src/orders/models.py
38 1028 src/orders/router.py
23 402 src/orders/schemas.py
62 1909 src/orders/service.py
0 0 src/products/__init__.py
16 471 src/products/models.py
38 1058 src/products/router.py
10 157 src/products/schemas.py
43 1275 src/products/service.py
0 0 src/__init__.py
10 166 src/config.py
21 533 src/database.py
430 12017 total

NestJS

$ find src -name '*.ts' | grep -v spec | xargs wc -l -m

12 274 src/app.controller.ts
40 1361 src/app.module.ts
8 142 src/app.service.ts
10 323 src/main.ts
49 1267 src/customers/customers.controller.ts
12 431 src/customers/customers.module.ts
70 2045 src/customers/customers.service.ts
5 92 src/customers/dto/create-customer.dto.ts
4 185 src/customers/dto/update-customer.dto.ts
13 219 src/customers/entities/customer.entity.ts
47 1231 src/products/products.controller.ts
12 421 src/products/products.module.ts
70 2006 src/products/products.service.ts
5 95 src/products/dto/create-product.dto.ts
4 181 src/products/dto/update-product.dto.ts
13 247 src/products/entities/product.entity.ts
47 1177 src/orders/orders.controller.ts
12 401 src/orders/orders.module.ts
14 273 src/orders/dto/create-order.dto.ts
4 173 src/orders/dto/update-order.dto.ts
48 909 src/orders/entities/order.entity.ts
68 2133 src/orders/orders.service.ts
22 662 src/logging.interceptor.ts
589 16248 total

まとめ

Golang の場合は、ORM を使わず、DAO を実装したり、トランザクションやコンテキストも実装したために書いたコードが結果に反映されているようです。
逆に、go-chi しか使ってない割には、特化したフレームワークのアプリと大差なく作れたとも言えると思います



株式会社ROBON's job postings

Weekly ranking

Show other rankings
Invitation from 株式会社ROBON
If this story triggered your interest, have a chat with the team?