SQLite互換サーバーレスDB Turso & FastAPI
Tursoとは?
公式ページによると,Tursoはスピードとスケーラビリティを兼ね備えたSQLite互換のデータベースです.
あまり使ったことはないですが,Cloudflare D1に似てますね.
料金
Tursoの大きな特徴はコストの低さです.無料プランには以下の機能が含まれています.
- 9 GBのストレージ基本容量、追加GBごとに$0.75
- 最大500データベース、最大3箇所のロケーション
- 月1億行の読み取り無料、超過分は10億行ごとに$1
- 月2500万行の書き込み無料、超過分は100万行ごとに$1
- 無制限の組み込みレプリカ
- オーバーユース対応
- 24時間以内のPITR
- データベースブランチング
無料枠でも機能の縛りが少なく魅力的です.より高機能な有料プランも月$8.25から始められます.この値段でクラウドDBが使えるのはありがたいです.
Turso CLI
Tursoにはデータベースを管理するためのダッシュボードがありますが,ダッシュボード上でできる操作はあまり多くありません.主にCLIを使って操作していくことを想定しているようです.
Quick Startに沿って進めていくと,CLIのインストールと基本的な操作を学ぶことができます.
Turso & SQLModel & FastAPI
本題はこちらです.
TursoはSQLAlchemyに対応しています(sqlalchemy-libsql).さらにFastAPIと相性の良いSQLModelはSQLAlchemyがベースになっています.つまり Turso & SQLModel & FastAPI の組み合わせが実現できそう!と思い立って実際に試してみました.
データベース準備
Turso CLIでデータベースを作成します.
turso db create my-db
Created database my-db at group default in 2.113s.
Start an interactive SQL shell with:
turso db shell my-db
To see information about the database, including a connection URL, run:
turso db show my-db
To get an authentication token for the database, run:
turso db tokens create my-db
データベースのURLと,データベースにアクセスするためのトークンを取得します.
まずはデータベースのURLを取得します.
turso db show --url my-db
libsql://...
次にトークンを取得します.
turso db tokens create my-db
eyJhbGciOiJFZERT...
FastAPIから接続する
fastapi
,sqlmodel
,sqlalchemy
,sqlalchemy-libsql
をインストールします.
python -m venv .venv
source .vnev/bin/activate
pip install fastapi sqlmodel sqlalchemy sqlalchemy-libsql
検証用のコードです.<DB_URL>
と<TOKEN>
を先ほど取得したものに置き換えてください.
from fastapi import FastAPI
from sqlmodel import Field, Session, SQLModel, create_engine, select
TURSO_DATABASE_URL = "<DB_URL>"
TURSO_AUTH_TOKEN = "<TOKEN>"
sqlite_url = f"sqlite+{TURSO_DATABASE_URL}/?authToken={TURSO_AUTH_TOKEN}&secure=true"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, echo=True, connect_args=connect_args)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
app = FastAPI()
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
class HeroCreate(SQLModel):
name: str
secret_name: str
age: int | None = None
@app.on_event("startup")
def on_startup():
create_db_and_tables()
@app.post("/heroes/")
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
@app.get("/heroes/")
def read_heroes():
with Session(engine) as session:
heroes = session.exec(select(Hero)).all()
return heroes
FastAPIを実行します.
fastapi dev main.py
動作確認
/heroes
に対してPOSTリクエストを送信してデータを登録します.
curl -X POST "http://127.0.0.1:8000/heroes/" \
-H "Content-Type: application/json" \
-d '{"name": "Peter Parker", "secret_name": "Spider-Man", "age": 18}'
{"id":1,"secret_name":"Spider-Man","name":"Peter Parker","age":18}
ちゃんとIDが振られてるのが確認できます.
続けてGETリクエストでデータが保存されているか確認します.
curl -X GET "http://127.0.0.1:8000/heroes/"
[{"id":1,"secret_name":"Spider-Man","name":"Peter Parker","age":18}]
ちゃんとデータが保存されているのがわかります.
Tursoのダッシュボードからデータベースの中身を確認すると,確かにデータが登録されています.
ローカルサーバーも立てられる
ちなみに開発環境用でローカルのTursoデータベース(正確にはlibsqlサーバー)を立てられます.この場合トークンは不要です.
turso dev
sqld listening on port 8080.
Use the following URL to configure your libSQL client SDK for local development:
http://127.0.0.1:8080
No auth token is required when sqld is running locally.
This server is using an ephemeral database. Changes will be lost when this server stops.
If you want to persist changes, use --db-file to specify a SQLite database file instead.
turso db shell
でローカルデータベースのアドレスを指定することもできます.
turso db shell http://127.0.0.1:8080
おわり
今回紹介しませんでしたが,レプリカ作ったりPITRしたりダンプしたりも簡単にできます.最高ですね.
Discussion