🐶

【Poteto】GolangでWebフレームワークはじめ③ Routingを良い感じにする

2024/11/27に公開

はじめに

https://github.com/poteto0/poteto
はじめまして、Potetoという名前のGolangのwebAPIフレームワークをせっせと作っているものです。大分開発も進んできて、もう使えるレベルになってきました!!今は自分が書いてたgolangのwebAPIプロジェクトをPotetoに移行しながら改良を進めています。

簡単なサンプルAPIも公開しているので、興味ある方はぜひ使ってみてください。
https://github.com/poteto0/poteto-sample-api/tree/main

この記事は第三回ということで今までのものは以下から

さて、今回のテーマは、「Routingを良い感じにする」です。

Routingを良い感じにする

良い感じのRouterとは何なのか?それを確かめるため、人々はアマゾンの奥地へと向かった。。。というのは置いといて、自分の感覚で良いルーターを考えました。

今までのPotetoのRouting

今までのPotetoのRoutingはよくあるフレームワークって感じです。

func main() {
  p := poteto.New()
  p.GET("/exmaple", exmapleHandler)
  p.Run(":8080")
}

これが悪いという話ではなく、インパクトは薄いんですよね。世の中メンテナンスの行き届いたWebフレームワークがあふれてる中で、個人開発を選択する理由はないので、インパクトが欲しいところです。

Potetoの課題:middlewareTree

middlewareTreeについてはぜひ前回の記事をご覧ください。
https://zenn.dev/poteto0/articles/5c4d99c9923aea

PotetoはmiddlewareTreeとRoutingがダイレクトに紐づかないという課題を抱えていました。

func main() {
  p := poteto.New()
  exmapleApi := p.Combine("/example", <your middleware>)
  p.GET("/exmaple", exmapleHandler)
  p.Run(":8080")
}

これは設計をシンプルにするためのものであり、個人的には気に入りつつも、やっぱり脳内で、どのミドルウェアが適用されるのか考えにくいという課題がありました。

RailsのRouting

そしてもうひとつ昔触って鮮明に覚えていた快適なRoutingがありました。それがRailsのroutingです。(記憶があやふやなので、全然違うことを言っているかもしれません。)

resources :photos do
  get "view"
  post "create"
end

https://railsguides.jp/routing.html

これめちゃくちゃ分かりやすいですよね。一目で/photos/view/photos/createにマッピングされているのが分かります。もはやこれ羅列よりわかりやすいんですよね。

get "/photos/view"
post "/photos/create"

良い感じのRoutingとは:僕の感覚

上記を振り返って「良い感じのRouting」を以下のように定義しました。

Namespaceで区切られており、middlewareのマッピングがはっきり分かる

それではそういった良い感じのRoutingを実際に作ってみました。

Poteto.Leaf: 良い感じのRouting

これが現在のPotetoのRoutingです(もちろん上記のやつも動きます)。
どうですか?良い感じじゃないですか?少なくとも自分は満足したのでOKです。

func main() {
  p := poteto.New()
  p.Leaf("/users", func(userApi poteto.Leaf) { 
    userApi.Register(<your middleware>)
    userApi.GET("/", controller.UserHandler)
    userApi.GET("/:name", controller.UserNameHandler)
  })
  p.Run("8080")
}

Poteto.Leaf()の実装

実装は単純で、Leafという構造体に*potetobasePathを持たせています。
Poteto.Leaf(basePath, leafHandler)ではまずLeaf構造体を作ります

type Leaf struct {
  poteto Poteto
  basePath string
}

func(p *poteto) Leaf(basePath string, handelr LeafHandler) {
  leaf := NewLeaf(basePath, handler)
  handler(leaf)
}

LeafではそれぞれPotetoの関数をbasePathをプラスした状態でインターナルに呼ぶだけです。比較すると分かりやすいですね。

/* 従来のRouting */
userApi := p.Combine("/users", <your middleware>)
p.GET("/users", controller.UserHandler)

/* Leaf */
p.Leaf("/users", func(userApi poteto.Leaf) {
  // Internal call p.Combine("/users", <your middleware>)
  userApi.Register(<your middleware>)
  // Internal call p.GET("/users", controller.UserHandler)
  userApi.GET("/", controller.UserHandler)
})

実装上の工夫としては以下の2点でした。

  • PotetoとLeafの相互参照を避ける
  • Leafの機能を最低限に絞り、参照が行われても理解しやすいようにする

結構満足いく設計にもなりました。

さいごに

ここまで読んでいただき、ありがとうございます。今回は個人的に満足感の高い機能を入れることができました。ぜひ、良い感じになったPotetoのRoutingをお試しください

Discussion