Skip to content

Searchable, paginated data with Prisma & Next.js

A common use case with a simple API route

Need to revisit. I would use Zod for type inference.
import {Teammate} from '@prisma/client';
import Joi from 'joi';
import type {NextApiRequest, NextApiResponse} from 'next';
 
import {dbClient} from '@/b/db/db-client';
import {getUserMetadata} from '@/b/utils/get-all-user-metadata';
import {getSessionOrThrow} from '@/b/utils/get-user-session';
import {validateSchema} from '@/b/utils/validate-schema';
 
const DEFAULT_LIMIT = 20;
 
const schema = Joi.object({
  page: Joi.number().min(1).default(1),
  limit: Joi.number().min(1).max(100).default(DEFAULT_LIMIT),
  searchTerm: Joi.string().trim().optional().empty(''),
});
 
export type GetAllTeammatesProps = {
  page?: number;
  limit?: number;
  searchTerm?: string;
};
 
export type GetAllTeammatesData = {
  data: Teammate[];
  pagination: {
    total: number;
    pageCount: number;
    currentPage: number;
    perPage: number;
    from: number;
    to: number;
  };
};
 
export async function getAllTeammates(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<GetAllTeammatesData> {
  try {
    const {userEmail} = await getSessionOrThrow(req);
 
    const metadata = validateSchema<GetAllTeammatesProps>(schema, req.query);
    const user = await getUserMetadata(userEmail);
 
    const {page = 1, limit = DEFAULT_LIMIT, searchTerm} = metadata;
 
    const searchTermFilter = searchTerm
      ? {OR: [{name: {contains: searchTerm}}, {email: {contains: searchTerm}}]}
      : {};
 
    const teammatesCount = await dbClient.teammate.count({
      where: {orgId: user.membership.orgId, ...searchTermFilter},
    });
 
    const teammates = await dbClient.teammate.findMany({
      where: {orgId: user.membership.orgId, ...searchTermFilter},
      skip: limit * (page - 1),
      take: limit,
    });
 
    const response = {
      data: teammates,
      pagination: {
        total: teammatesCount,
        pageCount: Math.ceil(teammatesCount / limit),
        currentPage: page,
        perPage: limit,
        from: (page - 1) * limit + 1,
        to: (page - 1) * limit + teammates.length,
      },
    };
 
    return res.status(200).json(response);
  } catch (error) {
    console.log(error);
    res.status(422).json({message: error.message});
  }
}