概要
前回、ブラウザのメモリ上でsqliteを動かすsql.jsを試した。
型をつけてO/Rマッパーを導入してみる。
一応動くことは確認できたが、無理やり感は否めない。
ソースコード
typescript設定
{
"compilerOptions": {
"target": "ESNext",
+ "experimentalDecorators": true, // アノテーションが使えるようにする
// "emitDecoratorMetadata": true, // esbuildでは emitDecoratorMetadata は未対応
"sourceMap": true
},
}
typeormはアノテーションを利用するため、typescriptがそれを許可するようにする。
emitDecoratorMetadata
はtypeormではtrueにするよう指示しているが、
vite
が利用しているesbuild
で非対応なので設定しない。
swcをesbuildの代わりに利用する方法もあるようだが、設定ファイルをesbuild
用に作っていたため断念。
一度試したが、buildが通らなくなったので使用を諦めた*。
モデル作成
/* eslint-disable unused-imports/no-unused-vars */
import {
Entity,
Column,
CreateDateColumn,
UpdateDateColumn,
BaseEntity,
PrimaryColumn,
} from 'typeorm/browser'
@Entity()
export class User extends BaseEntity {
@PrimaryColumn({ type: 'text' })
public id!: string
@Column({ type: 'text' })
public name!: string
@CreateDateColumn()
public readonly createdAt!: Date
@UpdateDateColumn()
public readonly updatedAt!: Date
}
emitDecoratorMetadata
がfalse
のため、reflect-metadata
が使えない。
公式ドキュメントの通り、@Column() public name!: string
と設定すると、以下のエラーが発生する。
esbuildを通すと、型情報が消えるので、reflect-metadata
で値を取得することができない。
Column.ts:192 Uncaught ColumnTypeUndefinedError: Column type for User#name is not defined and cannot be guessed. Make sure you have turned on an "emitDecoratorMetadata": true option in tsconfig.json. Also make sure you have imported "reflect-metadata" on top of the main entry file in your application (before any entity imported).If you are using JavaScript instead of TypeScript you must explicitly provide a column type.
at Column.ts:192:23
at __decorateClass (User.ts:7:24)
at User.ts:18:10
DB準備
import initSqlJs, { SqlJsStatic } from 'sql.js'
export const initSQL = async (wasmFileDirPath: string) => {
const SQL: SqlJsStatic = await initSqlJs({
locateFile: (file) => `${wasmFileDirPath}/${file}`,
})
// typeorm用にwindow.SQLに保存
Object.assign(globalThis as any, { SQL })
return SQL
}
import { DataSource } from 'typeorm/browser'
import { WASM_FILE_PATH } from '../sqlite/constants'
import { initSQL } from '../sqlite/initSQL'
import { User } from './models/User'
import type { PromiseReturnType } from '@/types'
const initRepository = async () => {
await initSQL(WASM_FILE_PATH)
const conn = new DataSource({
type: 'sqljs', // this connection search window.SQL on browser
entities: [User],
synchronize: true,
logging: ['query', 'schema'],
})
await conn.initialize()
const userRepository = conn.getRepository(User)
return { userRepository }
}
let repositories: PromiseReturnType<typeof initRepository> | undefined
export const getRepositories = async () => {
if (repositories) return repositories
repositories = await initRepository()
return repositories
}
結論
typeormがもともとNode.js上で動かすものなので、いろいろハマった。
また、ファイルサイズが大きいため、クライアントには不向きかもしれない。
参考
typeorm + absurd-sql on Browser のロマン構成
github - typeorm - sqljs-data-source-options
typeorm/typescript-example