🏃♀️
GolangでREST(その3)
前回は
全体の設計方針からはじめて、RESTリソース毎に行う実装まで説明しました。
実装の続き
main
server/main.go
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.
server/db.go
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
}
最後に
今回のソースコードは、以下のリポジトリで公開しています。
おまけ
折角、複数プラットフォームで、同じ人が同じお題で実装したので、簡単な(非常に雑な)性能比較をしてみます。
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
まとめ
Method | Golang | FastAPI | NestJS |
---|---|---|---|
POST | 8.150 ms | 17.406 ms | 16.509 ms |
GET | 7.527 ms | 5.305 ms | 4.531 ms |
PUT | 8.352 ms | 17.634 ms | 7.968 ms |
DELETE | 8.233 ms | 12.275 ms | 4.641 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 | FastAPI | NestJS | |
---|---|---|---|
行数 | 1,434 | 430 | 589 |
文字数 | 30,510 | 12,017 | 16,248 |
Golang の場合は、ORM を使わず、DAO を実装したり、トランザクションやコンテキストも実装したために書いたコードが結果に反映されているようです。
逆に、go-chi しか使ってない割には、特化したフレームワークのアプリと大差なく作れたとも言えると思います。
GitHubで編集を提案
Discussion