{"id":16762175,"url":"https://github.com/mirceaalexandru/sample-project-express","last_synced_at":"2026-04-02T03:03:42.276Z","repository":{"id":43980051,"uuid":"245992972","full_name":"mirceaalexandru/sample-project-express","owner":"mirceaalexandru","description":null,"archived":false,"fork":false,"pushed_at":"2023-01-24T01:34:53.000Z","size":1372,"stargazers_count":0,"open_issues_count":12,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-16T10:20:02.444Z","etag":null,"topics":["docker","docker-compose","expressjs","kubernetes","mongodb","nodejs","swagger"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/mirceaalexandru.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}},"created_at":"2020-03-09T09:25:41.000Z","updated_at":"2020-03-09T09:28:53.000Z","dependencies_parsed_at":"2023-01-31T15:01:11.438Z","dependency_job_id":null,"html_url":"https://github.com/mirceaalexandru/sample-project-express","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mirceaalexandru/sample-project-express","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirceaalexandru%2Fsample-project-express","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirceaalexandru%2Fsample-project-express/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirceaalexandru%2Fsample-project-express/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirceaalexandru%2Fsample-project-express/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mirceaalexandru","download_url":"https://codeload.github.com/mirceaalexandru/sample-project-express/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirceaalexandru%2Fsample-project-express/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31294828,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T01:43:37.129Z","status":"online","status_checked_at":"2026-04-02T02:00:08.535Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["docker","docker-compose","expressjs","kubernetes","mongodb","nodejs","swagger"],"created_at":"2024-10-13T04:44:12.823Z","updated_at":"2026-04-02T03:03:42.127Z","avatar_url":"https://github.com/mirceaalexandru.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# A challenge project\n\n* [A challenge project](#a-challenge-project)\n  * [Project description](#project-description)\n     * [Campaign](#campaign)\n     * [Ad Group](#ad-group)\n     * [Ad](#ad)\n     * [Ad Stats](#ad-stats)\n     * [Data files](#data-files)\n  * [Starting the service](#starting-the-service)\n  * [Features](#features)\n     * [Configuration](#configuration)\n     * [Swagger documentation](#swagger-documentation)\n     * [Endpoint validation](#endpoint-validation)\n     * [Linting](#linting)\n     * [Database](#database)\n     * [Docker](#docker)\n     * [Docker Compose](#docker-compose)\n     * [Linting](#linting-1)\n     * [Testing](#testing)\n  * [Used libraries](#used-libraries)\n  * [npm tasks](#npm-tasks)\n  * [DB Collections](#db-collections)\n  * [Further comments about API](#further-comments-about-api)\n  * [Postman](#postman)\n  * [Deploying service using Kubernetes](#deploying-service-using-kubernetes)\n\n## Project description\n\n### Campaign\nDescribes advertising campaign. Campaign is composed of one or multiple adgroups and each adgroup has a set of ads assigned to it\n\n### Ad Group\nDescribes ad group of advertising campaign\n\n### Ad\nDescribes ad of particular ad group\n\n### Ad Stats\nDescribes statistics of particular ad for time t\n\n### Data files\nData files contain a collections of advertising campaigns, adgroups, ads and ads statistics over time. \nEach advertising campaign has following structure: Campaign 1-\u003e N Ad groups 1 -\u003e N Ads 1 -\u003e N stats\n\n## Starting the service\n\n * Clone project locally\n \n`git clone git@github.com:mirceaalexandru/sample-project-express.git`\n\n * Prepare configuration: \n   * Copy `sample.env` as `.env`. Change any environment variable to match your environment configuration.\n   * Copy `sample-env-variables.env` as `env-variables.env`. Change any environment variable to match your environment configuration. This file contains the configuration required by docker-compose.\n\n You can start you service in one of these ways:\n * Start your service using an external Mongo DB. Just need to provide correct DB URI in `.env` (or as environment variable).\n * Start your service using the MongoDB started with docker-compose. \n You need to start docker-compose (`docker-compose up --build --scale service=0`) and then you can start your service on local host.\n * Start your service and MongoDB using docker-compose. You can start docker compose (`docker-compose up --build`). This will start multiple MongoDB and then start the service. \n\n`Note` The docker-compose method is another way of starting this service in development mode, using nodemon which will restart the application\n        when code changes are made.\n\n## Features\n\n### Configuration\n\n * Configuration is loaded from environment variables.\n * Support for loading from an `.env` file is provided. However the `dotenv` is added as dev dependency making sure that `.env` is not loaded in production as this will be a bad practice.\n * Before service is started a strict schema validation is applied on configuration object. This is required to make sure the application is started with a valid configuration, at least from structure point of view.\n * There are no configuration defaults. This will enforce creating proper environment variables for all configuration parameters.\n \n### Swagger documentation\n\n * Swagger documentation for implemented API is exposed automatically on `/api-docs/` endpoint.\n * This documentation is created automatically using the annotations in the API code.\n * This documentation is exposed automatically only for `development` environments, in `staging` or `production` it will not be available.\n * Swagger documentation is available here: http://test.alexandrumircea.ro:30000/api-docs \n \n### Endpoint validation\n\n * All endpoints have a strict validation implementing using JOI library.\n * This implementation is described in the Swagger documentation.\n * Validation errors details are not exposed in the HTTP response.\n \n### Linting\n\nThe project is using eslint to make sure the code style is consistent.\n \n### Database\n\nThe docker-compose provided in this project is creating the required MongoDB node.\n\n### Docker\n\nDocker files are provided for:\n * development environment - `Dockerfile.dev`. This is using nodemon for automatically reloading the service when changes are made.\n * production environment - `Dockerfile`\n\n### Docker Compose\n\nA docker compose is provided. This docker compose contains our service and also a MongoDB. The docker-compose will:\n * start MongoDB\n * start service\n \n### Linting\n\nPlease run `npm run lint` for linting.\n\n### Testing\n\nPlease run `npm run test:integrations` for running integration tests.\n\n## Used libraries\n\nSome of the libraries used in this project:\n \n - `@hapi/joi` - a very good JSON validation library. It is used to validate the configuration before starting the service and to validate requests.\n - `swagger-jsdoc` - generate Swagger JSON based on annotations in code\n - `swagger-ui-express` - generate and expose UI fo Swagger documentation based on output from `swagger-jsdoc`\n \n## npm tasks\n \nThe following npm tasks are available to the developer:\n \n  - `start` - start the service.\n  - `start:dev` - start service in development mode, using nodemon library. It reloads the service when code changes are detected.\n  - `db:clean` - clean database by deleting all documents in known collection (see next section for details). *Cannot be executed in any other environment that development*\n  - `db:createIndexes` - create indexes fo MongoDB collections. Having the indexes stored in a file is good approach for maintaining database structure. This task can also be used by any pre-task when service is deployed/started.\n  - `db:populate` - Use the [data files](#data-files) populate the database with some initial testing data. *Cannot be executed in any other environment that development*\n  - `db:prepare` - is running `db:clean`, `db:createIndexes` and `db:populate` tasks in this order.\n\n`Note #1` One initial requirement was to populate database when service is started. Because these scripts are destructive for database content\nthe decisions was to implement them as separate npm tasks. In this way the user can decide when it is OK to run these tasks.\n\n`Note #2` Some of the above npm tasks are very dangerous to be applied in other environments than development. \nBecause of this these tasks have at the start a test for environment and exit automatically if this condition is not matched. \nIt is very important to be careful when running these tasks as these are very destructive for DB content. \n  \n`Note #3` Each document in data files has an id that has 36 chars length. This is different from _id that is automatically created\nfor each MongoDB document. The assumption that this id should be used by all endpoints. \nBecause of this all endpoints  are actually using (and returning in response) this id, not returning the _id internal mongo document id. \n\n`Note #4` As described above there is provided a script for adding [indexes](./scripts/createIndexes.js) fo known collections. \nSome of these indexes are also adding unique constraints for `id` in the collections, making sure no duplicates can be inserted.\n\n## DB Collections\n\nA file containing information about all database collection can be found [here](./scripts/dbHelper.js). \nThis list is used by above scripts when preparing database with testing data.\n\n## Further comments about API\n\n- For one endpoint (`/v1/ads/:adId/stats/startDate/:startDate/endDate/:endDate`) it was also implemented pagination and sorting.\n In order to implement pagination \u0026 sorting it was required to compute cost per click as part of the retrieve data operation.\n Fo this endpoint it was used [MongoDB Aggregation Pipelines](./lib/models/stats.js).\n Also, the same aggregation pipelines computes the total number of items matching provided criteria as this is required for a proper implementation for pagination on client side.\n- All endpoints have versions (`/v1` in this case). This helps manage changes in API definitions with backward compatibility.\n- Health endpoint (`/v1/health`) is provided for liveness probe. \n- Health endpoint (`/v1/health`) can be used also for readiness probe as service is exposing the endpoints after DB connection is created.\n\n## Postman\n\nPostman collection of requests definitions and local environment setup variables can be found in [./docs/postman](./docs/postman) folder.\n\n## Deploying service using Kubernetes\n\n- Please check [here](./docs/eks.md) for details how this service was deployed using AWS ECR/EKS.\n- Configuration files used for deploying the service can be found in [./docs/k8s](./docs/k8s) folder.\n- Service is exposed at: http://test.alexandrumircea.ro:30000\n- Swagger documentation is available here: http://test.alexandrumircea.ro:30000/api-docs","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmirceaalexandru%2Fsample-project-express","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmirceaalexandru%2Fsample-project-express","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmirceaalexandru%2Fsample-project-express/lists"}