🏃‍♀️

GolangでREST(その3)

2023/04/26に公開

前回は

https://zenn.dev/robon/articles/a81cc526cac283

全体の設計方針からはじめて、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.

https://pkg.go.dev/database/sql#DB

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
}

最後に

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

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

まとめ

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で編集を提案
株式会社ROBONの技術ブログ

Discussion