From 21f84c920d4ca06490187115aa6223c9d6e9b10c Mon Sep 17 00:00:00 2001 From: willACosta Date: Tue, 1 Oct 2024 17:56:30 -0300 Subject: [PATCH] refactor(users): improve api readability using safeApiCall reusable function --- src/app.ts | 2 +- src/common/functions/index.ts | 1 + .../functions/safe-api-call.function.ts | 21 +++++++++ .../is-authenticated.middleware.ts | 11 ++--- src/common/types/index.ts | 3 +- src/common/types/network.types.ts | 20 +++++++++ .../{usecase.type.ts => usecase.types.ts} | 2 +- .../dataproviders/user.dataprovider.ts | 36 +++++++++++---- .../controllers/users.controller.ts | 45 ++++++++++++++++--- src/modules/users/core/entities/index.ts | 2 +- 10 files changed, 118 insertions(+), 25 deletions(-) create mode 100644 src/common/functions/index.ts create mode 100644 src/common/functions/safe-api-call.function.ts create mode 100644 src/common/types/network.types.ts rename src/common/types/{usecase.type.ts => usecase.types.ts} (96%) diff --git a/src/app.ts b/src/app.ts index 6e92c05..83d14ff 100644 --- a/src/app.ts +++ b/src/app.ts @@ -19,7 +19,7 @@ app.use(express.json()) app.use('/', AuthRoutes) app.use('/status', StatusRoutes) -app.use('/user', UserRoutes) +app.use('/users', UserRoutes) app.use('/gen-ai', GenAIRoutes) app.listen(port, () => { diff --git a/src/common/functions/index.ts b/src/common/functions/index.ts new file mode 100644 index 0000000..2ce7288 --- /dev/null +++ b/src/common/functions/index.ts @@ -0,0 +1 @@ +export * from './safe-api-call.function' diff --git a/src/common/functions/safe-api-call.function.ts b/src/common/functions/safe-api-call.function.ts new file mode 100644 index 0000000..56ea06c --- /dev/null +++ b/src/common/functions/safe-api-call.function.ts @@ -0,0 +1,21 @@ +import { AppResponse } from '../types' + +type ApiHandler = () => Promise + +export async function safeApiCall( + handler: ApiHandler, + res: AppResponse, +): Promise> { + try { + const data = await handler() + return res.status(200).json({ + success: true, + data, + }) + } catch (err: any) { + return res.status(500).json({ + success: false, + error: err, + }) + } +} diff --git a/src/common/middlewares/is-authenticated.middleware.ts b/src/common/middlewares/is-authenticated.middleware.ts index 48c8dec..9f24609 100644 --- a/src/common/middlewares/is-authenticated.middleware.ts +++ b/src/common/middlewares/is-authenticated.middleware.ts @@ -1,13 +1,10 @@ -import { NextFunction, Request, Response } from 'express' +import { NextFunction, Response } from 'express' import jwt from 'jsonwebtoken' -export interface AuthRequest extends Request { - user?: object - token?: string -} +import { AppRequest } from '../types' export function isAuthenticated( - req: AuthRequest, + req: AppRequest, res: Response, next: NextFunction, ) { @@ -26,7 +23,7 @@ export function isAuthenticated( return res.status(401).json({ success: false, error: { - message: 'Invalid auth mechanism.', + message: 'Invalid auth mechanism. This API supports only Bearer token.', }, }) } diff --git a/src/common/types/index.ts b/src/common/types/index.ts index ec2142c..6cf1c14 100644 --- a/src/common/types/index.ts +++ b/src/common/types/index.ts @@ -1 +1,2 @@ -export * from './usecase.type' +export * from './network.types' +export * from './usecase.types' diff --git a/src/common/types/network.types.ts b/src/common/types/network.types.ts new file mode 100644 index 0000000..7f2e5a7 --- /dev/null +++ b/src/common/types/network.types.ts @@ -0,0 +1,20 @@ +import { User } from '@/modules/users/core' +import { Request, Response } from 'express' + +export interface AppRequest< + P extends { [key: string]: string } = { + [key: string]: string + }, + T = any, + R = any, + S = any, +> extends Request { + user?: User + token?: string +} + +export type AppResponse = Response<{ + success: boolean + data?: T + error?: { message: string; code?: string } +}> diff --git a/src/common/types/usecase.type.ts b/src/common/types/usecase.types.ts similarity index 96% rename from src/common/types/usecase.type.ts rename to src/common/types/usecase.types.ts index 0aa97d7..ba019f3 100644 --- a/src/common/types/usecase.type.ts +++ b/src/common/types/usecase.types.ts @@ -1,3 +1,3 @@ export interface UseCase { invoke(params: P): Promise -} \ No newline at end of file +} diff --git a/src/modules/users/adapters/dataproviders/user.dataprovider.ts b/src/modules/users/adapters/dataproviders/user.dataprovider.ts index 358a4fe..590f801 100644 --- a/src/modules/users/adapters/dataproviders/user.dataprovider.ts +++ b/src/modules/users/adapters/dataproviders/user.dataprovider.ts @@ -1,18 +1,34 @@ import { prismaClient } from '@/di' +import { Prisma } from '@prisma/client' import { UserParams } from '../../core' +export type PrismaUser = Prisma.PromiseReturnType< + typeof UserDataProvider.prototype.getUserById +> + export class UserDataProvider { - async insert({ name, email, password }: UserParams) { + private _selectUserProperties = { + id: true, + name: true, + email: true, + createdAt: true, + } + + insert = async ({ name, email, password }: UserParams) => { return await prismaClient.users.create({ data: { name, email, password, }, + select: this._selectUserProperties, }) } - async update({ id, name, email, password }: UserParams) { + update = async ( + id: string, + { name, email }: { name: string; email: string }, + ) => { return await prismaClient.users.update({ where: { id, @@ -20,32 +36,36 @@ export class UserDataProvider { data: { name, email, - password, }, + select: this._selectUserProperties, }) } - async delete(id: string) { + delete = async (id: string) => { return await prismaClient.users.delete({ where: { id, }, + select: this._selectUserProperties, }) } - async getAllUsers() { - return await prismaClient.users.findMany() + getAllUsers = async () => { + return await prismaClient.users.findMany({ + select: this._selectUserProperties, + }) } - async getUserById(id: string) { + getUserById = async (id: string) => { return await prismaClient.users.findUnique({ where: { id, }, + select: this._selectUserProperties, }) } - async findUserByEmail(email: string) { + findUserByEmail = async (email: string) => { return await prismaClient.users.findUnique({ where: { email, diff --git a/src/modules/users/application/controllers/users.controller.ts b/src/modules/users/application/controllers/users.controller.ts index 2938243..67a62de 100644 --- a/src/modules/users/application/controllers/users.controller.ts +++ b/src/modules/users/application/controllers/users.controller.ts @@ -1,12 +1,45 @@ -import { Request, Response } from 'express' +import { AppRequest, AppResponse } from '@/common/types' -import { UserDataProvider } from '../../adapters/dataproviders/user.dataprovider' +import { + PrismaUser, + UserDataProvider, +} from '../../adapters/dataproviders/user.dataprovider' + +import { safeApiCall } from '@/common/functions' export class UsersController { constructor(private _userProvider: UserDataProvider) {} - async getUser(req: Request, res: Response) {} - async getAllUsers() {} - async updateUser() {} - async deleteUser() {} + getUser = async (req: AppRequest, res: AppResponse) => { + return await safeApiCall( + () => this._userProvider.getUserById(req.user?.id!), + res, + ) + } + + getAllUsers = async (_req: AppRequest, res: AppResponse) => { + return await safeApiCall(() => this._userProvider.getAllUsers(), res) + } + + updateUser = async ( + req: AppRequest<{ id: string }>, + res: AppResponse, + ) => { + const { id } = req.params + const { name, email } = req.body + + return await safeApiCall( + () => + this._userProvider.update(id, { + name, + email, + }), + res, + ) + } + + deleteUser = async (req: AppRequest, res: AppResponse) => { + const { id } = req.params + return await safeApiCall(() => this._userProvider.delete(id), res) + } } diff --git a/src/modules/users/core/entities/index.ts b/src/modules/users/core/entities/index.ts index 3533f45..23182b3 100644 --- a/src/modules/users/core/entities/index.ts +++ b/src/modules/users/core/entities/index.ts @@ -1,5 +1,5 @@ export type User = { - id?: string + id: string email: string createdAt: Date password: string