前回の続き。Turso(libSQL)をDrizzle ORMで操作するときに、
例外やエラーの扱い方を調べてみたときの備忘録(*´ω`*)
調べたときのバージョンはこんな感じ
- drizzle-orm: 0.38.3
- @libsql/client: 0.14.0
- @libsql/core: 0.14.0
- libsql: 0.4.7
まとめ
最終的にはこんな感じ。libsqlErrorで判断できるっぽい
try { // DB操作 } catch (error: any) { // DBのエラーかの判定 if (error.libsqlError) { // SQLiteのエラーコードの判定 if (error.code == "SQLITE_CONSTRAINT_UNIQUE") { // 一意制約違反のときのエラー } } // それ以外のエラー throw error; }
細かいテーブル名やカラム名はmessageに埋め込まれてしまっていて、
個別では取得できないっぽい...
error.codeの値は、このあたりを参照するとよい
例外が投げられる流れ
基本的にDrizzleの例外はなく、
各クライアントの例外がそのまま投げれるっぽい
Tursoを利用する場合、libsql-clientを使うので、
libsql-clientが投げる例外のLibsqlErrorが実体っぽい
// https://github.com/tursodatabase/libsql-client-ts/blob/v0.14.0/packages/libsql-core/src/api.ts /** Error thrown by the client. */ export class LibsqlError extends Error { /** Machine-readable error code. */ code: string; /** Raw numeric error code */ rawCode?: number; constructor( message: string, code: string, rawCode?: number, cause?: Error, ) { if (code !== undefined) { message = `${code}: ${message}`; } super(message, { cause }); this.code = code; this.rawCode = rawCode; this.name = "LibsqlError"; } }
いくつかのライブラリが登場するが、流れ的には以下の感じ
- drizzle-orm
- Drizzle ORMのクライアント。libsql-clientを呼び出す
- libsql-client: libsql-client-tsのリポジトリ
- TypeScriptのクライアント。libsql-coreを呼び出す
- libsql-core: libsql-client-tsのリポジトリ
- LibsqlErrorを定義。libsqlを呼び出し、
LibsqlErrorに詰め替えている
- LibsqlErrorを定義。libsqlを呼び出し、
- libsql: libsql-jsのリポジトリ
- Rust製の部分?SQLを実行などをするっぽい
- 例外発生時、
SqliteErrorを投げる
libsqlのSqliteErrorの定義はこんな感じ
// https://github.com/tursodatabase/libsql-js/blob/v0.4.7/types/sqlite-error.d.ts export = SqliteError; declare function SqliteError(message: any, code: any, rawCode: any): SqliteError; declare class SqliteError { constructor(message: any, code: any, rawCode: any); code: string; rawCode: any; name: string; } //# sourceMappingURL=sqlite-error.d.ts.map
これをlibsql-client側で詰め替えているよう
// https://github.com/tursodatabase/libsql-client-ts/blob/v0.14.0/packages/libsql-client/src/sqlite3.ts import Database from "libsql"; // ... function mapSqliteError(e: unknown): unknown { if (e instanceof Database.SqliteError) { return new LibsqlError(e.message, e.code, e.rawCode, e); } return e; }
この流れから見ると、SqliteErrorの時点で、
テーブル名やカラム名を保持していないので、
messageの中身をパースしないと細かいハンドリングはできないっぽい。。
instanceOf LibsqlErrorでは判定できない
LibsqlErrorやDrizzleErrorは提供されているっぽいので、
こちらでも試してみたが、制約違反では判定できなかった。。。
import { LibsqlError } from "@libsql/client"; import { DrizzleError } from "drizzle-orm"; if (error instanceof DrizzleError) { // ここは通らない } if (error instanceof LibsqlError) { // ここも通らない } if (error.libsqlError) { // ここは通る if (error.code == "SQLITE_CONSTRAINT_UNIQUE") { // ... } } throw error;
該当のソースコードは見つけられなかったが、
中身を見てみるとこんな感じだったので、libsqlErrorで判定する感じにしている
console.error(`error.keys=${Object.keys(error)}`); // => error.keys=rawCode,code,libsqlError console.error(`${JSON.stringify({ code: error["code"], rawCode: error["rawCode"], libsqlError: error["libsqlError"], }, null, 2)}`); // => // { // "code": "SQLITE_CONSTRAINT_UNIQUE", // "rawCode": 2067, // "libsqlError": true // }
以上!! 情報があまりないけど、とりあえず、これでなんとかなりそう(*´ω`*)