diff --git a/backend/src/app.module.ts b/backend/apps/api/src/api.module.ts similarity index 89% rename from backend/src/app.module.ts rename to backend/apps/api/src/api.module.ts index 8662803..763be8e 100644 --- a/backend/src/app.module.ts +++ b/backend/apps/api/src/api.module.ts @@ -7,4 +7,4 @@ import { AppService } from './app.service'; controllers: [AppController], providers: [AppService], }) -export class AppModule {} +export class ApiModule {} diff --git a/backend/src/app.controller.spec.ts b/backend/apps/api/src/app.controller.spec.ts similarity index 100% rename from backend/src/app.controller.spec.ts rename to backend/apps/api/src/app.controller.spec.ts diff --git a/backend/src/app.controller.ts b/backend/apps/api/src/app.controller.ts similarity index 100% rename from backend/src/app.controller.ts rename to backend/apps/api/src/app.controller.ts diff --git a/backend/src/app.service.ts b/backend/apps/api/src/app.service.ts similarity index 100% rename from backend/src/app.service.ts rename to backend/apps/api/src/app.service.ts diff --git a/backend/apps/api/src/main.ts b/backend/apps/api/src/main.ts new file mode 100644 index 0000000..152170b --- /dev/null +++ b/backend/apps/api/src/main.ts @@ -0,0 +1,13 @@ +import { NestFactory } from '@nestjs/core'; +import { ApiModule } from './api.module'; + +export async function bootstrapApi(): Promise { + const app = await NestFactory.create(ApiModule); + app.enableShutdownHooks(); + app.setGlobalPrefix('api/v1'); + await app.listen(3000, '0.0.0.0'); +} + +if (require.main === module) { + void bootstrapApi(); +} diff --git a/backend/test/app.e2e-spec.ts b/backend/apps/api/test/api.e2e-spec.ts similarity index 83% rename from backend/test/app.e2e-spec.ts rename to backend/apps/api/test/api.e2e-spec.ts index a767839..59a4dba 100644 --- a/backend/test/app.e2e-spec.ts +++ b/backend/apps/api/test/api.e2e-spec.ts @@ -2,14 +2,14 @@ import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; import request from 'supertest'; import { App } from 'supertest/types'; -import { AppModule } from './../src/app.module'; +import { ApiModule } from './../src/api.module'; -describe('AppController (e2e)', () => { +describe('ApiModule (e2e)', () => { let app: INestApplication; beforeEach(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], + imports: [ApiModule], }).compile(); app = moduleFixture.createNestApplication(); diff --git a/backend/test/jest-e2e.json b/backend/apps/api/test/jest-e2e.json similarity index 63% rename from backend/test/jest-e2e.json rename to backend/apps/api/test/jest-e2e.json index e9d912f..a750e43 100644 --- a/backend/test/jest-e2e.json +++ b/backend/apps/api/test/jest-e2e.json @@ -1,8 +1,8 @@ { "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", + "rootDir": "../../..", "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", + "testRegex": "apps/api/test/.*\\.e2e-spec\\.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" } diff --git a/backend/apps/api/tsconfig.app.json b/backend/apps/api/tsconfig.app.json new file mode 100644 index 0000000..9e5319c --- /dev/null +++ b/backend/apps/api/tsconfig.app.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": false, + "outDir": "../../dist/apps/api" + }, + "exclude": ["node_modules", "dist", "test", "**/*.spec.ts"] +} diff --git a/backend/apps/worker/src/main.ts b/backend/apps/worker/src/main.ts new file mode 100644 index 0000000..76e8869 --- /dev/null +++ b/backend/apps/worker/src/main.ts @@ -0,0 +1,20 @@ +import { INestApplicationContext, Type } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { WorkerModule } from './worker.module'; + +type ContextFactory = ( + module: Type, +) => Promise; + +export async function bootstrapWorker( + createContext: ContextFactory = (module) => + NestFactory.createApplicationContext(module), +): Promise { + const app = await createContext(WorkerModule); + app.enableShutdownHooks(); + return app; +} + +if (require.main === module) { + void bootstrapWorker(); +} diff --git a/backend/apps/worker/src/worker.bootstrap.spec.ts b/backend/apps/worker/src/worker.bootstrap.spec.ts new file mode 100644 index 0000000..ddc7343 --- /dev/null +++ b/backend/apps/worker/src/worker.bootstrap.spec.ts @@ -0,0 +1,16 @@ +import { bootstrapWorker } from './main'; + +describe('bootstrapWorker', () => { + it('creates an application context without starting an HTTP listener', async () => { + const close = jest.fn().mockResolvedValue(undefined); + const enableShutdownHooks = jest.fn(); + const appContext = { close, enableShutdownHooks }; + const createContext = jest.fn().mockResolvedValue(appContext); + + const app = await bootstrapWorker(createContext as never); + + expect(createContext).toHaveBeenCalledTimes(1); + expect(enableShutdownHooks).toHaveBeenCalledTimes(1); + expect(app).toBe(appContext); + }); +}); diff --git a/backend/apps/worker/src/worker.module.ts b/backend/apps/worker/src/worker.module.ts new file mode 100644 index 0000000..a11e106 --- /dev/null +++ b/backend/apps/worker/src/worker.module.ts @@ -0,0 +1,4 @@ +import { Module } from '@nestjs/common'; + +@Module({}) +export class WorkerModule {} diff --git a/backend/apps/worker/tsconfig.app.json b/backend/apps/worker/tsconfig.app.json new file mode 100644 index 0000000..409991c --- /dev/null +++ b/backend/apps/worker/tsconfig.app.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": false, + "outDir": "../../dist/apps/worker" + }, + "exclude": ["node_modules", "dist", "test", "**/*.spec.ts"] +} diff --git a/backend/nest-cli.json b/backend/nest-cli.json index f9aa683..6666174 100644 --- a/backend/nest-cli.json +++ b/backend/nest-cli.json @@ -1,8 +1,30 @@ { "$schema": "https://json.schemastore.org/nest-cli", "collection": "@nestjs/schematics", - "sourceRoot": "src", + "monorepo": true, + "root": "apps/api", + "sourceRoot": "apps/api/src", "compilerOptions": { "deleteOutDir": true + }, + "projects": { + "api": { + "type": "application", + "root": "apps/api", + "entryFile": "main", + "sourceRoot": "apps/api/src", + "compilerOptions": { + "tsConfigPath": "apps/api/tsconfig.app.json" + } + }, + "worker": { + "type": "application", + "root": "apps/worker", + "entryFile": "main", + "sourceRoot": "apps/worker/src", + "compilerOptions": { + "tsConfigPath": "apps/worker/tsconfig.app.json" + } + } } } diff --git a/backend/package.json b/backend/package.json index 4524177..e6d1b2f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -6,18 +6,21 @@ "private": true, "license": "UNLICENSED", "scripts": { - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", + "build": "pnpm run build:api && pnpm run build:worker", + "build:api": "nest build api", + "build:worker": "nest build worker", + "format": "prettier --write \"apps/**/*.ts\" \"libs/**/*.ts\"", + "start": "nest start api", + "start:dev": "nest start api --watch", + "start:debug": "nest start api --debug --watch", + "start:api": "node dist/apps/api/main.js", + "start:worker": "node dist/apps/worker/main.js", + "lint": "eslint \"{apps,libs}/**/*.ts\" --fix", + "test": "jest --runInBand", "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json" + "test:e2e": "jest --config ./apps/api/test/jest-e2e.json" }, "dependencies": { "@nestjs/common": "^11.0.1", @@ -57,15 +60,21 @@ "json", "ts" ], - "rootDir": "src", + "rootDir": ".", "testRegex": ".*\\.spec\\.ts$", + "testPathIgnorePatterns": ["/node_modules/", "/dist/"], "transform": { "^.+\\.(t|j)s$": "ts-jest" }, + "moduleNameMapper": { + "^@travel/configuration$": "/libs/configuration/src", + "^@travel/infrastructure$": "/libs/infrastructure/src" + }, "collectCoverageFrom": [ - "**/*.(t|j)s" + "apps/**/*.(t|j)s", + "libs/**/*.(t|j)s" ], - "coverageDirectory": "../coverage", + "coverageDirectory": "coverage", "testEnvironment": "node" } } diff --git a/backend/src/main.ts b/backend/src/main.ts deleted file mode 100644 index f76bc8d..0000000 --- a/backend/src/main.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; - -async function bootstrap() { - const app = await NestFactory.create(AppModule); - await app.listen(process.env.PORT ?? 3000); -} -bootstrap(); diff --git a/backend/tsconfig.build.json b/backend/tsconfig.build.json deleted file mode 100644 index 64f86c6..0000000 --- a/backend/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/backend/tsconfig.json b/backend/tsconfig.json index aba29b0..dac7ed8 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -20,6 +20,10 @@ "forceConsistentCasingInFileNames": true, "noImplicitAny": false, "strictBindCallApply": false, - "noFallthroughCasesInSwitch": false + "noFallthroughCasesInSwitch": false, + "paths": { + "@travel/configuration": ["libs/configuration/src"], + "@travel/infrastructure": ["libs/infrastructure/src"] + } } }