diff --git a/README.md b/README.md index f292af3..505b1ad 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Start the API: ```bash npm run start:dev ``` -Verify running via the [healthcheck](http://localhost:3000/common-cents/healthcheck) endpoint. +To verify the API is running access the interactive [documentation](http://localhost:3000/common-cents/docs). Default port (`3000`) can be changed by setting the `PORT=` environment variable. diff --git a/package-lock.json b/package-lock.json index 129eef6..0dbd7e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,12 +14,13 @@ "@nestjs/core": "^11.0.1", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^11.0.1", + "@nestjs/swagger": "^11.2.6", "@nestjs/typeorm": "^11.0.0", "pg": "^8.18.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", - "sqlite3": "^5.1.7", - "typeorm": "^0.3.28" + "sqlite3": "^5.0.2", + "typeorm": "^0.3.6" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -2071,6 +2072,12 @@ "node": ">=8" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -2526,6 +2533,39 @@ "tslib": "^2.1.0" } }, + "node_modules/@nestjs/swagger": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.6.tgz", + "integrity": "sha512-oiXOxMQqDFyv1AKAqFzSo6JPvMEs4uA36Eyz/s2aloZLxUjcLfUMELSLSNQunr61xCPTpwEOShfmO7NIufKXdA==", + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.16.0", + "@nestjs/mapped-types": "2.1.0", + "js-yaml": "4.1.1", + "lodash": "4.17.23", + "path-to-regexp": "8.3.0", + "swagger-ui-dist": "5.31.0" + }, + "peerDependencies": { + "@fastify/static": "^8.0.0 || ^9.0.0", + "@nestjs/common": "^11.0.1", + "@nestjs/core": "^11.0.1", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { "version": "11.1.13", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.13.tgz", @@ -2668,6 +2708,13 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, "node_modules/@sinclair/typebox": { "version": "0.34.48", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", @@ -4071,7 +4118,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/array-timsort": { @@ -7777,7 +7823,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -9475,9 +9520,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -10433,6 +10478,15 @@ "node": ">=8" } }, + "node_modules/swagger-ui-dist": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.31.0.tgz", + "integrity": "sha512-zSUTIck02fSga6rc0RZP3b7J7wgHXwLea8ZjgLA3Vgnb8QeOl3Wou2/j5QkzSGeoz6HusP/coYuJl33aQxQZpg==", + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 448c9c7..2a178a2 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,13 @@ "@nestjs/core": "^11.0.1", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^11.0.1", + "@nestjs/swagger": "^11.2.6", "@nestjs/typeorm": "^11.0.0", "pg": "^8.18.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", - "sqlite3": "^5.1.7", - "typeorm": "^0.3.28" + "sqlite3": "^5.0.2", + "typeorm": "^0.3.6" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", diff --git a/src/app.module.ts b/src/app.module.ts index 20cde09..591367f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { HealthcheckController } from './healthcheck.controller'; import { TypeOrmModule } from '@nestjs/typeorm'; import { MerchantsModule } from './merchants/merchants.module'; import { TagsModule } from './tags/tags.module'; @@ -40,6 +40,6 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; SubCategoriesModule, ExpensesModule ], - controllers: [AppController] + controllers: [HealthcheckController] }) export class AppModule { } diff --git a/src/app.controller.ts b/src/healthcheck.controller.ts similarity index 79% rename from src/app.controller.ts rename to src/healthcheck.controller.ts index 0f03a45..bca0907 100644 --- a/src/app.controller.ts +++ b/src/healthcheck.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get } from '@nestjs/common'; @Controller() -export class AppController { +export class HealthcheckController { @Get('healthcheck') health() { return { status: 'healthy' }; diff --git a/src/main.ts b/src/main.ts index fcba9d5..2843802 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,20 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { NestExpressApplication } from '@nestjs/platform-express'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; async function bootstrap(): Promise { const app = await NestFactory.create(AppModule); - app.enableCors(); - app.setGlobalPrefix(process.env.PREFIX ?? 'common-cents'); + const appPrefix = process.env.PREFIX ?? 'common-cents'; + const config = new DocumentBuilder() + .setTitle('Common Cents API') + .setDescription('Documentation for Common Cents API') + .setVersion('1.0') + .build(); + const documentFactory = () => SwaggerModule.createDocument(app, config); + SwaggerModule.setup(appPrefix + '/docs', app, documentFactory); + app.enableCors(); // TODO: Research if this is worth worrying about + app.setGlobalPrefix(appPrefix); await app.listen(process.env.PORT ?? 3000); } void bootstrap(); diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts index 36852c5..593635a 100644 --- a/test/app.e2e-spec.ts +++ b/test/app.e2e-spec.ts @@ -4,7 +4,7 @@ import request from 'supertest'; import { App } from 'supertest/types'; import { AppModule } from './../src/app.module'; -describe('AppController (e2e)', () => { +describe('HealthcheckController (e2e)', () => { let app: INestApplication; beforeEach(async () => {