# Admin can search orders by order ID or customer identifier

- **Pattern:** `ab-001150` (`ecommerce-order-management.admin-management.order-search`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001150
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001150)

## Why it matters

An admin who cannot search orders by identifier cannot do their job. When a customer contacts support with 'my order ORD-48291 hasn't arrived', the admin must be able to pull up that record in under 10 seconds. Without server-side search across at least order ID and customer email, admins are left scrolling a paginated list — and for stores with hundreds of orders, that means support contacts go unresolved or require direct database access. CWE-285 applies here in an operational context: the admin role lacks the access controls and tooling its responsibilities require. iso-25010:2011 operability demands that the system is usable by the people maintaining it.

## Severity rationale

High because the inability to search orders by identifier makes customer support operationally impossible at any meaningful order volume, forcing admins to use direct database access.

## Remediation

Add a `?q=` search parameter to the admin orders API at `app/api/admin/orders/route.ts` with an OR query across order ID and customer email.

```ts
// app/api/admin/orders/route.ts
const query = new URL(req.url).searchParams.get('q')

const where = query
  ? {
      OR: [
        { id: { contains: query } },
        { user: { email: { contains: query, mode: 'insensitive' as const } } },
        { user: { name: { contains: query, mode: 'insensitive' as const } } },
      ],
    }
  : {}

const orders = await db.orders.findMany({
  where,
  include: { user: true },
  orderBy: { createdAt: 'desc' },
  take: 50,
})
```

Wire a debounced search input in the admin UI to this endpoint.

## Detection

- **ID:** `order-search`
- **Severity:** `high`
- **What to look for:** Find the admin order management section — typically a page at `/admin/orders` or `/dashboard/orders`. Count the number of searchable fields in the admin orders API endpoint. Enumerate which identifiers are supported: order ID, customer email, customer name, phone number, etc. At least 2 searchable fields are required. Quote the exact query parameter name used (e.g., `?q=` or `?search=`). Examine the query logic — is it a `contains`/`ILIKE` search, an exact match, or a full-text search?
- **Pass criteria:** An admin search interface exists and supports searching by at least 2 identifiers: order ID and customer email (or customer name). The search returns matching orders with key metadata (status, total, date). The search is functional — it actually queries the database (via `findMany`, `SELECT`, etc.) rather than filtering a pre-loaded client-side list. Report the count even on pass: "Admin search supports N searchable fields."
- **Fail criteria:** No order search functionality exists in the admin section (0 searchable fields). Admins can only view a paginated list of orders with no way to find a specific order. Or a search input exists in the UI but the backing query is not implemented (returns all results regardless of search term).
- **Skip (N/A) when:** The project has no admin section — all order management is performed through a third-party platform with its own search. No admin orders page exists in the codebase.
- **Detail on fail:** `"The admin orders page at /admin/orders displays a paginated list of all orders but has 0 searchable fields. Finding a specific order by ID or customer requires scrolling through all pages or querying the database directly."`
- **Remediation:** Add search to your admin orders API (`app/api/admin/orders/route.ts`) and UI:

  ```ts
  // app/api/admin/orders/route.ts
  export async function GET(req: Request) {
    const { searchParams } = new URL(req.url)
    const query = searchParams.get('q')

    const where = query
      ? {
          OR: [
            { id: { contains: query } },
            { user: { email: { contains: query, mode: 'insensitive' } } },
            { user: { name: { contains: query, mode: 'insensitive' } } },
          ],
        }
      : {}

    const orders = await db.orders.findMany({
      where,
      include: { user: true, items: true },
      orderBy: { createdAt: 'desc' },
      take: 50,
    })

    return Response.json({ orders })
  }
  ```

  In the admin UI, wire a debounced search input to this endpoint so results update as the admin types.

## External references

- cwe CWE-285
- iso-25010 operability

Taxons: observability

HTML version: https://auditbuffet.com/patterns/ab-001150
