Skip to content

Commit

Permalink
add and implement getQueryParams
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-hendrix committed Oct 21, 2023
1 parent fcb72fc commit a1c8526
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 7 deletions.
8 changes: 3 additions & 5 deletions src/app/api/users/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'
import { Prisma } from '@prisma/client'
import prisma from '@/lib/prisma'
import { generateHash } from '@/utils/hash'
import { ApiError, routeWrapper } from '@/utils/api'
import { ApiError, routeWrapper, getQueryParams } from '@/utils/api'

export const sanitizeUserSelect = () => {
const fields = Object.keys(Prisma.UserScalarFieldEnum)
Expand All @@ -29,11 +29,9 @@ export const checkUserBody = async (body: any, id: string | null = null) => {

export const GET = routeWrapper(
async (req: NextRequest) => {
const skip = req.nextUrl.searchParams.get('skip')
const take = req.nextUrl.searchParams.get('take')
const queryParams = getQueryParams(req.nextUrl)
const users = await prisma.user.findMany({
skip: skip && take ? Number(skip) : undefined,
take: skip && take ? Number(take) : undefined,
...queryParams,
select: sanitizeUserSelect()
})
return NextResponse.json(users)
Expand Down
4 changes: 2 additions & 2 deletions src/store/user.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { User } from '@prisma/client'
import { Prisma, User } from '@prisma/client'

export const userApi = createApi({
reducerPath: 'userApi',
Expand All @@ -10,7 +10,7 @@ export const userApi = createApi({
query: (id) => `users/${id}`,
providesTags: (_result, _err, id) => [{ type: 'User', id }],
}),
getUsers: build.query<User[], { skip: number, take: number } | undefined>({
getUsers: build.query<User[], Prisma.UserFindManyArgs | undefined>({
query: (params) => ({
url: 'users',
params
Expand Down
51 changes: 51 additions & 0 deletions src/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import { NextRequest, NextResponse } from 'next/server'
import { getServerSession } from 'next-auth'
import authOptions from '@/lib/auth'
import { NextURL } from 'next/dist/server/web/next-url'

/**
* Custom error class representing API errors with a specific status code.
*/
export class ApiError extends Error {
public readonly statusCode: number

Expand All @@ -15,6 +19,11 @@ export class ApiError extends Error {
}
}

/**
* Sanitizes sensitive information from the request body for logging purposes.
* @param consumedBody - The request body to be sanitized.
* @returns Sanitized request body.
*/
const sanitizeBody = (consumedBody: any) => {
const sanitizedText = '*****'
const sanitizedBody = { ...consumedBody }
Expand All @@ -23,6 +32,10 @@ const sanitizeBody = (consumedBody: any) => {
return sanitizedBody
}

/**
* Logs request details to the console for debugging purposes.
* @param req - The incoming request object.
*/
const logRequest = (req: NextRequest) => {
if (process.env.NODE_ENV === 'test') return
const { method, url, consumedBody } = req
Expand All @@ -33,18 +46,32 @@ const logRequest = (req: NextRequest) => {
console.info('---')
}

/**
* Logs error details to the console for debugging purposes.
* @param error - The error object to be logged.
*/
const logError = (error: any) => {
if (process.env.NODE_ENV === 'test') return
console.info(`Error: ${JSON.stringify(error)}`)
console.info('---')
}

/**
* Extracts a human-readable error message from the given error string.
* @param message - The error message string.
* @returns Human-readable error message.
*/
const getErrorMessage = (message: string) => {
if (message.includes('Unique constraint failed on the fields: (`username`)')) return 'Username exists'
if (message.includes('Unique constraint failed on the fields: (`email`)')) return 'Email exists'
return 'Server failure'
}

/**
* Wraps a route handler function with error handling and request logging.
* @param routeHandler - The route handler function to be wrapped.
* @returns Wrapped route handler function.
*/
export const routeWrapper = (
routeHandler: (
req: NextRequest, context?: any) => Promise<NextResponse>
Expand All @@ -70,13 +97,37 @@ export const routeWrapper = (
}
}

/**
* Retrieves the user session information from the server session.
* @returns User session information.
* @throws ApiError if the user is not authenticated.
*/
export const withSessionUser = async () => {
const session = await getServerSession(authOptions)
if (!session) throw new ApiError('Unauthorized', 401)
return session?.user
}

/**
* Checks if the provided user ID matches the user ID in the session.
* @param userId - The user ID to be checked.
* @throws ApiError if the user IDs do not match, indicating unauthorized access.
*/
export const checkUserMatchesSession = async (userId: string | undefined) => {
const session = await getServerSession(authOptions)
if (session?.user?.id !== userId) throw new ApiError('Unauthorized', 401)
}

/**
* Parses and extracts query parameters from a Next.js URL object.
* @param nextUrl - Next.js URL object containing query parameters.
* @returns Parsed query parameters as an object.
*/
export const getQueryParams = (nextUrl: NextURL) => {
const queryParams = Array.from(nextUrl.searchParams.entries()).reduce((acc, [key, value]) => {
const numericValue = !Number.isNaN(Number(value)) ? Number(value) : value
return { ...acc, [key]: numericValue }
}, {} as { [key: string]: string | number })

return queryParams
}

0 comments on commit a1c8526

Please sign in to comment.