© TOOLS BOX — Next.js / React / TypeScript コードサンプル集

サンプルガイド
←サンプル一覧
nextjsapi

Prisma でフィルタ条件を動的に組み立ててクエリを実行する

URL クエリパラメータから受け取った検索条件を Prisma の where 句に動的に組み立て、キーワード・カテゴリ・難易度などの複合フィルタクエリを実行するパターン。

難易度: 中級·更新: 2026-04-18·
prisma

対応バージョン

nextjs 15react 19prisma 5

前提環境

Prisma の基本的な CRUD 操作と Next.js Server Component のデータフェッチを理解していること

概要

URL クエリパラメータから受け取った検索条件(キーワード・カテゴリ・難易度)を Prisma の where 句に動的に組み立てる。undefined を渡すと Prisma がその条件を無視するプロパティを活用し、条件の有無に関係なく同一のクエリ関数で対応できるパターンを示す。

インストール

npm install prisma @prisma/client
npx prisma init

実装

Prisma スキーマ(参考)

// prisma/schema.prisma
model Sample {
  id         String   @id @default(cuid())
  slug       String   @unique
  title      String
  summary    String
  category   String
  difficulty String
  updatedAt  DateTime @updatedAt
}

where 句を動的に組み立てる関数

// lib/db/samples.ts
import { prisma } from "@/lib/prisma";

type FilterParams = {
  q?: string;
  category?: string;
  difficulty?: string;
};

export async function findSamples(filter: FilterParams) {
  return prisma.sample.findMany({
    where: {
      // q が undefined のときこの条件全体が無視される
      ...(filter.q
        ? {
            OR: [
              { title: { contains: filter.q, mode: "insensitive" } },
              { summary: { contains: filter.q, mode: "insensitive" } },
            ],
          }
        : {}),
      // undefined を渡すと Prisma はその条件をスキップする
      category: filter.category ?? undefined,
      difficulty: filter.difficulty ?? undefined,
    },
    orderBy: { updatedAt: "desc" },
  });
}

Server Component から呼び出す

// app/samples/page.tsx(抜粋)
import { findSamples } from "@/lib/db/samples";

type SearchParams = {
  q?: string;
  category?: string;
  difficulty?: string;
};

type Props = {
  searchParams: Promise<SearchParams>;
};

export default async function SamplesPage({ searchParams }: Props) {
  const params = await searchParams;

  const samples = await findSamples({
    q: params.q || undefined,
    category: params.category || undefined,
    difficulty: params.difficulty || undefined,
  });

  return (
    <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
      {samples.map((sample) => (
        <div key={sample.id}>{sample.title}</div>
      ))}
    </div>
  );
}

ページネーションと組み合わせる

// lib/db/samples.ts(ページネーション版)
const PAGE_SIZE = 24;

export async function findSamplesPaged(filter: FilterParams, page = 1) {
  const where = {
    ...(filter.q
      ? {
          OR: [
            { title: { contains: filter.q, mode: "insensitive" as const } },
            { summary: { contains: filter.q, mode: "insensitive" as const } },
          ],
        }
      : {}),
    category: filter.category ?? undefined,
    difficulty: filter.difficulty ?? undefined,
  };

  const [total, items] = await Promise.all([
    prisma.sample.count({ where }),
    prisma.sample.findMany({
      where,
      orderBy: { updatedAt: "desc" },
      skip: (page - 1) * PAGE_SIZE,
      take: PAGE_SIZE,
    }),
  ]);

  return {
    items,
    total,
    totalPages: Math.ceil(total / PAGE_SIZE),
  };
}

ポイント

  • Prisma では undefined を渡したプロパティはクエリ条件から除外される。null は IS NULL として解釈されるため、「条件なし」には必ず undefined を使う
  • contains + mode: "insensitive" で大文字小文字を無視した部分一致検索ができる。PostgreSQL では ILIKE にコンパイルされる
  • キーワード検索を OR で複数フィールドに広げる場合、スプレッド演算子 ...{} で条件ブロックを動的に追加する。条件なし時は空オブジェクトをスプレッドして何も追加しない
  • count と findMany を Promise.all で並行実行することで、ページネーションに必要な総件数と現ページのデータを 1 往復で取得できる
  • filter.q || undefined の変換は、空文字 ("") を undefined に変えるために必要。空文字をそのまま渡すと contains: "" となり全件マッチしてしまう
  • mode: "insensitive" は MySQL では非対応(常に case-insensitive)なため、DB エンジンによって挙動が変わる点に注意

注意点

nextjs-url-filter-reset は URL クエリのリセット操作。jest-url-search-params-test はクエリ変換ロジックのテスト。これは Prisma where 句への動的条件組み立てと undefined を利用した条件省略パターンに特化。

関連サンプル

同じテーマや技術スタックを使った実装例

  • Prisma で複数カラムをまたぐキーワード検索クエリを実装する

    タイトル・本文・タグなど複数カラムをまたぐキーワード検索を Prisma の OR 条件と fulltext 検索で実装するパターン。単語分割・スコアリングなしの実用的な部分一致パターンを示す。

  • Next.js Route Handler でクエリパラメータを使ってソートを実装する

    sort キーと order(asc/desc)をクエリパラメータで受け取り、Route Handler でデータをソートして返すパターンと、クライアント側のソートボタン UI を示す例。

  • Prisma と Route Handler で CRUD API を実装する

    Prisma Client の findMany / create / update / delete を Route Handler から呼び出して CRUD API を構築する例。スキーマ定義からマイグレーション・型安全なクエリまでを示す。

  • Next.js Route Handler でサーバーサイドキーワード検索を実装する

    クエリパラメータで受け取ったキーワードを Route Handler でサーバーサイドフィルタし、ページネーション付きで返す例。クライアントのデバウンス入力と合わせた構成も示す。

  • Prisma の orderBy で並び替えクエリを実装する

    Prisma の orderBy による単一フィールド・複数フィールド・リレーション先フィールドでの並び替えパターンと、null 値の扱い(nulls first / last)の例。

関連仕様

このサンプルを理解するのに役立つ仕様や概念

  • FrameworkNext.jsReact ベースのフルスタックフレームワーク。SSR・SSG・App Router・API Routes を提供する。
  • API / OptionwherePrisma クエリで絞り込み条件を指定するオプション。完全一致・部分一致・複数条件(AND / OR)など様々なフィルタを組み立てられる。
  • API / OptionorderByPrisma クエリの並び順を指定するオプション。単一フィールド・複数フィールド・リレーション先フィールドを指定できる。
  • API / OptionselectPrisma クエリで取得するフィールドを明示的に指定するオプション。不要なフィールドを除外してover-fetching を防ぐ。
←サンプル一覧に戻る