diff --git a/src/app/api/users/route.ts b/src/app/api/users/route.ts new file mode 100644 index 0000000..a07a6d3 --- /dev/null +++ b/src/app/api/users/route.ts @@ -0,0 +1,24 @@ +import { user } from "@/dao/user.dao"; +import { PaginationParamsSchema } from "@/shared/pagination"; +import { NextResponse, NextRequest } from "next/server"; +import { z } from "zod"; + +const ListAllUsersParamsSchema = z + .object({ + name: z.string().optional(), + username: z.string().optional(), + email: z.string().optional(), + }) + .merge(PaginationParamsSchema); + +export async function GET(req: NextRequest) { + const queryParams = ListAllUsersParamsSchema.parse( + Object.fromEntries(req.nextUrl.searchParams) + ); + + const { page, pageSize, ...filters } = queryParams; + + const users = await user.fetchUsers(filters, page, pageSize); + + return NextResponse.json(users); +} diff --git a/src/dao/user.dao.ts b/src/dao/user.dao.ts index 7aa7ed2..111185f 100644 --- a/src/dao/user.dao.ts +++ b/src/dao/user.dao.ts @@ -4,6 +4,7 @@ import { z } from "zod"; import { prisma } from "./client"; import { raise } from "@/shared/exceptions"; import { formSchema } from "@/shared/onboarding"; +import { GetUsers } from "@/entities/users"; class UserDAO { async createUser(loginUser: NextUser) { @@ -28,6 +29,44 @@ class UserDAO { }); } + async fetchUsers(filter: GetUsers, page = 1, pageSize = 10) { + const skip = (page - 1) * pageSize; + const whereCondition: Prisma.Enumerable = []; + + Object.keys(filter).forEach((key) => { + const property = key as keyof typeof filter; + const value = filter[property]; + + if (value !== undefined) { + whereCondition.push({ + [key]: { contains: value, mode: "insensitive" }, + }); + } + }); + + let where: Prisma.UserWhereInput = {}; + if (whereCondition.length > 0) { + where = { OR: whereCondition }; + } + + const [users, total] = await prisma.$transaction([prisma.user.findMany({ + where: where, + skip, + take: pageSize, + }), prisma.user.count({ + where: where, + }),]); + + return { + total, + data: users, + pagination: { + page, + pageSize, + }, + }; + } + async isOboardingFinished(username: string) { const user = (await this.fetchUserBy({ username })) ?? diff --git a/src/entities/users.ts b/src/entities/users.ts index 794df3b..142a21d 100644 --- a/src/entities/users.ts +++ b/src/entities/users.ts @@ -1,11 +1,17 @@ import { Session } from "next-auth"; - +import { User as UserEntity } from "@prisma/client"; export type User = { name: string; email: string; image: string; }; +export type GetUsers = { + name?: UserEntity["name"]; + email?: UserEntity["email"]; + username?: UserEntity["username"]; +}; + export function getUserSession(session: Session | null): User { if (session?.user == null) { throw new Error("User session not found"); diff --git a/src/shared/pagination.ts b/src/shared/pagination.ts new file mode 100644 index 0000000..57c7593 --- /dev/null +++ b/src/shared/pagination.ts @@ -0,0 +1,9 @@ +import { z } from "zod"; + +const DEFAULT_PAGE = 1; +const DEFAULT_PAGE_SIZE = 10; + +export const PaginationParamsSchema = z.object({ + page: z.coerce.number().min(1).default(DEFAULT_PAGE), + pageSize: z.coerce.number().min(10).max(100).default(DEFAULT_PAGE_SIZE), +});