PocketBaseでバックエンドを爆速構築 - 2026年版完全ガイド


PocketBaseは、たった1つの実行ファイルで動く超軽量バックエンドです。Firebase/Supabaseのようなリアルタイムデータベース、認証、ストレージを、Go言語のシングルバイナリとして提供します。2026年現在、個人開発からスタートアップまで幅広く採用されています。

PocketBaseとは?

PocketBaseはオールインワンのバックエンドツールです。

主な特徴

  • 1ファイルで完結: バイナリ1つダウンロードして実行するだけ
  • SQLiteベース: 軽量・高速・ポータブル
  • リアルタイムDB: WebSocket経由でデータ変更を即座に通知
  • 認証機能: Email/Password、OAuth2(Google、GitHub等)
  • ファイルストレージ: 画像・動画などのアップロード・配信
  • Admin UI: ブラウザベースの管理画面が標準搭載
  • REST & Realtime API: SDKも充実
  • 拡張可能: Go言語でカスタムロジックを追加可能
  • 無料: ライセンス料金ゼロ(MITライセンス)

Firebase/Supabaseとの比較

機能PocketBaseFirebaseSupabase
セルフホスト×
料金無料従量課金無料枠あり
データベースSQLiteFirestorePostgreSQL
リアルタイム
認証
ストレージ
サーバー不要××
学習曲線

小〜中規模アプリで自前サーバーを持てるならPocketBase最強

インストール

方法1: バイナリダウンロード(最速)

# macOS/Linux
curl -L https://github.com/pocketbase/pocketbase/releases/download/v0.22.0/pocketbase_0.22.0_darwin_amd64.zip -o pocketbase.zip
unzip pocketbase.zip
chmod +x pocketbase

# 起動
./pocketbase serve

# ブラウザで http://127.0.0.1:8090 を開く

方法2: Dockerで起動

# docker-compose.yml
version: '3.8'
services:
  pocketbase:
    image: ghcr.io/muchobien/pocketbase:latest
    ports:
      - "8090:8090"
    volumes:
      - ./pb_data:/pb_data
    restart: unless-stopped
docker-compose up -d

方法3: Goから使う(拡張開発用)

go mod init myapp
go get github.com/pocketbase/pocketbase
// main.go
package main

import (
    "log"
    "github.com/pocketbase/pocketbase"
)

func main() {
    app := pocketbase.New()

    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}

初期セットアップ

1. 管理者アカウント作成

http://127.0.0.1:8090/_/ にアクセスして管理者を作成:

2. コレクション作成(データベーステーブル)

Admin UI > Collections > New Collection

例: Blogコレクション

{
  "name": "posts",
  "type": "base",
  "schema": [
    {
      "name": "title",
      "type": "text",
      "required": true
    },
    {
      "name": "content",
      "type": "editor",
      "required": true
    },
    {
      "name": "author",
      "type": "relation",
      "options": {
        "collectionId": "users",
        "cascadeDelete": false
      }
    },
    {
      "name": "tags",
      "type": "select",
      "options": {
        "maxSelect": 5,
        "values": ["tech", "design", "business"]
      }
    },
    {
      "name": "coverImage",
      "type": "file",
      "options": {
        "maxSelect": 1,
        "maxSize": 5242880
      }
    },
    {
      "name": "published",
      "type": "bool",
      "required": true
    }
  ]
}

3. APIルール設定

各コレクションには5つのルールを設定できます:

  • List/Search: 一覧取得
  • View: 詳細取得
  • Create: 作成
  • Update: 更新
  • Delete: 削除

例: 公開投稿は誰でも閲覧可、作成は認証済みユーザーのみ

// List/Search rule
published = true

// View rule
published = true

// Create rule
@request.auth.id != ""

// Update rule
@request.auth.id = author.id

// Delete rule
@request.auth.id = author.id

JavaScript SDKでデータ操作

セットアップ

npm install pocketbase
// lib/pocketbase.ts
import PocketBase from 'pocketbase'

export const pb = new PocketBase('http://127.0.0.1:8090')

// TypeScript型定義
export interface Post {
  id: string
  title: string
  content: string
  author: string
  tags: string[]
  coverImage: string
  published: boolean
  created: string
  updated: string
}

CRUD操作

Create(作成)

import { pb } from './lib/pocketbase'

async function createPost() {
  const data = {
    title: 'PocketBase入門',
    content: '<p>PocketBaseは最高です...</p>',
    author: pb.authStore.model?.id,  // ログイン中のユーザー
    tags: ['tech', 'backend'],
    published: true,
  }

  const record = await pb.collection('posts').create<Post>(data)
  console.log('Created:', record)
}

Read(読み取り)

// 一覧取得
async function getPosts() {
  const records = await pb.collection('posts').getList<Post>(1, 50, {
    filter: 'published = true',
    sort: '-created',
    expand: 'author',  // リレーション展開
  })

  console.log(records.items)
}

// 詳細取得
async function getPost(id: string) {
  const record = await pb.collection('posts').getOne<Post>(id, {
    expand: 'author',
  })

  console.log(record)
}

// 全件取得(ページング自動処理)
async function getAllPosts() {
  const records = await pb.collection('posts').getFullList<Post>({
    sort: '-created',
  })

  console.log(records)
}

Update(更新)

async function updatePost(id: string) {
  const record = await pb.collection('posts').update<Post>(id, {
    title: '更新されたタイトル',
  })

  console.log('Updated:', record)
}

Delete(削除)

async function deletePost(id: string) {
  await pb.collection('posts').delete(id)
  console.log('Deleted')
}

フィルタリング・検索

// タグで絹り込み
const techPosts = await pb.collection('posts').getList<Post>(1, 20, {
  filter: 'tags ~ "tech" && published = true',
})

// テキスト検索
const searchResults = await pb.collection('posts').getList<Post>(1, 20, {
  filter: 'title ~ "React" || content ~ "React"',
})

// 日付範囲
const recentPosts = await pb.collection('posts').getList<Post>(1, 20, {
  filter: 'created >= "2026-01-01"',
  sort: '-created',
})

// 複雑なフィルタ
const complexFilter = await pb.collection('posts').getList<Post>(1, 20, {
  filter: '(tags ~ "tech" || tags ~ "design") && author.id = "xyz123"',
})

認証(Authentication)

Email/Password認証

ユーザー登録

async function signup(email: string, password: string, name: string) {
  const data = {
    email,
    password,
    passwordConfirm: password,
    name,
  }

  const user = await pb.collection('users').create(data)

  // 認証メール送信(設定で有効化必要)
  await pb.collection('users').requestVerification(email)

  return user
}

ログイン

async function login(email: string, password: string) {
  const authData = await pb.collection('users').authWithPassword(email, password)

  console.log('Token:', pb.authStore.token)
  console.log('User:', pb.authStore.model)

  return authData
}

ログアウト

function logout() {
  pb.authStore.clear()
}

認証状態の保持

// authStoreは自動的にlocalStorageに保存される
console.log('Is logged in:', pb.authStore.isValid)
console.log('Current user:', pb.authStore.model)

// 認証状態変更を監視
pb.authStore.onChange((token, model) => {
  console.log('Auth changed:', token, model)
})

OAuth2認証(Google、GitHub等)

async function loginWithGoogle() {
  // OAuth2フロー開始
  const authData = await pb.collection('users').authWithOAuth2({ provider: 'google' })

  console.log('OAuth User:', authData.record)
  console.log('OAuth Meta:', authData.meta)
}

// サポートプロバイダー
// - google
// - github
// - gitlab
// - discord
// - microsoft
// - apple

ファイルアップロード

画像アップロード

async function uploadImage(file: File) {
  const formData = new FormData()
  formData.append('title', 'My Photo')
  formData.append('image', file)

  const record = await pb.collection('posts').create(formData)

  // 画像URL取得
  const imageUrl = pb.files.getUrl(record, record.coverImage)
  console.log('Image URL:', imageUrl)

  // サムネイル(自動生成)
  const thumbUrl = pb.files.getUrl(record, record.coverImage, { thumb: '100x100' })
  console.log('Thumbnail:', thumbUrl)
}

React Hook Form連携

import { useForm } from 'react-hook-form'
import { pb } from './lib/pocketbase'

function PostForm() {
  const { register, handleSubmit } = useForm()

  const onSubmit = async (data: any) => {
    const formData = new FormData()
    formData.append('title', data.title)
    formData.append('content', data.content)
    formData.append('coverImage', data.coverImage[0])

    await pb.collection('posts').create(formData)
    alert('投稿完了!')
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('title')} placeholder="タイトル" />
      <textarea {...register('content')} placeholder="本文" />
      <input type="file" {...register('coverImage')} accept="image/*" />
      <button type="submit">投稿</button>
    </form>
  )
}

リアルタイムサブスクリプション

// リアルタイムでpostsコレクションの変更を監視
pb.collection('posts').subscribe<Post>('*', (e) => {
  console.log('Event:', e.action)  // 'create' | 'update' | 'delete'
  console.log('Record:', e.record)
})

// 特定のレコードのみ監視
pb.collection('posts').subscribe<Post>('RECORD_ID', (e) => {
  console.log('Post updated:', e.record)
})

// 購読解除
await pb.collection('posts').unsubscribe()

Reactでリアルタイム更新

import { useEffect, useState } from 'react'
import { pb, Post } from './lib/pocketbase'

function PostList() {
  const [posts, setPosts] = useState<Post[]>([])

  useEffect(() => {
    // 初期データ取得
    pb.collection('posts').getList<Post>(1, 50).then((res) => {
      setPosts(res.items)
    })

    // リアルタイム監視
    pb.collection('posts').subscribe<Post>('*', (e) => {
      if (e.action === 'create') {
        setPosts((prev) => [e.record, ...prev])
      } else if (e.action === 'update') {
        setPosts((prev) => prev.map((p) => (p.id === e.record.id ? e.record : p)))
      } else if (e.action === 'delete') {
        setPosts((prev) => prev.filter((p) => p.id !== e.record.id))
      }
    })

    return () => {
      pb.collection('posts').unsubscribe()
    }
  }, [])

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

カスタムロジック(Go拡張)

package main

import (
    "log"
    "net/http"

    "github.com/labstack/echo/v5"
    "github.com/pocketbase/pocketbase"
    "github.com/pocketbase/pocketbase/core"
)

func main() {
    app := pocketbase.New()

    // カスタムエンドポイント
    app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
        e.Router.GET("/api/hello", func(c echo.Context) error {
            return c.JSON(http.StatusOK, map[string]string{
                "message": "Hello from custom API!",
            })
        })

        return nil
    })

    // レコード作成前のフック
    app.OnRecordBeforeCreateRequest("posts").Add(func(e *core.RecordCreateEvent) error {
        // 自動でslug生成
        e.Record.Set("slug", generateSlug(e.Record.GetString("title")))
        return nil
    })

    // レコード作成後のフック(通知送信など)
    app.OnRecordAfterCreateRequest("posts").Add(func(e *core.RecordCreateEvent) error {
        // メール通知、Slack通知など
        sendNotification(e.Record)
        return nil
    })

    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}

本番デプロイ

Fly.ioでデプロイ

# Dockerfile
FROM alpine:latest

RUN apk add --no-cache ca-certificates

COPY pocketbase /usr/local/bin/pocketbase

EXPOSE 8090

CMD ["/usr/local/bin/pocketbase", "serve", "--http=0.0.0.0:8090"]
# fly.toml
app = "my-pocketbase"
primary_region = "nrt"

[build]
  dockerfile = "Dockerfile"

[env]
  PORT = "8090"

[http_service]
  internal_port = 8090
  force_https = true

[[mounts]]
  source = "pb_data"
  destination = "/pb_data"
fly launch
fly volumes create pb_data --size 1
fly deploy

Railway/Render/VPSでデプロイ

# systemdサービス(Linux VPS)
# /etc/systemd/system/pocketbase.service

[Unit]
Description=PocketBase
After=network.target

[Service]
Type=simple
User=pocketbase
WorkingDirectory=/opt/pocketbase
ExecStart=/opt/pocketbase/pocketbase serve --http=0.0.0.0:8090
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl enable pocketbase
sudo systemctl start pocketbase

バックアップ

# 自動バックアップスクリプト
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
tar -czf backups/pb_data_$DATE.tar.gz pb_data/

# 古いバックアップを削除(30日以上前)
find backups/ -name "pb_data_*.tar.gz" -mtime +30 -delete
# cronで毎日実行
0 2 * * * /opt/pocketbase/backup.sh

まとめ

PocketBaseは以下の点で革新的です:

シンプルさ:

  • 1ファイルで完結
  • セットアップ数分
  • 学習曲線が低い

機能性:

  • リアルタイムDB
  • 認証・認可
  • ファイルストレージ
  • Admin UI標準搭載

柔軟性:

  • SQLite(ポータブル)
  • Go言語で拡張可能
  • REST & Realtime API

コスト:

  • ライセンス料金ゼロ
  • VPS月$5で運用可能
  • スケールしてもコスト増えにくい

PocketBaseが向いているケース:

  • 個人プロジェクト・スタートアップ
  • リアルタイムが必要なアプリ
  • 自前サーバーを持てる環境
  • 低コストで始めたい

Firebase/Supabaseの代替として、また小規模バックエンドの最適解として、PocketBaseは2026年の必修ツールです。

参考リンク: