From fa92c9126139f0519877f7d164ce04cd2bf49cfe Mon Sep 17 00:00:00 2001 From: Pierre Berger Date: Thu, 1 Feb 2024 21:09:32 +0100 Subject: [PATCH] chore: change architecture (#3) --- bun.lockb | Bin 161677 -> 162537 bytes package.json | 3 ++- prisma/seed.ts | 4 +-- src/app.ts | 5 +++- .../example/handler/example.handler.ts | 1 + .../example/router/example.router.test.ts | 2 +- .../example/router/example.router.ts} | 7 +++--- src/domains/users/db/findAllUsers.db.ts | 3 --- .../db/{createUser.db.ts => users.db.ts} | 4 ++- src/domains/users/handler/users.handler.ts | 14 +++++++++++ .../users/router/users.router.ts} | 23 +++++++++--------- .../{user.schema.ts => users.schema.ts} | 0 src/libs/__mocks__/prisma.ts | 10 ++++++++ src/libs/prisma.ts | 4 +++ src/plugins/prisma.plugin.ts | 21 ---------------- src/routes/root.route.ts | 9 ------- test/routes/root.test.ts | 10 -------- 17 files changed, 55 insertions(+), 65 deletions(-) create mode 100644 src/domains/example/handler/example.handler.ts rename test/routes/example.test.ts => src/domains/example/router/example.router.test.ts (80%) rename src/{routes/example/index.route.ts => domains/example/router/example.router.ts} (78%) delete mode 100644 src/domains/users/db/findAllUsers.db.ts rename src/domains/users/db/{createUser.db.ts => users.db.ts} (61%) create mode 100644 src/domains/users/handler/users.handler.ts rename src/{routes/users/users.route.ts => domains/users/router/users.router.ts} (53%) rename src/domains/users/schemas/{user.schema.ts => users.schema.ts} (100%) create mode 100644 src/libs/__mocks__/prisma.ts create mode 100644 src/libs/prisma.ts delete mode 100644 src/plugins/prisma.plugin.ts delete mode 100644 src/routes/root.route.ts delete mode 100644 test/routes/root.test.ts diff --git a/bun.lockb b/bun.lockb index 72b1d6b60297d5c6e05f19b9f995d518ffa0d086..9b4f15b4aec9fec0fdf353176f02649bebc27499 100755 GIT binary patch delta 26799 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 diff --git a/package.json b/package.json index 823fa66..603beca 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "ts-node": "^10.4.0", "ts-node-dev": "^2.0.0", "typescript": "^5.2.2", - "vitest": "^1.2.2" + "vitest": "^1.2.2", + "vitest-mock-extended": "^1.3.1" } } diff --git a/prisma/seed.ts b/prisma/seed.ts index 058c1eb..f64841c 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,8 +1,6 @@ import type { Prisma } from '@prisma/client' -import { PrismaClient } from '@prisma/client' import { cleanupDb } from '../test/utils/db.util' - -const prisma = new PrismaClient() +import prisma from '../src/libs/prisma' const users: Prisma.UserCreateInput[] = [ { diff --git a/src/app.ts b/src/app.ts index 6899dac..a56c551 100644 --- a/src/app.ts +++ b/src/app.ts @@ -16,7 +16,10 @@ const app: FastifyPluginAsync = async (fastify, opts): Promise => { fastify.setSerializerCompiler(serializerCompiler) fastify.register(AutoLoad, { - dir: path.join(__dirname, 'routes'), + dir: path.join(__dirname, 'domains'), + dirNameRoutePrefix: false, + matchFilter: path => path.endsWith('router.ts'), + ignoreFilter: path => path.endsWith('.test.ts'), options: opts, }) } diff --git a/src/domains/example/handler/example.handler.ts b/src/domains/example/handler/example.handler.ts new file mode 100644 index 0000000..40b03b0 --- /dev/null +++ b/src/domains/example/handler/example.handler.ts @@ -0,0 +1 @@ +export const exampleHandler = () => 'this is an example' diff --git a/test/routes/example.test.ts b/src/domains/example/router/example.router.test.ts similarity index 80% rename from test/routes/example.test.ts rename to src/domains/example/router/example.router.test.ts index 3e502c8..200f5df 100644 --- a/test/routes/example.test.ts +++ b/src/domains/example/router/example.router.test.ts @@ -1,5 +1,5 @@ import { it, expect } from 'vitest' -import fastify from '../helper' +import fastify from '../../../../test/helper' it('example is loaded', async () => { const res = await fastify.inject({ diff --git a/src/routes/example/index.route.ts b/src/domains/example/router/example.router.ts similarity index 78% rename from src/routes/example/index.route.ts rename to src/domains/example/router/example.router.ts index eb9b76e..d0c3a93 100644 --- a/src/routes/example/index.route.ts +++ b/src/domains/example/router/example.router.ts @@ -1,16 +1,15 @@ import type { FastifyPluginAsync } from 'fastify' import type { ZodTypeProvider } from 'fastify-type-provider-zod' import { z } from 'zod' +import { exampleHandler } from '../handler/example.handler' const example: FastifyPluginAsync = async (fastify, _opts): Promise => { fastify .withTypeProvider() .get( - '/', + '/example', { schema: { response: { 200: z.string() } } }, - async (_request, _reply) => { - return 'this is an example' - }, + exampleHandler, ) } diff --git a/src/domains/users/db/findAllUsers.db.ts b/src/domains/users/db/findAllUsers.db.ts deleted file mode 100644 index 340b7e3..0000000 --- a/src/domains/users/db/findAllUsers.db.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { PrismaClient } from '@prisma/client' - -export const findAllUsers = (prisma: PrismaClient) => prisma.user.findMany() diff --git a/src/domains/users/db/createUser.db.ts b/src/domains/users/db/users.db.ts similarity index 61% rename from src/domains/users/db/createUser.db.ts rename to src/domains/users/db/users.db.ts index e3a0c63..9c88f5d 100644 --- a/src/domains/users/db/createUser.db.ts +++ b/src/domains/users/db/users.db.ts @@ -1,5 +1,5 @@ import type { PrismaClient } from '@prisma/client' -import type { CreateUserSchema } from '../schemas/user.schema' +import type { CreateUserSchema } from '../schemas/users.schema' export const createUser = (prisma: PrismaClient, user: CreateUserSchema) => prisma.user.create({ @@ -8,3 +8,5 @@ export const createUser = (prisma: PrismaClient, user: CreateUserSchema) => name: user.name, }, }) + +export const findAllUsers = (prisma: PrismaClient) => prisma.user.findMany() diff --git a/src/domains/users/handler/users.handler.ts b/src/domains/users/handler/users.handler.ts new file mode 100644 index 0000000..672fe97 --- /dev/null +++ b/src/domains/users/handler/users.handler.ts @@ -0,0 +1,14 @@ +import { createUser, findAllUsers } from '../db/users.db' +import prisma from '../../../libs/prisma' +import type { FastifyReply, FastifyRequest } from 'fastify' +import type { CreateUserSchema } from '../schemas/users.schema' + +export const findAllUsersHandler = () => findAllUsers(prisma) + +export const createUserHandler = async ( + req: FastifyRequest<{ Body: CreateUserSchema }>, + reply: FastifyReply, +) => { + const user = await createUser(prisma, req.body) + reply.code(201).send(user) +} diff --git a/src/routes/users/users.route.ts b/src/domains/users/router/users.router.ts similarity index 53% rename from src/routes/users/users.route.ts rename to src/domains/users/router/users.router.ts index 1c79a95..bb83ac1 100644 --- a/src/routes/users/users.route.ts +++ b/src/domains/users/router/users.router.ts @@ -1,22 +1,26 @@ import type { FastifyPluginAsync } from 'fastify' import type { ZodTypeProvider } from 'fastify-type-provider-zod' -import { findAllUsers } from '../../domains/users/db/findAllUsers.db' import { createUserResponseSchema, createUserSchema, findAllUsersSchema, -} from '../../domains/users/schemas/user.schema' -import { createUser } from '../../domains/users/db/createUser.db' +} from '../schemas/users.schema' +import { + createUserHandler, + findAllUsersHandler, +} from '../handler/users.handler' const users: FastifyPluginAsync = async (fastify, _opts) => { fastify .withTypeProvider() - .get('/', { schema: { response: { 200: findAllUsersSchema } } }, () => { - return findAllUsers(fastify.prisma) - }) + .get( + '/users', + { schema: { response: { 200: findAllUsersSchema } } }, + findAllUsersHandler, + ) fastify.withTypeProvider().post( - '/', + '/users', { schema: { body: createUserSchema, @@ -25,10 +29,7 @@ const users: FastifyPluginAsync = async (fastify, _opts) => { }, }, }, - async (request, reply) => { - const user = await createUser(fastify.prisma, request.body) - reply.code(201).send(user) - }, + createUserHandler, ) } diff --git a/src/domains/users/schemas/user.schema.ts b/src/domains/users/schemas/users.schema.ts similarity index 100% rename from src/domains/users/schemas/user.schema.ts rename to src/domains/users/schemas/users.schema.ts diff --git a/src/libs/__mocks__/prisma.ts b/src/libs/__mocks__/prisma.ts new file mode 100644 index 0000000..037d7bc --- /dev/null +++ b/src/libs/__mocks__/prisma.ts @@ -0,0 +1,10 @@ +import type { PrismaClient } from '@prisma/client' +import { beforeEach } from 'vitest' +import { mockDeep, mockReset } from 'vitest-mock-extended' + +beforeEach(() => { + mockReset(prisma) +}) + +const prisma = mockDeep() +export default prisma diff --git a/src/libs/prisma.ts b/src/libs/prisma.ts new file mode 100644 index 0000000..cad25fe --- /dev/null +++ b/src/libs/prisma.ts @@ -0,0 +1,4 @@ +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() +export default prisma diff --git a/src/plugins/prisma.plugin.ts b/src/plugins/prisma.plugin.ts deleted file mode 100644 index 087d0c7..0000000 --- a/src/plugins/prisma.plugin.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { PrismaClient } from '@prisma/client' -import type { FastifyPluginAsync } from 'fastify' -import fp from 'fastify-plugin' - -declare module 'fastify' { - interface FastifyInstance { - prisma: PrismaClient - } -} - -export default fp(async (fastify, _opts) => { - const prisma = new PrismaClient() - - await prisma.$connect() - - fastify.decorate('prisma', prisma) - - fastify.addHook('onClose', async fastify => { - await fastify.prisma.$disconnect() - }) -}) diff --git a/src/routes/root.route.ts b/src/routes/root.route.ts deleted file mode 100644 index 3ce3b3b..0000000 --- a/src/routes/root.route.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { FastifyPluginAsync } from 'fastify' - -const root: FastifyPluginAsync = async (fastify, _opts): Promise => { - fastify.get('/', async (_request, _reply) => { - return { root: true } - }) -} - -export default root diff --git a/test/routes/root.test.ts b/test/routes/root.test.ts deleted file mode 100644 index 9147cfc..0000000 --- a/test/routes/root.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { it, expect } from 'vitest' - -import fastify from '../helper' - -it('default root route', async () => { - const res = await fastify.inject({ - url: '/', - }) - expect(JSON.parse(res.payload)).toHaveProperty('root', true) -})