１つの実行ファイルでバックエンドが作れるPocketBase

# PocketBase とは？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/b278f5a6-0f6d-a502-9feb-dddb0463c69c.png)

https://pocketbase.io/

オープンソースの BaaS (Backend as a Service) です．Supabase，Firebase，AWS Amplify などが BaaS として有名ですが，PocketBase はその中でもオープンソースであること，そして非常にシンプルであることが特徴です．

現在（2023/10/30）バージョンは`v0.19.1`で，まだ開発途中のようです．

PocketBase を使うと**たった１つの実行ファイル**だけで，例えば次のような機能が付いたバックエンドを構築できます．
- 管理UI
- 認証 (email/password，OAuth2）
- リアルタイムデータベース
- ファイルストレージ（AWS S3 対応）
- 自動バックアップ（AWS S3 対応）
- レコード（行）レベルのアクセスコントロール

この記事では PocketBase の魅力を伝えるために，機能の一部を勝手に選んで紹介します．

# 実際に動かしてみる

使い始めるのもとても簡単です．

1. [公式ページ](https://pocketbase.io/docs/)からZIPファイルをダウンロードする
1. 解凍してターミナルから開いて`./pocketbase serve`（Windowsでは`pocketbase serve`）を実行する

これだけで PocketBase が起動します．

起動するとREST API と Admin UI の URL が表示されるので Admin UI の方を開きます．
```cmd: Windowsの場合
C:\Users\xxx\pocketbase_0.19.0_windows_amd64>pocketbase serve
2023/10/16 20:26:29 Server started at http://127.0.0.1:8090
├─ REST API: http://127.0.0.1:8090/api/
└─ Admin UI: http://127.0.0.1:8090/_/
```

初回は Admin アカウントの作成を求められます．ローカルで動かしているので適当な Email と Password でOKです．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/29794367-85b9-606d-579d-f07652b8c4c0.png)

すると Admin UI が表示されます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/74f69df4-d46e-1261-54eb-d3112c85208a.png)

# テーブル（コレクション）作成

PocetBase は「テーブル」を 「コレクション」，「行」を 「レコード」，「列」を 「フィールド」 と呼びます．

下の画像のように，Admin UI からコレクションを作成することができます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/7ce97d00-f6ed-cbda-f01c-26a1bf354217.png)

`posts`コレクションを作成しました．`title`と`text`というフィールドを持ち，`categories`テーブルとリレーションを持っています．リレーション先のコレクション（ここでは`categories`）を作っていなくても，その場で作ることができて便利です．

ChatGPTに作ってもらったサンプルデータを入れました．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/720d01c3-164b-52b0-36d3-0c7c9c9430cb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/7a288c39-25f9-b1c0-27f8-8464bf106f40.png)

# APIからデータを取得する

まず「APIルール」を変更し，`posts`と`categories`の両方のコレクションのAPIに誰でもアクセスできるようにします．

各コレクションの設定内の「API rules」を見ると，すべてのAPIがデフォルトでは「Admin Only」になっています．各APIルールのテキストボックスをクリックすると「Admin Only」が解除されてフィルターを設定できるようになるので，何も入力せず空欄のままにしておきます．つまり誰でもアクセスできるようになります．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/3c115307-8c2f-536b-59ff-28ebca62c9a7.png)

APIにアクセスしてみます．
```cmd
> curl http://127.0.0.1:8090/api/collections/posts/records
```

```json
{
  "page": 1,
  "perPage": 30,
  "totalItems": 5,
  "totalPages": 1,
  "items": [
    {
      "categories": [
        "2qsgp9ylno27ny8"
      ],
      "collectionId": "7u9p9gdg6w6sy68",
      "collectionName": "posts",
      "created": "2023-10-30 11:55:44.921Z",
      "id": "yro54qadjxpajmp",
      "text": "今日のAIはどれだけ進化しているのか？",
      "title": "最新のAI技術について",
      "updated": "2023-10-30 11:55:44.921Z"
    },
    {
      "categories": [
        "7pzujww5zkwm73p"
      ],
      "collectionId": "7u9p9gdg6w6sy68",
      "collectionName": "posts",
      "created": "2023-10-30 11:56:09.257Z",
      "id": "et0agkpqwza11y5",
      "text": "旅の魅力を再発見しよう！",
      "title": "世界の絶景スポット10選",
      "updated": "2023-10-30 11:56:09.257Z"
    },
    ...（省略）
  ]
}
```

`posts`コレクションのデータを取得することができました．ここでも便利な機能がいくつかあって，たとえばクエリパラメータ`expand`にリレーションのフィールド名を指定すると，リレーション先のデータを持ってきてくれます．

```cmd
> curl http://127.0.0.1:8090/api/collections/posts/records?expand=categories
```

```json
{
  "page": 1,
  "perPage": 30,
  "totalItems": 5,
  "totalPages": 1,
  "items": [
    {
      "categories": [
        "2qsgp9ylno27ny8"
      ],
      "collectionId": "7u9p9gdg6w6sy68",
      "collectionName": "posts",
      "created": "2023-10-30 11:55:44.921Z",
      "expand": {
        "categories": [
          {
            "collectionId": "3y5kk0h15h1bkn0",
            "collectionName": "categories",
            "created": "2023-10-30 11:55:40.726Z",
            "id": "2qsgp9ylno27ny8",
            "title": "テクノロジー",
            "updated": "2023-10-30 11:55:40.726Z"
          }
        ]
      },
      "id": "yro54qadjxpajmp",
      "text": "今日のAIはどれだけ進化しているのか？",
      "title": "最新のAI技術について",
      "updated": "2023-10-30 11:55:44.921Z"
    },
    {
      "categories": [
        "7pzujww5zkwm73p"
      ],
      "collectionId": "7u9p9gdg6w6sy68",
      "collectionName": "posts",
      "created": "2023-10-30 11:56:09.257Z",
      "expand": {
        "categories": [
          {
            "collectionId": "3y5kk0h15h1bkn0",
            "collectionName": "categories",
            "created": "2023-10-30 11:56:05.537Z",
            "id": "7pzujww5zkwm73p",
            "title": "旅行",
            "updated": "2023-10-30 11:56:05.537Z"
          }
        ]
      },
      "id": "et0agkpqwza11y5",
      "text": "旅の魅力を再発見しよう！",
      "title": "世界の絶景スポット10選",
      "updated": "2023-10-30 11:56:09.257Z"
    },
    ...（省略）
  ]
}
```

詳しくは[ドキュメント](https://pocketbase.io/docs/api-records/)や「API Preview」を確認してください．

またフロントエンドから利用する場合は，公式の[JavaScript SDK](https://github.com/pocketbase/js-sdk)が存在します．

# APIルール

先ほど出てきたAPIルールを使うとクライアントに返すレコードをフィルタリングできます．よくあるのは例えば次のような使い方です：
- ログイン済みのユーザーに対してのみレコードを返す．`@request.auth.id != ""`
- クライアントのユーザーIDと，レコードの`author`フィールドが等しいレコードのみを返す．`@request.auth.id != author.id`
- 状態が`active`であるレコードのみを返す．`status = "active"`

[API rules and filters - Docs - PocketBase](https://pocketbase.io/docs/api-rules-and-filters/#filters-syntax)

# ファイルアップロード

他のフィールドと同じようにファイルを扱うことができます．ファイルをアップロードする場合はボディを`multipart/form-data`で送信します．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/640f6ef6-68c7-5ce1-2410-f1f7a3475ec6.png)

[Files upload and handling - Docs - PocketBase](https://pocketbase.io/docs/files-handling/)

ちなみに，アップロードされたファイルはデフォルトではローカルのディレクトリに保存されますが，AWS S3 に保存するように設定することもできます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/13433153-f554-31a8-b320-e3d92ba0e35f.png)

# バックアップ

GUI上でバックアップの作成・復元が可能です．さらに自動バックアップや作成したバックアップを AWS S3 に保存することも可能です．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320225/c26b0d1d-9c17-9de3-ed84-6929be0e03f1.png)


PocketBase は SQLite をデータベースとして採用しています．そして SQLite ファイルやアップロードされたファイルなどのデータはすべて `pb_data` ディレクトリに保存されます．バックアップ作成時には `pb_data` ディレクトリが圧縮されてバックアップファイルになります．

[Going to production - Docs - PocketBase](https://pocketbase.io/docs/going-to-production/#backup-and-restore)

# SQLite？

PocketBase は データベースとして SQLite を利用しています．SQLite で大丈夫なの？と思われるかもしれません．

[FAQ](https://pocketbase.io/faq/) にそのような疑問に対する回答があります．

> PocketBase is a great choice for small and midsize applications - SaaS, mobile api backend, intranet, etc.
Even without optimizations, PocketBase can easily serve 10 000+ persistent realtime connections on a cheap $4 Hetzner CAX11 VPS (2vCPU, 4GB RAM).
>
> PocketBaseは、SaaS、モバイルAPIバックエンド、イントラネットなどの小規模から中規模のアプリケーションに最適な選択肢です。最適化を施さなくても、PocketBaseは低価格なHetznerのCAX11 VPS（2vCPU、4GB RAM）を使用して、1万以上の持続的なリアルタイム接続を簡単に処理できます。

> For majority of the queries SQLite (in WAL mode) outperforms traditional databases like MySQL, MariaDB or PostgreSQL (especially for read operations).
>
> ほとんどのクエリにおいて、SQLite（WALモード時）は、MySQL、MariaDB、PostgreSQLといった従来のデータベースを性能で上回っています（特に読み込み操作において）。

要するに大規模な利用でなければ SQLite で十分だ，ということでしょう．安価な VPS でも十分使えるというのは嬉しいです．

# その他

実際に PocketBase を使って開発しようとすると様々な疑問が出てきます．これらの疑問はドキュメントを読めばだいたい解決すると思います．特に[Going to production](https://pocketbase.io/docs/going-to-production) と [FAQ](https://pocketbase.io/faq/) のページは参考になります．