「Prisma」はNode.jsとTypeScriptで使えるORM(Object-Relational Mapping)ツールです。Prismaを使用すると、SQLを直接書くことなくデータベースを操作できるので、開発者はデータベースとのやり取りをより直感的かつ安全に行うことができます。
本記事ではPrismaとNext.jsを使用してMySQLと連携するWeb APIを作成し、JavaScript UIライブラリ「Wijmo(ウィジモ)」のデータグリッドコントロール「FlexGrid(フレックスグリッド)」と連携してデータの生成(Create)、読込(Read)、更新(Update)、削除(Delete)を行うCRUDアプリケーションを作成する方法をご紹介します。
目次
開発環境
- Next.js 15.4.6
- React 19.1.0
- Prisma 6.13.0
- Wijmo 5.20251.40
Next.jsプロジェクトの作成
以下のコマンドを実行して、ベースとなるNext.jsのアプリケーションを作成します。
npx create-next-app@latest wijmo-prisma-app
プロジェクトが作成されたら、以下のコマンドでプロジェクトの配下に移動します。
cd wijmo-prisma-app
バックエンド部分の作成
Prisma のインストールと初期化
次に以下のコマンドを実行して、PrismaとPrisma Clientをインストール、ならびにPrismaの初期化を行います。
npm install prisma tsx --save-dev
npm install @prisma/client
npx prisma init --datasource-provider mysql --output ../generated/prisma
初期化が完了すると「prisma/schema.prisma」ファイルと「 .env」ファイルが生成されます
MySQLとの接続設定とスキーマの定義
次に生成された「 .env」ファイルにMySQLへの接続設定を記載します。
※ user、password、db_nameはお使いの環境に合わせて修正してください。
DATABASE_URL="mysql://user:password@localhost:3306/db_name"
続けて、「prisma/schema.prisma」ファイルを編集し、「OrderData」モデルを定義します。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model OrderData {
id Int @id @default(autoincrement())
product String
price Int
quantity Int
orderdate DateTime
}
モデル定義を追記したら、以下のコマンドでマイグレーションを実行してMySQLにテーブルを作成します。
npx prisma migrate dev --name init
実行が完了すると、「OrderData」テーブルが MySQL に作成されます。

また、同時に「generated/prisma」フォルダ配下にPrisma Clientも作成されます。
APIルートの作成
次にAPIルートを作成していきます。「app」フォルダ配下に「api/orderdata/route.ts」を作成し、GETやPOSTのリクエストの処理を記載します。
import { PrismaClient } from '@prisma/client';
import { NextRequest, NextResponse } from 'next/server';
const prisma = new PrismaClient();
export async function GET(req: NextRequest) {
try {
const orderData = await prisma.orderData.findMany();
return NextResponse.json(orderData);
} catch (error: any) {
return new NextResponse(
JSON.stringify({ error: 'Failed to fetch order data', detail: error.message }),
{
status: 500,
headers: {
'Content-Type': 'application/json',
},
}
);
}
}
export async function POST(req: NextRequest) {
try {
const data = await req.json();
const newOrder = await prisma.orderData.create({
data: {
product: data.product,
quantity: data.quantity,
price: data.price,
orderdate: new Date(data.orderdate),
},
});
return NextResponse.json(newOrder);
} catch (error: any) {
return new NextResponse(
JSON.stringify({ error: 'Failed to create order', detail: error.message }),
{
status: 500,
headers: {
'Content-Type': 'application/json',
},
}
);
}
}
同様に「api/orderdata/[id]/route.ts」を作成し、PUTやDELETE、さらに特定のデータに対するGETリクエストの処理を記載します。
import { PrismaClient } from '@prisma/client';
import { NextRequest, NextResponse } from 'next/server';
const prisma = new PrismaClient();
// GET (特定のデータ)
export async function GET(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await params;
const order = await prisma.orderData.findUnique({
where: {
id: parseInt(id, 10),
},
});
if (!order) {
return new NextResponse(
JSON.stringify({ error: 'Order not found' }),
{
status: 404,
headers: { 'Content-Type': 'application/json' },
}
);
}
return NextResponse.json(order);
} catch (error: any) {
return new NextResponse(
JSON.stringify({ error: 'Failed to fetch order', detail: error.message }),
{
status: 500,
headers: { 'Content-Type': 'application/json' },
}
);
}
}
// PUT
export async function PUT(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await params;
const data = await req.json();
const updatedOrder = await prisma.orderData.update({
where: {
id: parseInt(id, 10),
},
data: {
product: data.product,
quantity: data.quantity,
price: data.price,
orderdate: new Date(data.orderdate),
},
});
return NextResponse.json(updatedOrder);
} catch (error: any) {
return new NextResponse(
JSON.stringify({ error: 'Failed to update order', detail: error.message }),
{
status: 500,
headers: { 'Content-Type': 'application/json' },
}
);
}
}
// DELETE
export async function DELETE(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await params;
const deletedOrder = await prisma.orderData.delete({
where: {
id: parseInt(id, 10),
},
});
return NextResponse.json(deletedOrder);
} catch (error: any) {
return new NextResponse(
JSON.stringify({ error: 'Failed to delete order', detail: error.message }),
{
status: 500,
headers: { 'Content-Type': 'application/json' },
}
);
}
}
作成したら以下のコマンドを実行してAPIを起動します。
npm run dev
curlなどで「http://localhost:3000/api/orderdata」にGETリクエストを送信すると、APIからデータが取得できます(この時点では0件)。

Prisma Studioでデータの編集
PrismaはGUIでDBのデータ管理が可能な「Prisma Studio」を提供しています。
APIを起動した状態で、以下のコマンドを実行することでPrisma Studioを起動できます。
npx prisma studio
ブラウザから「http://localhost:5555」にアクセスすると以下のような管理画面でGUIによるデータ編集が可能です。

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

「components」フォルダに「FlexGrid.tsx」を追加し、以下のように記述します。
WijmoのReactモジュールやCSS、日本語カルチャファイルをインポートし、WijmoのhttpRequestメソッドを使用してWeb APIからデータを取得します。
※ ライセンスキーを設定しない場合トライアル版を示すメッセージが表示されます。ライセンスキーの入手や設定方法についてはこちらをご覧ください。
'use client'
import * as React from "react";
import "@mescius/wijmo.styles/wijmo.css";
import * as WjCore from "@mescius/wijmo";
import * as WjGrid from "@mescius/wijmo.react.grid";
import { FlexGridFilter } from "@mescius/wijmo.react.grid.filter";
import "@mescius/wijmo.cultures/wijmo.culture.ja";
import OrderUpdateButton from "./OrderUpdateButton";
WjCore.setLicenseKey('ここにWijmoのライセンスキーを設定します');
type OrderItem = {
id: number;
product: string;
price: number;
quantity: number;
orderdate: Date;
};
const FlexGrid = () => {
const url = "http://localhost:3000/api/orderdata/";
const [order, setOrder] = React.useState<WjCore.CollectionView<OrderItem> | null>(null);
//GET
React.useEffect(() => {
WjCore.httpRequest(url, {
success: (xhr) => {
setOrder(
new WjCore.CollectionView(JSON.parse(xhr.response, reviver), {
trackChanges: true,
pageSize: 15,
})
);
},
error: (xhr) => {
alert("データの取得に失敗しました");
}
});
}, []);
const update = async () => {
if (!order) return;
// PUT
const putPromises = order.itemsEdited.map((item: any) =>
new Promise<void>((resolve, reject) => {
WjCore.httpRequest(url + item.id, {
method: "PUT",
data: item,
success: () => resolve(),
error: () => reject(new Error("PUT Failed")),
});
})
);
// POST
const postPromises = order.itemsAdded.map((item: any) =>
new Promise<void>((resolve, reject) => {
WjCore.httpRequest(url, {
method: "POST",
data: item,
success: () => resolve(),
error: () => reject(new Error("POST Failed")),
});
})
);
// DELETE
const deletePromises = order.itemsRemoved.map((item: any) =>
new Promise<void>((resolve, reject) => {
WjCore.httpRequest(url + item.id, {
method: "DELETE",
success: () => resolve(),
error: () => reject(new Error("DELETE Failed")),
});
})
);
try {
await Promise.all([...putPromises, ...postPromises, ...deletePromises]);
if (order.itemsEdited.length > 0)
alert(order.itemsEdited.length + "件のデータを更新しました。");
if (order.itemsAdded.length > 0)
alert(order.itemsAdded.length + "件のデータを登録しました。");
if (order.itemsRemoved.length > 0)
alert(order.itemsRemoved.length + "件のデータを削除しました。");
// データ再取得
WjCore.httpRequest(url, {
success: (xhr) => {
setOrder(
new WjCore.CollectionView(JSON.parse(xhr.response, reviver), {
trackChanges: true,
pageSize: 15,
})
);
},
error: () => {
alert("データの再取得に失敗しました");
}
});
} catch (e) {
alert("一部のリクエストでエラーが発生しました。");
}
};
return (
<div>
<div>
<OrderUpdateButton onClick={update} disabled={!order} />
</div>
<div>
{order ? (
<WjGrid.FlexGrid itemsSource={order} allowAddNew={true} allowDelete={true} style={{ width: '680px' }}>
<FlexGridFilter />
<WjGrid.FlexGridColumn header="ID" binding="id" width={80} />
<WjGrid.FlexGridColumn header="製品名" binding="product" width={200} />
<WjGrid.FlexGridColumn header="単価" binding="price" width={100} />
<WjGrid.FlexGridColumn header="数量" binding="quantity" width={100} />
<WjGrid.FlexGridColumn header="受注日" binding="orderdate" width={150} />
</WjGrid.FlexGrid>
) : (
<div>Loading...</div>
)}
</div>
</div>
);
}
// 日付文字列を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;
}
export default FlexGrid;
さらに「components」フォルダに「OrderUpdateButton.tsx」を追加し、更新処理実行用のボタンのコンポーネントを作成します。
import React from "react";
type Props = {
onClick: () => void;
disabled?: boolean;
};
const OrderUpdateButton = ({ onClick, disabled }: Props) => (
<button type="button" onClick={onClick} disabled={disabled}>
更新
</button>
);
export default OrderUpdateButton;
次に「app/page.tsx」ファイルの内容を以下のように書き換えます。「components/FlexGrid.tsx」ファイルを動的インポートし、その際{ ssr: false }
オプションを指定することで対象ファイルをSSR(サーバーサイドレンダリング)しないように設定します。
'use client';
import dynamic from 'next/dynamic'
const FlexGrid = dynamic(
() => {
return import("../components/FlexGrid");
},
{ ssr: false }
);
export default function Home() {
return (
<>
<h1 className="mt-2.5 mb-2.5 text-sky-500 text-4xl font-bold">
Wijmo×Prisma CRUDサンプル
</h1>
<FlexGrid></FlexGrid>
</>
)
}
また、「app/layout.tsx」のMetadata
でタイトルを修正し、加えてlang
属性も修正します。
・・・(中略)・・・
export const metadata: Metadata = {
title: "Wijmo×Prisma CRUDサンプル",
description: "PrismaとWijmoを使用したCRUDサンプルアプリケーション",
};
・・・(中略)・・・
<html lang="ja">
・・・(中略)・・・
最後に「app/globals.css」にスタイルを追加します。
@import "tailwindcss";
body{
margin-left: 10px
}
button {
@apply bg-blue-500 text-white font-bold py-2 px-4 mb-2 rounded;
}
データの読込(READ)
以上の手順で、Wijmoの組み込みは完了です。再び「npm run dev」コマンドを実行して「http://localhost:3000/」にブラウザでアクセスすると、FlexGrid上にAPIから取得したデータが表示されていることを確認できます。

今回のサンプルでは、FlexGridFilterコンポーネントも組み込んでいるため(FlexGrid.tsxファイル115行目)、Excelライクなソートやフィルタも利用可能です。
データの登録(CREATE)
FlexGridの一番下の行にデータを入力することで新規データの登録が可能です。データ入力後[更新]ボタンを押下するとAPIにPOSTのリクエストを送信できます。
データの更新(UPDATE)
FlexGrid上で任意のデータを更新し、[更新]ボタンを押下するとAPIにPUTのリクエストを送信できます。一度に複数件の更新も可能です。
データの削除(DELETE)
FlexGrid上で削除したい行を選択しDeleteキーを押下すると対象の行を削除できます。その後、[更新]ボタンを押下するとAPIにDELETEのリクエストを送信できます。
今回作成したアプリケーションは以下よりダウンロード可能です。
さいごに
今回はPrismaとNext.jsを使用してMySQLと連携するWeb APIを作成し、Wijmoのデータグリッド「FlexGrid」でCRUD処理を行うWebアプリケーションを作成する方法をご紹介しました。
製品サイトでは、Wijmoの機能を手軽に体験できるデモアプリケーションやトライアル版も公開しておりますので、こちらもご確認ください。
また、ご導入前の製品に関するご相談、ご導入後の各種サービスに関するご質問など、お気軽にお問合せください。