Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,12 @@ Temporary Items
dist
.webpack
.serverless/**/*.zip

# Local SQLite databases (runtime + e2e)
data.sqlite
data.sqlite-journal
test/e2e-*.sqlite
test/e2e-*.sqlite-journal

# Personal assignment brief (not part of the source)
NestJS Feature Enhancement Assignment.md
277 changes: 208 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,237 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
</p>

[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest

<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->

## Description

[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.

## Project setup
# NestJS Events Feature — Feature Enhancement Assignment

```bash
$ npm install
```
This repository is a fork of the [nestjs/typescript-starter](https://github.com/nestjs/typescript-starter) that adds a full **Events** feature (per the assignment brief).

It demonstrates a small but realistic NestJS feature: two TypeORM entities (`Event`, `User`), a Many-to-Many relationship, DTO validation, unit tests with mocked repositories, and a true end-to-end test that boots a real SQLite database.

---

## What's in the box

| Layer | File(s) |
| --- | --- |
| App bootstrap | [src/main.ts](src/main.ts), [src/app.module.ts](src/app.module.ts) |
| Events feature | [src/events/event.entity.ts](src/events/event.entity.ts), [src/events/event-status.enum.ts](src/events/event-status.enum.ts), [src/events/dto/create-event.dto.ts](src/events/dto/create-event.dto.ts), [src/events/events.service.ts](src/events/events.service.ts), [src/events/events.controller.ts](src/events/events.controller.ts), [src/events/events.module.ts](src/events/events.module.ts) |
| Users feature | [src/users/user.entity.ts](src/users/user.entity.ts), [src/users/dto/create-user.dto.ts](src/users/dto/create-user.dto.ts), [src/users/users.service.ts](src/users/users.service.ts), [src/users/users.controller.ts](src/users/users.controller.ts), [src/users/users.module.ts](src/users/users.module.ts) |
| Unit tests (mocked) | [src/events/events.service.spec.ts](src/events/events.service.spec.ts), [src/events/events.controller.spec.ts](src/events/events.controller.spec.ts), [src/users/users.controller.spec.ts](src/users/users.controller.spec.ts) |
| E2E tests (real DB) | [test/events.e2e-spec.ts](test/events.e2e-spec.ts) |

---

## REST API

| Method | Path | Body / Params | Description |
| --- | --- | --- | --- |
| `POST` | `/users` | `{ "name": "Ada" }` | Create a user. |
| `GET` | `/users` | – | List all users. |
| `GET` | `/users/:id` | – | Fetch a user by id. |
| `POST` | `/users/:id/merge-events` | – | **MergeAll** for that user (UsersController). |
| `POST` | `/events` | [CreateEventDto](src/events/dto/create-event.dto.ts) | Create an event. |
| `GET` | `/events` | – | List all events. |
| `GET` | `/events?userId=:id` | – | List only events that user is invited to. |
| `GET` | `/events/:id` | – | Fetch an event by id. |
| `DELETE` | `/events/:id` | – | Delete an event and update invitees' `events` lists. |
| `POST` | `/events/merge/:userId` | – | **MergeAll** for that user (EventsController). |

> **MergeAll is exposed on both routers** — `POST /users/:id/merge-events` and
> `POST /events/merge/:userId` both call the same service method.

---

## Data model

**`Event`** — `id` (UUID), `title` (string, required), `description` (string, optional),
`status` (`TODO` \| `IN_PROGRESS` \| `COMPLETED`), `startTime`, `endTime`,
`createdAt`, `updatedAt`, `invitees: User[]`.

> **Note on `status` column type:** stored as `varchar(32)` rather than a native
> SQL ENUM so the same schema runs on SQLite (no native ENUM), MySQL, and
> PostgreSQL without driver changes. Values are still fully constrained at the
> application layer via class-validator's `@IsEnum` on `CreateEventDto`.

**`User`** — `id` (UUID), `name`, `events: string[]` (event IDs, per the assignment
spec), `createdAt`, `updatedAt`, and an inverse `invitedEvents: Event[]` relation.
The service keeps `events` in sync with the join table so that **MergeAll** can
quickly find the events to merge for a given user.

## Compile and run the project
---

## MergeAll

`POST /events/merge/:userId` (or `POST /users/:id/merge-events`) collapses every
overlapping event the user is invited to into a single superset event.

Per the assignment FAQ:

- **Mutates the database** — deletes the originals, inserts a merged row, and
updates each invitee's `events` list. It is not just a response.
- **Scalars**: `title` and `description` are appended with `" | "`.
- **Status heuristic**: `IN_PROGRESS` > `COMPLETED` > `TODO` (most-active state wins).
- **Invitees** of the merged event are the union of all invitees from the original
events.

Example: `E1 = 10:00–11:00 (TODO)` and `E2 = 10:45–12:00 (IN_PROGRESS)` collapse
to `E_merged = 10:00–12:00 (IN_PROGRESS)` titled `"Standup | 1:1"`.

---

## How to run

```bash
# development
$ npm run start
# 1. Install dependencies
npm install

# watch mode
$ npm run start:dev
# 2. Start the API (SQLite file auto-created at ./data.sqlite)
npm run start:dev

# production mode
$ npm run start:prod
# Server listens on http://localhost:3000
# Override the DB path: DB_PATH=/tmp/myapp.sqlite npm run start:dev
# Override the port: PORT=4000 npm run start:dev
```

## Run tests
### Environment variables

| Variable | Default | Description |
| --- | --- | --- |
| `DB_PATH` | `data.sqlite` | Path to the SQLite database file |
| `PORT` | `3000` | HTTP port the server listens on |

> **Swapping to PostgreSQL or MySQL:** edit `src/app.module.ts` — change
> `type: 'better-sqlite3'` to `type: 'postgres'` (or `'mysql'`) and supply
> the usual connection keys. No service or entity changes are required.

---

## How to test

Per assignment FAQ #1 we do **both** — mocked unit tests and a real local-DB e2e test.

```bash
# unit tests
$ npm run test
# Unit tests (mocked TypeORM repositories — no database needed)
npm test

# e2e tests
$ npm run test:e2e
# End-to-end tests (real SQLite DB, full Nest app + supertest HTTP calls)
npm run test:e2e

# test coverage
$ npm run test:cov
# Coverage report
npm run test:cov
```

## Deployment
Expected results:

When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information.
| Command | Suites | Tests |
| --- | --- | --- |
| `npm test` | 3 | 16 |
| `npm run test:e2e` | 1 | 4 |

If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps:
---

## Try it with curl

```bash
$ npm install -g @nestjs/mau
$ mau deploy
# Create two users
curl -s -X POST http://localhost:3000/users -H 'Content-Type: application/json' \
-d '{"name":"Ada"}' | tee /tmp/ada.json
curl -s -X POST http://localhost:3000/users -H 'Content-Type: application/json' \
-d '{"name":"Bea"}' | tee /tmp/bea.json

ADA_ID=$(jq -r .id /tmp/ada.json)
BEA_ID=$(jq -r .id /tmp/bea.json)

# Create two overlapping events
curl -s -X POST http://localhost:3000/events -H 'Content-Type: application/json' -d "{
\"title\": \"Standup\",
\"status\": \"TODO\",
\"startTime\": \"2026-01-01T10:00:00Z\",
\"endTime\": \"2026-01-01T11:00:00Z\",
\"invitees\": [\"$ADA_ID\"]
}"

curl -s -X POST http://localhost:3000/events -H 'Content-Type: application/json' -d "{
\"title\": \"1:1\",
\"status\": \"IN_PROGRESS\",
\"startTime\": \"2026-01-01T10:45:00Z\",
\"endTime\": \"2026-01-01T12:00:00Z\",
\"invitees\": [\"$ADA_ID\", \"$BEA_ID\"]
}"

# List only Ada's events
curl -s "http://localhost:3000/events?userId=$ADA_ID" | jq .

# Merge via EventsController route
curl -s -X POST "http://localhost:3000/events/merge/$ADA_ID" | jq .

# — or via UsersController route —
curl -s -X POST "http://localhost:3000/users/$ADA_ID/merge-events" | jq .

# Fetch an event by id
curl -s "http://localhost:3000/events/<event-id>" | jq .

# Delete an event
curl -s -X DELETE "http://localhost:3000/events/<event-id>" -o /dev/null -w "%{http_code}\n"
```

With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure.
---

## Resources
## Stack & decisions

Check out a few resources that may come in handy when working with NestJS:
- **NestJS 11** with `@nestjs/typeorm`, `class-validator`, `class-transformer`.
- **better-sqlite3** as the database driver. Zero setup, real SQL, real FK constraints.
Swap to Postgres/MySQL by editing `src/app.module.ts` — no service-level changes required.
- **TypeORM `synchronize: true`** is enabled for the demo. For production, use TypeORM migrations.
- **DTO validation** is enabled globally via `ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true })`.
- **Status column** is `varchar(32)` (not native ENUM) so the same schema runs on SQLite,
MySQL, and PostgreSQL without migration changes. Values are still constrained at the app layer.
- **Tests** follow the [NestJS testing guide](https://docs.nestjs.com/fundamentals/testing):
unit tests use `Test.createTestingModule` with mock repositories; e2e tests use
`Test.createTestingModule` against a real SQLite file (deleted after each run).

- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks.
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
---

## Support
## Project structure

Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
```
.
├── README.md
├── eslint.config.mjs
├── nest-cli.json
├── package.json
├── tsconfig.json
├── tsconfig.build.json
└── src
├── main.ts
├── app.module.ts
├── events
│ ├── event-status.enum.ts
│ ├── event.entity.ts
│ ├── events.controller.ts ← POST/GET/DELETE + POST merge/:userId
│ ├── events.controller.spec.ts ← 6 unit tests
│ ├── events.module.ts
│ ├── events.service.ts ← create / findById / findAll / findAllForUser / deleteById / mergeAllForUser
│ ├── events.service.spec.ts ← 7 unit tests
│ └── dto
│ └── create-event.dto.ts
└── users
├── user.entity.ts
├── users.controller.ts ← POST /users/:id/merge-events
├── users.controller.spec.ts ← 3 unit tests
├── users.module.ts
├── users.service.ts
└── dto
└── create-user.dto.ts
└── test
├── events.e2e-spec.ts ← 4 e2e tests (real SQLite)
└── jest-e2e.json
```

## Stay in touch
---

- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## Demo video script

## License
Suggested 90-second script:

Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
1. `npm install` (skip if already installed).
2. `npm test` — show the **16 passing unit tests** across 3 suites.
3. `npm run start:dev` in a second terminal — server starts on port 3000.
4. Run the `curl` block above in order: create users → create events → list user events → merge → fetch → delete.
5. `npm run test:e2e` — show the **4 passing e2e tests**.
Loading