{"id":29132167,"url":"https://github.com/alejandrehl/tcit-posts-backend","last_synced_at":"2026-04-11T18:02:59.217Z","repository":{"id":301615336,"uuid":"1009796474","full_name":"Alejandrehl/tcit-posts-backend","owner":"Alejandrehl","description":"RESTful API for blog post management built with NestJS, TypeScript, and PostgreSQL. Includes modular architecture, robust validation, OpenAPI (Swagger) documentation, and Docker support.","archived":false,"fork":false,"pushed_at":"2025-06-27T22:12:16.000Z","size":226,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-27T23:23:09.114Z","etag":null,"topics":["api-rest","backend","blog","clean-code","docker","modular-architecture","nestjs","nodejs","openapi","postgresql","posts","swagger","typescript","validation"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Alejandrehl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-27T18:17:25.000Z","updated_at":"2025-06-27T22:12:19.000Z","dependencies_parsed_at":"2025-06-27T23:23:09.657Z","dependency_job_id":null,"html_url":"https://github.com/Alejandrehl/tcit-posts-backend","commit_stats":null,"previous_names":["alejandrehl/tcit_posts_backend","alejandrehl/tcit-posts-backend"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Alejandrehl/tcit-posts-backend","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alejandrehl%2Ftcit-posts-backend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alejandrehl%2Ftcit-posts-backend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alejandrehl%2Ftcit-posts-backend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alejandrehl%2Ftcit-posts-backend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Alejandrehl","download_url":"https://codeload.github.com/Alejandrehl/tcit-posts-backend/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alejandrehl%2Ftcit-posts-backend/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31689762,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-11T13:07:20.380Z","status":"ssl_error","status_checked_at":"2026-04-11T13:06:47.903Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-rest","backend","blog","clean-code","docker","modular-architecture","nestjs","nodejs","openapi","postgresql","posts","swagger","typescript","validation"],"created_at":"2025-06-30T06:16:25.858Z","updated_at":"2026-04-11T18:02:59.201Z","avatar_url":"https://github.com/Alejandrehl.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TCIT Posts Backend\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/Alejandrehl/tcit-posts-backend/ci.yml?branch=main\u0026label=build)](https://github.com/Alejandrehl/tcit-posts-backend/actions)\n[![Coverage](https://img.shields.io/badge/coverage-99%25-brightgreen)](https://github.com/Alejandrehl/tcit-posts-backend/actions)\n[![Docker Image Size](https://img.shields.io/docker/image-size/library/node/20-slim?label=docker%20image)](https://hub.docker.com/_/node)\n\nTechnical Challenge for TCIT - Senior Full Stack React/Node.js Engineer\n\nA robust REST API for managing blog posts built with NestJS, TypeScript, and PostgreSQL.\n\n---\n\n## Table of Contents\n- [Features](#features)\n- [Quick Start](#quick-start)\n- [Local Development](#local-development)\n- [Health Check](#health-check)\n- [Docker Workflow](#docker-workflow)\n- [Environment Variables](#environment-variables)\n- [API Documentation](#api-documentation)\n- [Testing](#testing)\n- [Architecture](#architecture)\n- [NPM Scripts](#npm-scripts)\n- [Database Migrations](#database-migrations)\n- [CI/CD](#cicd)\n- [FAQ](#faq)\n- [License](#license)\n- [Author \u0026 Contact](#author--contact)\n\n---\n\n## Features\n- **REST API** for managing Posts (CRUD operations)\n- **Modern Stack**: Node.js 20, NestJS 11+, Fastify, TypeORM 0.3, PostgreSQL 14+\n- **Type Safety**: Strict TypeScript with comprehensive type definitions\n- **Security**: Global CORS, Helmet, rate limiting, input validation\n- **Documentation**: OpenAPI 3.1 docs with Swagger UI at `/docs`\n- **Health Check**: `/health` endpoint for monitoring and health checks\n- **Testing**: 98% test coverage with unit and e2e tests\n- **CI/CD**: GitHub Actions with automated testing\n- **Docker**: Multi-stage Dockerfile optimized for production\n- **API Versioning**: RESTful endpoints with `/v1` prefix\n- **Database**: PostgreSQL with TypeORM migrations\n\n---\n\n## Quick Start\n\n### Prerequisites\n- **Node.js** 20.x ([Download here](https://nodejs.org/en/download))\n- **npm** (comes with Node.js)\n- **PostgreSQL** 14+ ([Install for Mac](https://postgresapp.com/), [Linux](https://www.postgresql.org/download/linux/), [Windows](https://www.postgresql.org/download/windows/))\n- **Git**\n\n### Local Development Setup\n\n1. **Clone the repository**\n   ```bash\n   git clone https://github.com/Alejandrehl/tcit-posts-backend.git\n   cd tcit-posts-backend\n   ```\n\n2. **Install dependencies**\n   ```bash\n   npm install\n   ```\n\n3. **Configure environment variables**\n   Copy the example file and edit it with your local credentials:\n   ```bash\n   cp .env.example .env\n   ```\n   Edit `.env` and make sure to fill in all values, especially `DATABASE_USER` and `DATABASE_PASSWORD`.\n\n4. **Create the PostgreSQL user and database**\n   If you do not have a user or database, you can create them as follows (adjust user and password to match your .env):\n   ```bash\n   # Access the PostgreSQL console\n   psql -U postgres\n   # Inside psql:\n   CREATE USER postgres WITH PASSWORD 'your_password_here';\n   CREATE DATABASE tcit_posts OWNER postgres;\n   \\q\n   ```\n   If you use a different user, update your .env accordingly.\n\n5. **Run database migrations**\n   ```bash\n   npm run migration:run\n   ```\n\n6. **Start the development server**\n   ```bash\n   npm run start:dev\n   ```\n\n7. **Verify the installation**\n   - API: http://localhost:3000\n   - Documentation: http://localhost:3000/docs\n   - Health check: http://localhost:3000/health\n   - Posts: http://localhost:3000/v1/posts\n\n### Docker Setup (Alternative)\n\n1. **Make sure you have Docker and Docker Compose installed**\n   - [Docker Desktop](https://www.docker.com/products/docker-desktop/)\n\n2. **Start the services**\n   ```bash\n   docker-compose up -d\n   ```\n\n3. **Access the application**\n   - API: http://localhost:3000\n   - PostgreSQL: localhost:5432\n\n4. **Stop the containers**\n   ```bash\n   docker-compose down\n   ```\n\n\u003e **Note:** If port 3000 or 5432 is already in use, stop the process using it or change the port in `.env` and in `docker-compose.yml`.\n\n---\n\n## Troubleshooting (Common Issues)\n\n- **Database connection error**: Make sure PostgreSQL is running and the credentials in `.env` are correct.\n- **Port 3000 already in use**: Stop the process using it (`lsof -i :3000` and then `kill \u003cPID\u003e`), or change the port in `.env`.\n- **`psql` command not found**: Make sure PostgreSQL is installed and the binary is in your PATH.\n- **Dependency issues**: Run `npm ci` to reinstall from scratch.\n- **Tests fail due to files in dist**: Run `find dist -name 'test-setup.*' -delete` and re-run the tests.\n\n---\n\n## Local Development\n\n### Development Workflow\n\n- **Start development server**\n  ```bash\n  npm run start:dev\n  ```\n  The server will restart automatically on file changes.\n\n- **Run tests**\n  ```bash\n  npm test\n  npm run test:cov\n  npm run test:watch\n  ```\n\n- **Lint and format**\n  ```bash\n  npm run lint\n  npm run format\n  ```\n\n- **Database operations**\n  ```bash\n  npm run migration:run\n  npm run migration:generate -- src/migrations/NewMigration\n  ```\n\n### API Testing\n\n- **Swagger UI**: http://localhost:3000/docs\n- **Health Check**: http://localhost:3000/health\n- **Curl examples**:\n  ```bash\n  # Create a post\n  curl -X POST http://localhost:3000/v1/posts \\\n    -H \"Content-Type: application/json\" \\\n    -d '{\"name\": \"My First Post\", \"description\": \"Post content\"}'\n\n  # List all posts\n  curl http://localhost:3000/v1/posts\n\n  # Delete a post\n  curl -X DELETE http://localhost:3000/v1/posts/1\n\n  # Health check\n  curl http://localhost:3000/health\n  ```\n\n---\n\n## Health Check\n\n- **Endpoint**: `GET /health`\n- **Purpose**: Used for Docker and monitoring\n- **Response Example**:\n  ```json\n  {\n    \"status\": \"ok\",\n    \"timestamp\": \"2025-06-27T19:42:19.382Z\",\n    \"uptime\": 45.152403271,\n    \"database\": {\n      \"status\": \"connected\",\n      \"responseTime\": 12\n    },\n    \"memory\": {\n      \"used\": 39.31,\n      \"total\": 40.87,\n      \"percentage\": 96.18\n    }\n  }\n  ```\n- **Error Example**:\n  ```json\n  {\n    \"status\": \"error\",\n    \"timestamp\": \"2025-06-27T19:42:19.382Z\",\n    \"uptime\": 45.152403271,\n    \"database\": {\n      \"status\": \"disconnected\",\n      \"error\": \"Connection timeout\"\n    },\n    \"memory\": {\n      \"used\": 39.31,\n      \"total\": 40.87,\n      \"percentage\": 96.18\n    }\n  }\n  ```\n\n---\n\n## Environment Variables\n\n| Variable | Description | Default | Required |\n|----------|-------------|---------|----------|\n| `NODE_ENV` | Environment mode | `development` | No |\n| `PORT` | Server port | `3000` | No |\n| `DATABASE_HOST` | PostgreSQL host | `localhost` | Yes |\n| `DATABASE_PORT` | PostgreSQL port | `5432` | No |\n| `DATABASE_USER` | Database username | - | Yes |\n| `DATABASE_PASSWORD` | Database password | - | Yes |\n| `DATABASE_NAME` | Database name | `tcit_posts` | No |\n\n---\n\n## API Documentation\n\n### Base URL\n- **Local**: http://localhost:3000\n\n### Endpoints\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `GET` | `/v1/posts` | List all posts |\n| `POST` | `/v1/posts` | Create a new post |\n| `DELETE` | `/v1/posts/:id` | Delete a post by ID |\n\n### Interactive Documentation\n- **Swagger UI**: `/docs`\n- **OpenAPI JSON**: `/docs-json`\n\n### Example Requests\n\n**Create Post**\n```bash\ncurl -X POST /v1/posts \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"My Blog Post\",\n    \"description\": \"This is the content of my blog post\"\n  }'\n```\n\n**List Posts**\n```bash\ncurl GET /v1/posts\n```\n\n**Delete Post**\n```bash\ncurl -X DELETE /v1/posts/1\n```\n\n---\n\n## Testing\n\n### Test Coverage\n- **Current Coverage**: 98%\n- **Target Coverage**: 80% (configurable)\n- **Test Types**: Unit tests, Integration tests, E2E tests\n\n### Running Tests\n```bash\n# All tests\nnpm test\n\n# With coverage report\nnpm run test:cov\n\n# Watch mode\nnpm run test:watch\n\n# E2E tests only\nnpm run test:e2e\n```\n\n### Test Structure\n```\nsrc/\n├── posts/\n│   ├── __tests__/\n│   │   ├── posts.controller.spec.ts\n│   │   ├── posts.service.spec.ts\n│   │   ├── posts.repository.spec.ts\n│   │   └── posts.e2e-spec.ts\n│   └── ...\n└── ...\n```\n\n---\n\n## Architecture\n\n```\nsrc/\n├── main.ts                 # Application bootstrap\n├── app.module.ts          # Root module\n├── config/                # Configuration module\n│   ├── config.module.ts\n│   ├── typeorm.config.ts\n│   └── validation.schema.ts\n├── posts/                 # Posts feature module\n│   ├── post.entity.ts     # Database entity\n│   ├── posts.controller.ts # REST endpoints\n│   ├── posts.service.ts   # Business logic\n│   ├── posts.repository.ts # Data access\n│   ├── posts.module.ts    # Module definition\n│   ├── dto/               # Data Transfer Objects\n│   │   ├── create-post.dto.ts\n│   │   └── list-posts.dto.ts\n│   └── __tests__/         # Tests\n└── migrations/            # Database migrations\n```\n\n### Design Patterns\n- **SOLID Principles**: Clean architecture implementation\n- **Repository Pattern**: Data access abstraction\n- **DTO Pattern**: Input/output validation\n- **Dependency Injection**: NestJS IoC container\n\n---\n\n## NPM Scripts\n\n| Script | Description |\n|--------|-------------|\n| `npm start` | Start production server |\n| `npm run start:dev` | Start development server with hot reload |\n| `npm run build` | Build TypeScript to dist/ |\n| `npm test` | Run all tests |\n| `npm run test:cov` | Run tests with coverage report |\n| `npm run test:e2e` | Run end-to-end tests |\n| `npm run lint` | Lint code with ESLint |\n| `npm run format` | Format code with Prettier |\n| `npm run migration:run` | Run database migrations |\n| `npm run migration:generate` | Generate new migration |\n\n---\n\n## Database Migrations\n\n### Commands\n```bash\n# Generate migration\nnpm run migration:generate -- src/migrations/MigrationName\n\n# Run migrations\nnpm run migration:run\n\n# Revert last migration\nnpm run migration:revert\n```\n\n### Migration Files\n- Located in `/migrations/`\n- Written in TypeScript\n- Automatically run on application startup\n\n---\n\n## CI/CD\n\n### GitHub Actions\n- **Trigger**: Push to main branch or Pull Request\n- **Workflow**: `.github/workflows/ci.yml`\n- **Steps**:\n  1. Lint code\n  2. Run tests with coverage\n  3. Build application\n\n### Quality Gates\n- Code must pass linting\n- Tests must pass with 80%+ coverage\n- Build must succeed\n\n---\n\n## FAQ\n\n**Q: How do I reset the database?**\nA: For local development, drop and recreate the database. For Docker: `docker-compose down -v \u0026\u0026 docker-compose up -d`\n\n**Q: How do I add a new API endpoint?**\nA: Create the controller method, add corresponding service method, and update tests.\n\n**Q: How do I update the database schema?**\nA: Modify the entity, generate a migration: `npm run migration:generate`, then run it: `npm run migration:run`\n\n**Q: How do I access the API documentation?**\nA: Visit `/docs` endpoint when the server is running.\n\n---\n\n## License\n\nThis project is for technical evaluation purposes only.\n\n---\n\n## Author \u0026 Contact\n\n**Alejandro Exequiel Hernández Lara**\n\n- **Phone:** +56 9 4488 9280\n- **Email:** [alejandrehl@icloud.com](mailto:alejandrehl@icloud.com)\n- **LinkedIn:** [linkedin.com/in/alejandrehl](https://www.linkedin.com/in/alejandrehl/)\n- **GitHub:** [github.com/Alejandrehl](https://github.com/Alejandrehl)\n\n---","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falejandrehl%2Ftcit-posts-backend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falejandrehl%2Ftcit-posts-backend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falejandrehl%2Ftcit-posts-backend/lists"}