📌

TS製フルスタックフレームワークfrourioの入門会を主催しました!

2021/06/07に公開

オンライン家庭教師マナリンク CTO の名人です。

今回は2021年6月6日に開催されたイベント”【frourio入門会】TypeScriptでフルスタックWeb開発!開発者によるライブ配信解説付き”についてのイベントレポートを書いていきます。
https://connpass.com/event/213606/

事前登録が140人超え、当日の参加者もおそらく70名以上の方にお越しいただき大盛況のうちに幕を閉じました。

今回は開発者のSolufaさんも会場である弊社オフィスにお越しいただき、技術的な点やOSSを作る苦労について喋ってもらいながらイベントを進めました。

frourioとは

frourioは国産のTypeScript製フルスタックWebフレームワークです。

frourioの特長

特長としては以下の点が挙げられます。

  • 型安全にREST API通信を記述できるライブラリaspidaを内包しており、フロントエンド〜バックエンド〜ORMまですべて型安全に書ける
  • 関数ベースでControllerを記述でき、フロントエンド寄りのエンジニアに対して馴染みの深い設計手法で実装できる
    • 加えて関数ベースでDependency Injectionを実現できるvelonaも内包。テスタブルに書きたい要望も叶える
  • frourio自身が提供しているのはあくまでバックエンドのController層の周辺のため非常に軽量。フロントエンドをReact/Vueから選択できるし、ORMもPrisma/TypeORMから選択するのも開発者の自由

便宜上フルスタックフレームワークと言っていますが、フルスタックフレームワークを実現するための肝となるフロントエンド↔バックエンドの型安全性に注力している軽量フレームワークと言っても良いかも知れません。

frourioを支えるライブラリ

同じ開発チームによるライブラリ


frourioが活用しているライブラリ

  • fastify
    • Node.jsサーバー最速。pluginが充実してきておりexpressの対抗馬として非常に有力。Encapsulationによりリバースプロキシのような役割も果たせるなど注目の機能が多い。弊社でも使っています
  • Prisma
    • Type SafeなORM。migrationの仕組みが付属しており、migrationファイルから型安全なDBクライアントが自動生成される。また、introspectコマンドで既存DBをmigrationファイルに起こすこともでき移植性に優れる

イベントの様子

全体の段取り

2時間ほどのスケジュールで実施しました。

スクリーンショット 2021-06-07 12 24 01

create-frourio-app

yarn create frourio-appを実行すると、ブラウザが立ち上がってGUI上でセットアップできます。普通はCLIでのセットアップになりますので、ここの工夫は大変面白いと思いました。

https://twitter.com/mamezou_yuki/status/1401439447644741634?s=20

先に述べたとおり、frourio自身は薄いフレームワークですがそれだけですとユースケースの想定がしずらいため、こういったツールを提供してNext.jsやPrismaなどをプリセットされた状態でローカルで動かせるようにしてくれています。

今回はNext.jsやPrisma、SQLiteを選びました。

  • 純粋なAPIサーバーのみ
  • MySQL/PostgresQLなどを選んだらDockerコンテナも用意してくれる

みたいな選択肢ができるともっと良いかも知れません。

順番にfrourioの解説

僕の画面を画面共有で映しながらコードリーディングを進めました。

開発したソースは一応以下のリポジトリに上げています。

https://github.com/TeXmeijin/frourio-workshop


https://github.com/TeXmeijin/frourio-workshop/blob/main/pages/index.tsx

  const createTask = useCallback(
    async (e: FormEvent) => {
      e.preventDefault()
      if (!label) return

      await apiClient.tasks.post({ body: { label } })
      setLabel('')
      revalidate()
    },
    [label]
  )

await apiClient.tasks.post({ body: { label } })がAPIを叩いているところですね。フロントエンドからはこんな感じで型安全にAPIを叩けます。

https://github.com/TeXmeijin/frourio-workshop/blob/main/server/api/tasks/index.ts

import type { Task } from '$prisma/client'

export type Methods = {
  get: {
    query?: {
      limit?: number
      message?: string
    }

    resBody: Task[]
  }
  post: {
    reqBody: Pick<Task, 'label'>
    resBody: Task
  }
}

APIの型定義はTypeScriptベースで書くことができます。postのリクエストボディにTask.label型が、戻り値にTask型が指定されていますね。

ここで定義した型定義がフロントでも使えるしバックエンドでも使えます。理屈としてはaspidaが生成した型定義を双方でImportしているだけです。

ハプニング:型がanyになる箇所が!?

こんな調子でfrourioの仕組みを追いつつ、作者のSolufaさんに質疑応答しながら最後にテストコードを書いていたとき。

/server/test/meijin.test.ts
import controller from '$/api/meijin/controller'
import fastify from 'fastify'

test('get meijin', async () => {
  const injectedController = controller.inject(() => ({
    getMeijin: () => 'meijin'
  }))(fastify())

  const res = await injectedController.get({})

  expect(res.status).toBe(200)

  expect(res.body.name).toBe('meijin')
})

最後のres.body.nameがany型になってしまう問題が発生しました。ここはStringであってほしいところです。
テストは通ったので、型の世界のみで不整合が起こっていることとなります。

Solufaさんも未知の問題だったため、「これはContributeチャンスですね!」ということでその場で型定義を追い始める時間になりました。

参加者の方も何名かソースを追ってZoomのチャットで仮説を展開し始める波乱の展開になりました。

▼Zoomのチャット(名前は変えています)

結論、APIの戻り値の型定義が、エラーレスポンスを考慮するためにUnion Typesになっており、それが原因でanyが勝ってしまっていたようです。

なのでここをどうするかは型定義の思想次第ですかね、といった感じでお開きとなりました。


イベント後、弊社の開発メンバーであり当イベントにもご参加いただいていたTechnoteさんがfrourioリポジトリへ調査内容や改善提案をIssueでまとめてくれました。
https://github.com/frouriojs/frourio/issues/170

Issueまで生まれてしまう意義のあるイベントになりました。開発者の方をイベントに呼ぶのは良いですね!

イベントの感想

よかったこと

  • Zoomのチャット機能を参加者の方が結構活用していただいて盛り上がった感じがした
  • 当初の集客予定を遥かに上回る方に視聴いただいた
  • ハッシュタグ#frourioも盛り上がった

https://twitter.com/kashiwakikanko/status/1401442548913111041?s=20

https://twitter.com/mamezou_yuki/status/1401449469850185728?s=20

https://twitter.com/myuske1/status/1401467253820821509?s=20

課題点

  • 実際にもくもくして成果物を上げていただいた方が見当たらなかった(2時間なので仕方ないのですがw)
  • けっこうぶっつけ本番で段取りを組んだので、トークショーとバグ究明するイベントみたいになった。期待値と違ったかも知れない

frourioの展望

最後に今後のfrourioの展望について個人的な見解を書いておきます。

日本語ドキュメント

日本語のドキュメントが充実していないため、日本から流行させていくには必要ですよね、といった話が参加者からあがりました。

導入側の視点に立ったブランディング

フルスタックフレームワークを導入したい場面は企業側から見るとさほど多くないです。そのため、APIフレームワークとして売り出してみたり、社内向け管理画面にどうですか、新規事業にどうですか、個人開発にどうですかみたいに導入側のユースケースに立ってブランディングするのが必要だったりするかも?
「ベンチャーCTOがfrourioで管理画面を2時間で作る様子を配信します」みたいなタイトルでイベントやったらキャッチーかなぁ。どうやろ

フロントとバックエンドを別々でデプロイしたり、別々のリポジトリで管理するときにaspidaの型定義だけプライベートnpmで切り出して相互から依存させる方法、とかをドキュメントで配布してくれると、既存の開発体制の企業にも導入しやすい?
一般論ですが、特にレッドオーシャンにおいては利用場面が想像できる形でブランディングするのが重要ですよね。

GraphQL/OpenAPIとの対決

純粋にTypeScriptの世界のみでAPIの型定義がフロントエンドとバックエンドで共有できる点は大変大きいです。既存のソリューションとしてGraphQLやOpenAPIがあると思うので、そことのDX比較イベントや記事を打っていくのも面白いかも?

NestJS/Blitzとの対決

コンセプトがまるで違うわけですが、TS製フレームワークとしてはNestJS/Blitzあたりがブランディング強いですね。

マナリンク Tech Blog

Discussion