viteでブラウザ上でtypeormを使ってみたメモ

## 概要
[前回](https://qiita.com/hibohiboo/items/8e0ed0adc92665b7d7d4)、ブラウザのメモリ上でsqliteを動かすsql.jsを試した。
型をつけてO/Rマッパーを導入してみる。
一応動くことは確認できたが、無理やり感は否めない。
[ソースコード](https://github.com/hibohiboo/tomoshibi/tree/id/192)

## typescript設定


```diff:tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
+    "experimentalDecorators": true, // アノテーションが使えるようにする
    // "emitDecoratorMetadata": true, // esbuildでは emitDecoratorMetadata は未対応
    "sourceMap": true
  },
}

```

typeormはアノテーションを利用するため、typescriptがそれを許可するようにする。
`emitDecoratorMetadata`はtypeormではtrueにするよう指示しているが、
`vite`が利用している`esbuild`で[非対応](https://esbuild.github.io/content-types/)なので設定しない。
[swcをesbuildの代わりに利用](https://stackoverflow.com/questions/68570519/why-cant-reflect-metadata-be-used-in-vite)する方法もあるようだが、設定ファイルを`esbuild`用に作っていたため断念。
一度試したが、buildが通らなくなったので使用を諦めた[*](https://github.com/hibohiboo/tomoshibi/commit/83675064b56664676a5f98265948735947993d53)。


## モデル作成

```domain/todo/models/User.ts
/* 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準備

```domain/sqlite/initSQL.ts
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
}
```

```ts:domain/todo/index.ts
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上で動かすものなので、いろいろハマった。
また、ファイルサイズが大きいため、クライアントには不向きかもしれない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99876/bc1cac47-1d8e-78a2-ac83-89db8a5805a5.png)

## 参考
[typeorm + absurd-sql on Browser のロマン構成](https://zenn.dev/mizchi/articles/typeorm-sqljs-absurd-js-example)

[github - typeorm - sqljs-data-source-options](https://github.com/typeorm/typeorm/blob/master/docs/data-source-options.md#sqljs-data-source-options)
[typeorm/typescript-example](https://github.com/typeorm/typescript-example)

[Node.js × TypeScriptのORM、TypeORMをMySQLで試す](https://kazuhira-r.hatenablog.com/entry/2022/03/13/235304)