はじめに
こんなこと、思ったことありませんか?
「バックエンドのSQLとか、フロントメインの自分には正直きつい…」
「設定値をJSONで管理してたら増えすぎてカオスになってきた」
「sqlite3モジュール触ってみたけど、コード量多すぎてつらい」
「ORMは大げさ、でも生SQLも書きたくない」
そんな方に朗報です。SQLを一切書かずに、いつものPython dictの感覚でSQLiteを扱える軽量ライブラリ「NanaSQLite」があります。
しかも MITライセンスで無料・改変自由。ドキュメントは 日本語・英語どちらも完備されており、日本語ネイティブの開発者も開発に携わっているので安心して使えます。
NanaSQLiteって何者?DictSQLiteとの関係
NanaSQLiteは、同じDisnanaが開発した先代ライブラリ DictSQLite の設計上の失敗・課題を徹底的に洗い直して作られた、進化版dict風SQLiteラッパーです。
DictSQLiteで見えてきた問題点(キャッシュ設計・非同期対応・スレッド安全性など)をすべて解消して、ゼロから再設計されています。先代の失敗が糧になっています。
主な特徴はこちら
- ✅ dict感覚で操作できる(
db["key"] = value) - ✅ 即時永続化(書いたそばからSQLiteに保存)
- ✅ スマートキャッシュ(遅延ロード・一括ロード切替可)
- ✅ async/await完全対応
- ✅ スレッドセーフ
- ✅ Pydantic対応
- ✅ 暗号化機能搭載
- ✅ WALモード・mmapによる高速化
- ✅ MITライセンス
インストール
pip install nanasqlite
内部では高速SQLiteバインディング apsw を使用しています。pip install時に自動で一緒に入るので、別途インストールは不要です。
apswは標準のsqlite3モジュールより低レベルかつ高速なSQLiteバインディングです。NanaSQLiteの速度を支える重要な依存ライブラリです。
JSONファイルからの移行がめちゃくちゃ簡単
今まで(JSONファイル管理)
import json, os
CONFIG_FILE = "config.json"
# 読み込み
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, "r") as f:
config = json.load(f)
# 書き込み
config["theme"] = "dark"
with open(CONFIG_FILE, "w") as f:
json.dump(config, f)
NanaSQLiteなら
from nanasqlite import NanaSQLite
db = NanaSQLite("config.db")
db["theme"] = "dark" # 読み書き、これだけ
ファイルの開き直し・json.dump / json.load・os.path.existsチェックがすべて不要になります。しかもJSONと違い、複数プロセスから同時アクセスしてもスレッドセーフです。
基本的な使い方
書く・読む・消す
from nanasqlite import NanaSQLite
with NanaSQLite("myapp.db") as db:
# 書き込み
db["username"] = "Nana"
db["settings"] = {"theme": "dark", "lang": "ja"}
# 読み込み
print(db["username"]) # 'Nana'
print(db["settings"]["theme"]) # 'dark'
# 存在確認
if "username" in db:
print("存在する!")
# 削除
del db["username"]
# 全件イテレート
for key, value in db.items():
print(key, value)
# 件数
print(len(db))
再起動してもデータが消えない
# 1回目の実行
with NanaSQLite("myapp.db") as db:
db["visits"] = 100
# 2回目の実行(再起動後・別プロセスでもOK)
with NanaSQLite("myapp.db") as db:
print(db["visits"]) # 100 ← ちゃんと残ってる!
JSONファイルと違い、ファイルの読み書きをコードで管理する必要がありません。書いた瞬間にSQLiteに保存されます。
ネストしたデータも30階層以上対応
db["user"] = {
"name": "Nana",
"age": 20,
"address": {
"prefecture": "Aichi",
"city": "Okazaki"
},
"tags": ["admin", "active"]
}
print(db["user"]["address"]["city"]) # 'Okazaki'
print(db["user"]["tags"]) # 'admin'
Pydanticモデルもそのまま保存できる
TypeScriptのZodみたいな感覚で、型安全にデータを管理できます。
from pydantic import BaseModel
from nanasqlite import NanaSQLite
class User(BaseModel):
name: str
age: int
email: str
with NanaSQLite("app.db") as db:
# Pydanticモデルをそのまま保存
db.set_model("user_1", User(name="Nana", age=20, email="nana@example.com"))
# 取り出すときも型付きで返ってくる
user = db.get_model("user_1", User)
print(user.name) # 'Nana'
print(type(user)) # <class '__main__.User'>
dictでの保存と違い、取り出したデータが自動でPydanticモデルに復元されます。バリデーションもそのまま機能します。
バッチ処理で爆速書き込み
# 10,000件のデータを一括書き込み
data = {f"key_{i}": {"value": i, "tag": "batch"} for i in range(10000)}
db.batch_update(data)
# 一括読み込み
results = db.batch_get([f"key_{i}" for i in range(100)])
バッチ処理は1件ずつの書き込みと比べて約19.9倍高速です。スクレイピング結果の蓄積や、大量設定値の一括更新には必ずbatch_updateを使いましょう。
暗号化もできる
SQLiteなのに、DBファイル自体を暗号化できます。設定値やトークンを保存するアプリでも安心です。
db = NanaSQLite("secret.db", encryption_key="your-secret-key")
db["api_token"] = "sk-xxxxxxxxxxxx"
暗号化キーを忘れるとデータを復元できません。環境変数など安全な方法でキー管理をしましょう。
SQLに詳しくなってきたら:22種類のラッパー関数
「もっと本格的に使いたい!テーブルを分けたい!」という方向けに、SQLを書かなくていいラッパーメソッドも充実しています。
# テーブル作成
db.create_table("users", {
"id": "INTEGER PRIMARY KEY",
"name": "TEXT NOT NULL",
"email": "TEXT UNIQUE"
})
# 挿入・更新・UPSERT
db.sql_insert("users", {"name": "Alice", "age": 25})
db.sql_update("users", {"age": 26}, "name = ?", ("Alice",))
db.upsert("users", {"id": 1, "name": "Alice", "age": 26})
# 条件付き取得・ページネーション
results = db.query(table_name="users", where="age > ?", parameters=(20,))
page2 = db.query_with_pagination("users", limit=10, offset=10)
# カラム追加・スキーマ確認・最適化
db.alter_table_add_column("users", "phone", "TEXT")
schema = db.get_table_schema("users")
db.vacuum()
複数テーブルを1ファイルで管理
with NanaSQLite("app.db") as main_db:
users_db = main_db.table("users")
settings_db = main_db.table("settings")
logs_db = main_db.table("logs")
users_db["user_1"] = {"name": "Taro", "role": "admin"}
settings_db["theme"] = "dark"
logs_db["2026-03-05-login"] = {"user": "user_1", "ip": "192.168.1.1"}
全テーブルが同一SQLite接続を共有するのでメモリ効率が高く、スレッド間の競合もロックで安全に制御されます。
async対応:Discord botやFastAPIにも即使える
import asyncio
from nanasqlite import AsyncNanaSQLite
async def main():
async with AsyncNanaSQLite("bot.db", max_workers=10) as db:
# 書き込み
await db.aset("guild_123", {"prefix": "!", "lang": "ja"})
# 読み込み
config = await db.aget("guild_123")
print(config) # {'prefix': '!', 'lang': 'ja'}
# 並行読み込み(高速)
results = await asyncio.gather(
db.aget("guild_123"),
db.aget("guild_456"),
db.aget("guild_789")
)
asyncio.run(main())
Discord.py / FastAPI / aiohttp など、asyncioベースのアプリならどれにでもそのまま組み込めます。awaitを付けるだけで通常版とほぼ同じ感覚で使えます。
セキュリティも本格対応(v1.2.0〜)
# SQLインジェクション・ReDoS対策を有効化
db = NanaSQLite("mydata.db",
strict_sql_validation=True, # 未許可のSQL関数を禁止
max_clause_length=500 # SQLの長さを制限
)
# 非同期用:読み取り専用接続プールで並列クエリを高速化
async with AsyncNanaSQLite("mydata.db", read_pool_size=5) as db:
results = await asyncio.gather(
db.query("logs", where="level=?", parameters=("ERROR",)),
db.query("logs", where="level=?", parameters=("INFO",)),
)
こんな用途に使えます
| ユースケース | 従来の方法 | NanaSQLiteなら |
|---|---|---|
| 設定ファイルの管理 |
config.jsonの読み書き |
db["config"] = {...} |
| Discord botの永続データ | pickle / 生SQLite | await db.aset("guild_id", data) |
| スクレイピング結果の蓄積 | CSVやpandas | db.batch_update(results) |
| 型安全なデータ管理 | Pydantic + JSONファイル | db.set_model("key", model) |
| 機密情報の保存 | 平文JSONファイル | NanaSQLite("db", encryption_key=...) |
| Webアプリの軽量DB | PostgreSQL / MySQL | FastAPI + AsyncNanaSQLite
|
ライセンス・ドキュメント
- 📄 ライセンス:MIT License(商用利用・改変・再配布すべて自由)
- 🌐 ドキュメント:英語・日本語の両方で完備
- 👨💻 日本語ネイティブの開発者が開発に携わっているので、日本語issueも安心して出せます
おわりに
「SQLを学ばなくていい」ということではありません。裏側ではちゃんとSQLiteが動いています。ただ、書かなくていいというのがNanaSQLiteの提供する価値です。
JSONで管理している設定が増えてきた方、フロントメインでバックエンドのSQLに苦手意識がある方、Discordボットのデータをpickleで無理やり管理している方、ぜひ一度触ってみてください。
気に入ったら GitHubのスター⭐ を押してもらえると泣いて喜びます!
pip install nanasqlite