React(Next.js)でDexieを使っているときのMissingAPIErrorの解決メモ
2023年2月16日 投稿
初めに
Next.js 13でDexieを用いてindexedDBへのアクセスを試みていたところ、MissingAPIError
に遭遇しました。
公式ドキュメントを参照したところ、原因はNext.js起動時にNode側でindexedDBにアクセスしようとして失敗しているためとのこと。公式ドキュメントには解決策として幾つかのライブラリを導入する方法が挙げられていますが、どれもモック用のライブラリっぽいくて、プロダクションでは採用したくありません。。。
そこで、他の解決策を探すことにしました。
環境
Next.js 13.1.6
dexie 3.2.3
dexie-react-hooks 1.1.1
chrome 108.0.5359.124
エラーが起きたコード
/** @format */
import { IMemoryMarker } from '@/types/indexedDB'
import { PromiseExtended } from 'dexie'
import { IndexedDB } from '../infrastructure/indexedDB'
export class LocalMemoryMarkerRepository {
private indexedDB
private static localMemoryMarkerRepository = new LocalMemoryMarkerRepository()
private constructor() {
this.indexedDB = IndexedDB.getInstance()
}
static getRepository() {
return this.localMemoryMarkerRepository
}
save({ lat, lng, created_at, updated_at }: IMemoryMarker): PromiseExtended<number> {
return await this.indexedDB.memory_marker.add({ lat, lng, created_at, updated_at })
}
findById(id: number): PromiseExtended<IMemoryMarker | undefined> {
return await this.indexedDB.memory_marker.get({ id: id })
}
findAll(): Promise<IMemoryMarker[]> {
return await this.indexedDB.memory_marker.toArray()
}
update({ id, lat, lng, updated_at }: IMemoryMarker): PromiseExtended<number> {
if (!id) throw new Error('idがundefiedです')
const updateParam = {
lat: lat,
lng: lng,
updated_at: updated_at,
}
return await this.indexedDB.memory_marker.update(id, updateParam)
}
delete(id: number) {
this.indexedDB.memory_marker.delete(id)
}
}
上記のコードは別クラスでシングルトンしてあるDexieインスタンス(変数名: indexedDB)を呼びだし、DexieインスタンスからDBの更新を行うリポジトリです。
コンパイル後のサーバー立ち上げ時にこちらのコードでエラーが発生していました。
解決策
- Dexieインスタンスを参照する際、
hasFailed()
メソッドを呼び出してindexedDBの存在を確かめる。
動作したコード
/** @format */
import { IMemoryMarker } from '@/types/indexedDB'
import { IndexedDB } from '../infrastructure/indexedDB'
export class LocalMemoryMarkerRepository {
private indexedDB
private static localMemoryMarkerRepository = new LocalMemoryMarkerRepository()
private constructor() {
this.indexedDB = IndexedDB.getInstance()
}
static getRepository() {
return this.localMemoryMarkerRepository
}
async save({ lat, lng, created_at, updated_at }: IMemoryMarker): Promise<number> {
if (this.indexedDB.hasFailed()) throw Error('データベースはOpenしていないようです。')
return await this.indexedDB.memory_marker.add({ lat, lng, created_at, updated_at })
}
async findById(id: number): Promise<IMemoryMarker | undefined> {
if (this.indexedDB.hasFailed()) return undefined
return await this.indexedDB.memory_marker.get({ id: id })
}
async findAll(): Promise<IMemoryMarker[]> {
if (this.indexedDB.hasFailed()) return []
return await this.indexedDB.memory_marker.toArray()
}
async update({ id, lat, lng, updated_at }: IMemoryMarker): Promise<number> {
if (!id) throw new Error('idがundefiedです')
if (this.indexedDB.hasFailed()) return 0
const updateParam = {
lat: lat,
lng: lng,
updated_at: updated_at,
}
return await this.indexedDB.memory_marker.update(id, updateParam)
}
async delete(id: number) {
if (this.indexedDB.hasFailed()) return
this.indexedDB.memory_marker.delete(id)
}
}
それぞれの関数の戻り値の型は、元々のPromiseExtended型の代わりにその親クラスであるPromiseに変更しました。そして、それぞれの関数で真っ先にhasFailed()
メソッドを呼び出すことで、indexedDBの存在を確かめています。