レビュー済み·難易度: 初級·更新: 2026-04-18

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

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

nextjscrudapiprisma

対応バージョン

nextjs 15react 19prisma 5

前提環境

Prisma の基本 CRUD と findMany の使い方を理解していること

概要

Prisma の orderBy はオブジェクト 1 つ(単一フィールド)、配列(複数フィールド・優先順位あり)、ネストオブジェクト(リレーション先フィールド)の 3 形式をサポートする。nulls: "first" | "last" で null 値の並び順も制御できる。

インストール

npm install prisma @prisma/client
npx prisma init

実装

Prisma スキーマ

// prisma/schema.prisma
model Post {
  id          Int       @id @default(autoincrement())
  title       String
  viewCount   Int       @default(0)
  publishedAt DateTime?
  createdAt   DateTime  @default(now())
  author      User      @relation(fields: [authorId], references: [id])
  authorId    Int
}

model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  createdAt DateTime @default(now())
  posts     Post[]
}

単一フィールドで並び替え

// lib/queries/posts.ts
import { prisma } from "@/lib/prisma";

// createdAt の新しい順
export async function getLatestPosts() {
  return prisma.post.findMany({
    orderBy: { createdAt: "desc" },
  });
}

// viewCount の多い順
export async function getPopularPosts() {
  return prisma.post.findMany({
    orderBy: { viewCount: "desc" },
  });
}

// title のアルファベット順
export async function getPostsAlphabetical() {
  return prisma.post.findMany({
    orderBy: { title: "asc" },
  });
}

複数フィールドで並び替え(配列形式)

// 複数フィールドを指定する場合は配列で渡す(先頭のキーが優先)
export async function getPostsByViewThenDate() {
  return prisma.post.findMany({
    orderBy: [
      { viewCount: "desc" }, // 1番目: viewCount の多い順
      { createdAt: "desc" }, // 2番目: 同じ viewCount なら新しい順
    ],
  });
}

リレーション先フィールドで並び替え

// 著者名のアルファベット順でソート(リレーション先)
export async function getPostsByAuthorName() {
  return prisma.post.findMany({
    orderBy: {
      author: { name: "asc" },
    },
    include: { author: { select: { name: true } } },
  });
}

// 著者の投稿数順でソート(集計フィールド)
export async function getPostsByAuthorPostCount() {
  return prisma.post.findMany({
    orderBy: {
      author: {
        posts: { _count: "desc" },
      },
    },
    include: { author: { select: { name: true } } },
  });
}

null 値の並び順を制御する

// publishedAt が null のものを最後に置く(デフォルトは DB によって異なる)
export async function getPostsByPublishedAt() {
  return prisma.post.findMany({
    orderBy: {
      publishedAt: { sort: "desc", nulls: "last" },
    },
  });
}

// publishedAt が null のものを先頭に置く
export async function getDraftPostsFirst() {
  return prisma.post.findMany({
    orderBy: {
      publishedAt: { sort: "asc", nulls: "first" },
    },
  });
}

Route Handler でソートパラメータを受け取る

// app/api/posts/route.ts
import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import type { Prisma } from "@prisma/client";

type AllowedSortField = "createdAt" | "viewCount" | "title";
type SortOrder = "asc" | "desc";

const ALLOWED_FIELDS: AllowedSortField[] = ["createdAt", "viewCount", "title"];

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);

  const sortField = searchParams.get("sort") ?? "createdAt";
  const sortOrder = searchParams.get("order") === "asc" ? "asc" : "desc";

  const field: AllowedSortField = (ALLOWED_FIELDS as string[]).includes(sortField)
    ? (sortField as AllowedSortField)
    : "createdAt";

  const orderBy: Prisma.PostOrderByWithRelationInput = {
    [field]: sortOrder as SortOrder,
  };

  const posts = await prisma.post.findMany({
    orderBy,
    select: { id: true, title: true, viewCount: true, createdAt: true },
  });

  return NextResponse.json({ posts });
}

ポイント

  • 単一フィールドのソートはオブジェクト { field: "asc" | "desc" } で指定する。複数フィールドの優先順位付きソートは配列 [{ field1: "desc" }, { field2: "asc" }] を使う
  • nulls: "first" | "last" で null 値の並び順を制御できる。DB によってデフォルト挙動が異なるため、期待する順序がある場合は明示的に指定する
  • リレーション先フィールドのソートはネストオブジェクト { relation: { field: "asc" } } で表現する。_count を使うとリレーションの件数でソートできる
  • Route Handler でソートキーを受け取るときは allowlist で許可フィールドを制限する。ユーザー入力を直接 orderBy に渡すと任意フィールドアクセスのリスクがある
  • prisma-pagination-query との組み合わせ: findManyorderByskip / take の両方を指定できる。ソート順を固定してページネーションすることで、ページをまたいで重複や欠落が起きないようにする

注意点

prisma-pagination-query は skip/take によるページネーション。prisma-relation-query は include/select による関連取得。nextjs-api-sort は Route Handler でのソートパラメータ受け取り。これは Prisma 側の orderBy による単一・複数・リレーション先フィールドのソートパターンに特化。null 値の並び順制御も示す。

関連サンプル