「Drizzle ORM」はTypeScript向けの軽量・型安全なORM(Object-Relational Mapping)ツールです。Drizzle ORMを使用すると、SQLを直接書くことなくデータベースを操作できるので、開発者はデータベースとのやり取りをより直感的かつ安全に行うことができます。
本記事ではDrizzle ORMとNuxtを使用してMySQLと連携するWeb APIを作成し、JavaScript UIライブラリ「Wijmo(ウィジモ)」のデータグリッドコントロール「FlexGrid(フレックスグリッド)」と連携してデータの生成(Create)、読込(Read)、更新(Update)、削除(Delete)を行うCRUDアプリケーションを作成する方法をご紹介します。
目次
開発環境
- Nuxt 4.4.2
- Vue 3.5.30
- Drizzle ORM 0.45.2
- Wijmo 5.20252.44
Nuxtプロジェクトの作成
まずはターミナルなどで以下のコマンドを実行して、ベースとなるNuxtのアプリケーションを作成します。
npx nuxi@latest init wijmo-drizzle-nuxt-appいくつかインストールに関するオプションを選択していきます。テンプレートは「minimal」を選択します。

パッケージマネージャーは「npm」を選択します。

Nuxtの開発で使用する主なモジュールを選択して一緒にインストールすることができます。今回はCSSフレームワークとして「Tailwind CSS」を使用するのでので「Yes」を選択します。

「@nuxtjs/tailwindcss」を検索し、Spaceキー、またはTabキーを押下してモジュールを選択し、Enterキーを押下してインストールします。

プロジェクトが作成されたら、以下のコマンドでプロジェクトの配下に移動し、アプリケーションを実行します。
cd wijmo-drizzle-nuxt-app
npm run devブラウザで「http://localhost:3000/」にアクセスするとNuxtのアプリケーションが開きます。

バックエンド部分の作成
Drizzle ORMのインストールと初期化
次に以下のコマンドを実行して、Drizzle ORMとMySQL2、ならびに環境変数を使用するためのdotenvの初期化を行います。
npm install drizzle-orm mysql2 dotenv次にDrizzle ORMで使えるマイグレーションツールのDrizzle Kitをインストールします。
npm install -D drizzle-kitMySQLとの接続設定とスキーマの定義
プロジェクトのルート直下に「 .env」ファイルを作成し、MySQLへの接続設定を記載します。
※ user、password、db_nameはお使いの環境に合わせて修正してください。
DATABASE_URL="mysql://user:password@localhost:3306/db_name"次に「nuxt.config.ts」の中で先ほど定義したDB設定を保持します。
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: true },
modules: ['@nuxtjs/tailwindcss'],
runtimeConfig: {
databaseUrl: process.env.DATABASE_URL,
},
})続けて、プロジェクトのルート直下に「server/utils」フォルダを作成し、配下に「db.ts」ファイルを以下のように追加し、DB接続のユーティリティを作成します。
import mysql from 'mysql2/promise'
import { drizzle } from 'drizzle-orm/mysql2'
export function useDb() {
const config = useRuntimeConfig()
const pool = mysql.createPool({
uri: config.databaseUrl!, // runtimeConfigから取得(サーバーのみ)
connectionLimit: 10,
})
return drizzle({ client: pool })
}次に「server」フォルダ配下に「db」フォルダを作成し、配下に「schema.ts」ファイルを以下のように追加し、OrderDataテーブルのスキーマを定義します。
import { int, mysqlTable, serial, varchar, datetime } from 'drizzle-orm/mysql-core'
export const orderData = mysqlTable('order_data', {
id: serial('id').primaryKey(),
product: varchar('product', { length: 255 }).notNull(),
price: int('price').notNull(),
quantity: int('quantity').notNull(),
orderdate: datetime('orderdate').notNull(),
})
export type OrderRow = typeof orderData.$inferSelect
export type NewOrderRow = typeof orderData.$inferInsert次にプロジェクトのルート直下に「drizzle.config.ts」ファイルを作成し、DBの接続設定を行います。
import 'dotenv/config'
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
out: './drizzle',
schema: './server/db/schema.ts',
dialect: 'mysql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
})モデル定義を追記したら、以下のコマンドでMySQLにテーブルを作成します。
npx drizzle-kit push実行が完了すると、「order_data」テーブルがMySQLに作成されます。

APIルートの作成
次にAPIルートを作成していきます。ここではorderdataエンドポイント名にして、以下のように作成します。
- GET:/api/orderdata(一覧参照)
- POST:/api/orderdata(登録)
- GET:/api/orderdata/:id(1件参照)
- PUT:/api/orderdata/:id(更新)
- DELETE:/api/orderdata/:id(削除)
「server」フォルダ配下に「api/orderdata」フォルダを作成し、配下に「index.get.ts」を追加してGET(一覧参照)の処理を記載します。
import { orderData } from '../../db/schema'
import { useDb } from '../../utils/db'
export default defineEventHandler(async () => {
const db = useDb()
return await db.select().from(orderData)
})同様に「index.post.ts」を追加してPOST(登録)の処理を記載します。
import { orderData } from '../../db/schema'
import { useDb } from '../../utils/db'
type CreatePayload = {
product: string
price: number
quantity: number
orderdate: string
}
export default defineEventHandler(async (event) => {
const body = await readBody<CreatePayload>(event)
const db = useDb()
const inserted = await db
.insert(orderData)
.values({
product: body.product,
price: body.price,
quantity: body.quantity,
orderdate: new Date(body.orderdate),
})
return { ok: true, inserted }
})さらに「[id].get.ts」「[id].put.ts」「[id].delete.ts」をそれぞれ追加し、GET(1件参照)、PUT(更新)、DELETE(削除)の処理を定義します。
import { eq } from 'drizzle-orm'
import { orderData } from '../../db/schema'
import { useDb } from '../../utils/db'
export default defineEventHandler(async (event) => {
const id = Number(getRouterParam(event, 'id'))
const db = useDb()
const rows = await db.select().from(orderData).where(eq(orderData.id, id))
if (!rows[0]) {
throw createError({ statusCode: 404, statusMessage: 'Not Found' })
}
return rows[0]
})import { eq } from 'drizzle-orm'
import { orderData } from '../../db/schema'
import { useDb } from '../../utils/db'
type UpdatePayload = {
product: string
price: number
quantity: number
orderdate: string
}
export default defineEventHandler(async (event) => {
const id = Number(getRouterParam(event, 'id'))
const body = await readBody<UpdatePayload>(event)
const db = useDb()
await db
.update(orderData)
.set({
product: body.product,
price: body.price,
quantity: body.quantity,
orderdate: new Date(body.orderdate),
})
.where(eq(orderData.id, id))
return { ok: true }
})import { eq } from 'drizzle-orm'
import { orderData } from '../../db/schema'
import { useDb } from '../../utils/db'
export default defineEventHandler(async (event) => {
const id = Number(getRouterParam(event, 'id'))
const db = useDb()
await db.delete(orderData).where(eq(orderData.id, id))
return { ok: true }
})作成したら以下のコマンドを実行してAPIを起動します。
npm run devcurlなどで「http://localhost:3000/api/orderdata」にGETリクエストを送信すると、APIからデータが取得できます(この時点では0件)。

Drizzle Studioでデータの編集
Drizzle ORMはGUIでDBのデータ管理が可能な「Drizzle Studio」を提供しています。
以下のコマンドを実行することでDrizzle Studioを起動できます。
npx drizzle-kit studioブラウザから「https://local.drizzle.studio/」にアクセスすると以下のような管理画面でGUIによるデータ編集が可能です。

こちらから何件かテストデータを登録しておきます。
フロントエンド部分の作成
以上でバックエンドのWeb APIの準備が整ったので、WijmoのFlexGridでデータ表示を行うフロントエンド部分を作成していきます。
Wijmoのインストールと組み込み
まずはWijmoのVue用パッケージをアプリケーションにインストールします。
npm install @mescius/wijmo.vue2.allnpmパッケージをインストールしたら、Wijmoの組み込みを行なっていきます。
まずは「app」フォルダ配下に「components」フォルダを作成します。

「components」フォルダに「FlexGrid.vue」を追加し、以下のように記述します。
WijmoのVueモジュールやCSS、日本語カルチャファイルをインポートし、Fetch APIを使用してWeb APIからデータを取得します。ライセンスキーは「 .env」ファイルに保持します。
※ ライセンスキーを設定しない場合トライアル版を示すメッセージが表示されます。ライセンスキーの入手や設定方法についてはこちらをご覧ください。
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import '@mescius/wijmo.styles/wijmo.css'
import '@mescius/wijmo.cultures/wijmo.culture.ja'
import * as wjCore from '@mescius/wijmo'
import { WjFlexGrid, WjFlexGridColumn } from '@mescius/wijmo.vue2.grid'
import { WjFlexGridFilter } from '@mescius/wijmo.vue2.grid.filter'
type OrderItem = {
id: number
product: string
price: number
quantity: number
orderdate: Date
}
const config = useRuntimeConfig()
// Wijmoライセンスキーの設定
wjCore.setLicenseKey(config.public.wijmoLicenseKey || '');
const cv = ref<wjCore.CollectionView<OrderItem> | null>(null)
const loading = ref(true)
async function load() {
loading.value = true
const response = await fetch('/api/orderdata')
const text = await response.text()
const rows = JSON.parse(text, reviver) as OrderItem[]
cv.value = new wjCore.CollectionView(rows, { trackChanges: true, pageSize: 15 })
loading.value = false
}
onMounted(load)
async function saveChanges() {
if (!cv.value) return
const view = cv.value
const url = '/api/orderdata'
// orderdateをISO 文字列へ正規化する
const normalizeOrderdate = (v: any): string => {
if (v instanceof Date) return v.toISOString()
if (typeof v === 'string') {
const s = v.trim()
const normalized = s.replace(/\//g, '-')
const d = new Date(normalized)
if (!isNaN(d.getTime())) return d.toISOString()
}
const d = new Date(v)
if (!isNaN(d.getTime())) return d.toISOString()
throw new Error('受注日が未入力、または日付形式が不正です。')
}
// 追加
await Promise.all(
view.itemsAdded.map((x: any) =>
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...x, orderdate: normalizeOrderdate(x.orderdate) }),
})
)
)
// 更新
await Promise.all(
view.itemsEdited.map((x: any) =>
fetch(`${url}/${x.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...x, orderdate: normalizeOrderdate(x.orderdate) }),
})
)
)
// 削除
await Promise.all(
view.itemsRemoved.map((x: any) =>
fetch(`${url}/${x.id}`, { method: 'DELETE' })
)
)
await load()
alert('変更を保存しました')
}
// 日付文字列をDate型に変換するreviver関数
function reviver(key: string, val: any) {
if (
typeof val === "string" &&
/^\d{4}-\d{2}-\d{2}T.*/.test(val)
) {
return new Date(Date.parse(val));
}
return val;
}
</script>
<template>
<div class="space-y-3">
<div class="flex gap-2 items-center">
<button class="px-4 py-2 rounded bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="loading || !cv"
@click="saveChanges">
更新
</button>
<span v-if="loading">Loading...</span>
</div>
<wj-flex-grid
v-if="cv"
:itemsSource="cv"
:allowAddNew="true"
:allowDelete="true"
style="width: 785px"
>
<wj-flex-grid-filter />
<wj-flex-grid-column header="ID" binding="id" :isReadOnly="true" :width="80" />
<wj-flex-grid-column header="製品名" binding="product" :width="240" />
<wj-flex-grid-column header="単価" binding="price" :width="120" />
<wj-flex-grid-column header="数量" binding="quantity" :width="120" />
<wj-flex-grid-column header="受注日" binding="orderdate" :width="180" />
</wj-flex-grid>
</div>
</template>DATABASE_URL="mysql://user:password@localhost:3306/db_name"
NUXT_PUBLIC_WIJMO_LICENSE_KEY="ここにWijmoのライセンスキーを設定します"「nuxt.config.ts」のruntimeConfigに環境変数の設定を追加します。また、ssr: falseオプションを指定して、SSR(サーバーサイドレンダリング)しないように設定します。さらに、ページのタイトルや言語の設定なども追加します。
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: true },
modules: ['@nuxtjs/tailwindcss'],
ssr: false,
runtimeConfig: {
databaseUrl: process.env.DATABASE_URL,
public: {
wijmoLicenseKey: process.env.NUXT_PUBLIC_WIJMO_LICENSE_KEY,
},
},
app: {
head: {
title: 'Wijmo×Drizzle CRUDサンプル',
meta: [
{
name: 'description',
content: 'NuxtとDrizzleを使用したWijmoのCRUDアプリケーションサンプルです。',
},
],
htmlAttrs: {
lang: 'ja',
},
},
},
})仕上げとして「app/app.vue」ファイルの内容を以下のように書き換えて、作成したFlexGridコンポーネントを組み込みます。
<template>
<div style="margin-left: 10px">
<h1 class="text-3xl font-bold text-sky-600 my-3">Wijmo×Drizzle CRUDサンプル</h1>
<FlexGrid />
</div>
</template>データの読込(READ)
以上の手順で、Wijmoの組み込みは完了です。再び「npm run dev」コマンドを実行して「http://localhost:3000/」にブラウザでアクセスすると、FlexGrid上にAPIから取得したデータが表示されていることを確認できます。

今回のサンプルでは、FlexGridFilterコンポーネントも組み込んでいるため、Excelライクなソートやフィルタも利用可能です。
・・・(中略)・・・
import { WjFlexGridFilter } from '@mescius/wijmo.vue2.grid.filter'
・・・(中略)・・・
<wj-flex-grid
v-if="cv"
:itemsSource="cv"
:allowAddNew="true"
:allowDelete="true"
style="width: 785px"
>
<wj-flex-grid-filter />
<wj-flex-grid-column header="ID" binding="id" :isReadOnly="true" :width="80" />
<wj-flex-grid-column header="製品名" binding="product" :width="240" />
<wj-flex-grid-column header="単価" binding="price" :width="120" />
<wj-flex-grid-column header="数量" binding="quantity" :width="120" />
<wj-flex-grid-column header="受注日" binding="orderdate" :width="180" />
</wj-flex-grid>
</div>
</template>データの登録(CREATE)
FlexGridの一番下の行にデータを入力することで新規データの登録が可能です。データ入力後[更新]ボタンを押下するとAPIにPOSTのリクエストを送信できます。
データの更新(UPDATE)
FlexGrid上で任意のデータを更新し、[更新]ボタンを押下するとAPIにPUTのリクエストを送信できます。一度に複数件の更新も可能です。
データの削除(DELETE)
FlexGrid上で削除したい行を選択しDeleteキーを押下すると対象の行を削除できます。その後、[更新]ボタンを押下するとAPIにDELETEのリクエストを送信できます。
今回作成したアプリケーションは以下よりダウンロード可能です。
さいごに
今回はDrizzle ORMとNuxtを使用してを使用してMySQLと連携するWeb APIを作成し、Wijmoのデータグリッド「FlexGrid」でCRUD処理を行うWebアプリケーションを作成する方法をご紹介しました。
DrizzleはTypeScriptとの親和性が高く、SQLライクな記述でデータベース操作を行えるため、Nuxtのようなモダンなフレームワークと組み合わせることで、シンプルかつ保守性の高い実装が可能になります。また、WijmoのFlexGridを利用することで、一覧表示・編集・追加・削除といった業務アプリケーションで頻出するUIも効率よく実装できます。
製品サイトでは、Wijmoの機能を手軽に体験できるデモアプリケーションやトライアル版も公開しておりますので、こちらもご確認ください。
また、ご導入前の製品に関するご相談、ご導入後の各種サービスに関するご質問など、お気軽にお問合せください。
