🗿
TypeORMを使ってブラウザでデータを永続化する
TypeORMをブラウザで利用した際に、永続化する方法がドキュメントや見つかった記事から読み取れなかったために半日潰してしまったのでメモを残しておく。
結論としては、TypeORMを(sql.jsを使って)ブラウザで利用する場合、ローカルストレージへ永続化するのがデフォルトの挙動だった。なので特別なことは不要で、シンプルにsql.jsの利用をセットアップすれば良い。
サンプルコード
- 以下のようなUserを用意して
src/entity/user.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column('text')
name: string
}
- 以下のようにDataSourceを初期化すれば
src/database.ts
import initSqlJs from 'sql.js'
import { DataSource } from 'typeorm'
import { User } from './entity/user'
export const setupSQLite = async () => {
;(window as any).SQL = await initSqlJs({
locateFile: (file: string) => `https://sql.js.org/dist/${file}`,
})
const AppDataSource = new DataSource({
type: 'sqljs',
autoSave: true,
location: 'test',
entities: [User],
synchronize: true, // for development
logging: true, // for development
})
const connection = await AppDataSource.initialize()
const userRepository = connection.getRepository(User)
return { userRepository }
}
- 以下の形で永続化できる
src/main.ts
import { setupSQLite } from './database'
import { User } from './entity/user'
const sample = async () => {
const { userRepository } = await setupSQLite()
const count = await userRepository.count()
console.log(`count: ${count}`)
const user = new User()
user.name = 'piyo'
await userRepository.save(user)
console.log(user)
}
sample()
実行結果
ブラウザで実行して、コンソールログに以下のような出力があれば成功。
実行の度にcountやidの値が増えていくことが確認できる。
またDeveloperConsoleで見ると、ローカルストレージにバイナリが保存されていることがわかる。
ローカルストレージを使いたくない場合
ただローカルストレージは以下のような点もあり、利用したくないケースもあるかもしれない
- 容量上限が数MB(ブラウザによる)
- 処理が同期的
その場合は、localForageというローカルストレージ互換のAPIでIndexedDBやWebSQLへ永続化ができるライブラリを使うと問題が回避できそう。TypeORMでlocalForageを利用するオプションが用意されている。
window.localforage
を用意してから、useLocalForage: true
をDataSource初期化時のオプションに渡すと保存先がIndexedDBに変わった。
src/database.ts
+ ;(window as any).localforage = (await import('localforage')).default
const AppDataSource = new DataSource({
type: 'sqljs',
autoSave: true,
location: 'test',
entities: [User],
synchronize: true, // for development
logging: true, // for development
+ useLocalForage: true,
})
今回苦戦したポイント
-
TypeORMの公式サイトへ行くとExample how to use TypeORM with TypeScript and SystemJS in Browserというリンクがあるが、最終更新が2018年と古め
- 非推奨となっているAPIも使われておりそのままは利用しづらかった
- 「typeorm browser」などでGoogle検索すると、TypeORMをブラウザで利用するにはsql.jsを利用する情報が見つかる
- sql.jsは公式に「It uses a virtual database file stored in memory, and thus doesn't persist the changes made to the database.」と書いてあるように、メモリ上にSQLiteのデータを保持するだけで永続化はしない。
- なので当然、sql.jsを利用してTypeORMをブラウザで使った場合も永続化されないと思いこんでしまった。
- しかし実際は、本記事にあるように特別なセットアップ無しでローカルストレージへ永続化される挙動になっていた
- ソースコードを見るとTypeORMのSqljsDriverの中で永続化処理が行われていた
- Adding TypeORM with sql.js to a Vite & Capacitor application | by Raymond Boswel | Mediumの記事を参考に、localForageを利用した永続化を試している途中で気づいた
参考
- viteでブラウザ上でtypeormを使ってみたメモ - Qiita
- Adding TypeORM with sql.js to a Vite & Capacitor application | by Raymond Boswel | Medium
- localForage/localForage: 💾 Offline storage, improved. Wraps IndexedDB, WebSQL, or localStorage using a simple but powerful API.
- sql-js/sql.js: A javascript library to run SQLite on the web.
- TypeORM - Amazing ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Electron platforms.
- typeorm/browser-example: Example how to use TypeORM in the browser with WebSQL.
Discussion