This project is a shortener of URL I have done in a interview for a job. Since the project was quite cool and interesting, I pushed it further and try to make a project the most complete as possible. The project has typescript, unit test, smoke test, e2e test, tyescript coverage, dockerization, documentation, sonar analyzis and a lot of other things...
For this project, I have used Nx
to create the structure, React
for the frontend, Express
for the backend and cypress
, jest
for the testing. I have also used sonar
, docker
, docker-compose
. For the database, I have used mongodb
and mongoose
as an ORM.
I explain with all the details how I build the project and my way of working.
Create a library at the root of the project using NX command:
$ nx generate @nrwl/js:library types
A path pointing to the lib will be added in tsconfig.base.json
. Since the other tsconfig are extending the tsconfig base, the path will be available in each project.
In order to be able to use swagger, we install the dependencies:
$ npm i swagger-jsdoc swagger-ui-express --save-dev
We then, set up the extensions in server.ts
and put the options in the folder docs
:
const specs = swaggerJsdoc(options);
this.#app.use(
`/${ENVIRONMENT.API.VERSION}/docs`,
swaggerUi.serve,
swaggerUi.setup(specs)
);
And finally, we put the documentation of the api as close as possible as the api endpoints in routes
.
The documentation can be consulted at: http://localhost:3333/v1/docs
In order to configure the test on the shortener-api, you need to add a target in the project.json
of the shortener-api project:
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/dist/apps/coverage-test-backend"],
"options": {
"jestConfig": "apps/shortener-api/jest.config.ts",
"codeCoverage": true,
"collectCoverageFrom": ["src/**/*.{ts,tsx}"],
"coverageReporters": [
"clover",
"json",
"lcov",
"text-summary"
],
"passWithNoTests": true
}
}
Once done, we also need to modify in ``jest.config.ts, the
coverageDirectory` in order to move the lcov result in the dist directory in order to be able to access through the express server:
{
"coverageDirectory": "../../dist/apps/coverage-test-backend",
}
Finally, in the express server, we add the route to serve our index as a static resource:
this.#app.use(
`/${ENVIRONMENT.API.VERSION}/test/backend`,
express.static(__dirname + '/../coverage-test-backend/lcov-report')
);
In order to generate the result, run the following command:
$ nx test shortener-api
# OR
$ npm run test-unit-backend
You can then access the result through the url: http://localhost:3333/v1/test/backend/
PS: Since we are using the "test" command, the environment variable is "test". The environment file need to be set in consequence.
PS2: If some test such as import let the server open, it's possible to force the exit and to make the test pass with this command
nx test shortener-api --detectOpenHandles --forceExit
For the react app, it's easier. Jest is already setup. However, we need to modify a bit the config in vite.config.mjs
.
In the section test, we can add the following:
test: {
coverage: {
reportsDirectory: '../../dist/apps/coverage-test-frontend',
enabled: true,
provider: 'v8',
},
},
Once done, we can add the route to serve our index as a static resource:
this.#app.use(
`/${ENVIRONMENT.API.VERSION}/test/backend`,
express.static(__dirname + '/../coverage-test-backend/lcov-report')
);
In order to generate the result, run the following command:
$ nx test shortener
# OR
$ npm run test-unit-frotend
You can then access the result through the url: http://localhost:3333/v1/test/frontend/
For checking my level of coverage of the typing, I am using the package typescript-coverage-report
.
The coverage has been push till 100%. Everything has been typed properly.
$ npm i typescript-coverage-report --save-dev
To create the coverage page, simply run the following command:
$ npm run ts-coverage-backend
If you want more information, the report can be watch through the api once the previous command has been run at least once:
$ nx server shortener-api
You can then look the report at this URL: http://localhost:3333/v1/typescript/backend/
In order to make absolute path possible, you need to config in the file tsconfig.json
of each project the following properties:
"baseUrl": ".",
"paths": {
"@controllers/*": [
"./src/controllers/*"
],
}
For the frontend, it's exactly the same as for the backend with one difference.
The plugin vite-tsconfig-paths
need to be install and configure in vite:
$ npm i -D vite-tsconfig-paths
Then in vite.config.mjs
, just add the plugin:
plugins: [
tsconfigPaths(),
],
Then, it's possible to add the path using the following typo:
import Home from '@pages/Home/Home';
.
├── .nx # Cache NX
├── apps # Contains all the apps of the project
└── mongodb # The script for setting up the mongodb
└── shortener # The react frontend
└── locales # The json file for i18n (multi language)
└── src # The source file
└── app # The app entrance file
└── assets # The asset of the project
└── components # The reusable component
└── interfaces # The interface for typescript
└── pages # The page for the project
└── services # The services for calling the backend
└── styles # The style of the frontend
└── shortener-api # The express backend
└── src # The source file
└── controllers # The controllers of the api
└── daos # The data object validating the parameter of the entering request
└── docs # The options for the swagger
└── interfaces # The interface for typescript
└── libs # The libs containing constant, logger and utils
└── models # The model to save in the database
└── repositories # The repository allowing operations to the db
└── routes # The routes allowed by the api
└── services # The services of the backend
└── shortener-api-e2e # The tests for the express backend (jest)
└── shortener-e2e # The tests for the react frontend (cypress)
├── env # Environment file
├── scenarios # The scenario for the load testing
├── seeding # Seed the database for the test
├── nginx # Contains the configuration for reverse proxy
├── node_modules # Contains all the dependencies of the two projects (monorepository)
├── .dockerignore # The file to ignore for docker
├── .eslintrc # The configuration for the linter
├── .prettierrc # The configuration for the prettier
├── docker-compose.yml # File for managing the creation of container for docker
├── Dockerfile # Creation of container for our apps
├── nx.json # Setting of nx
├── package.json # Npm configuration file
├── package-lock.json # Npm locker version for module
├── tsconfig.base.json # Shared configuration for typescript
├── vitest.workspace.json # Shareed configuration if multiple frontend
├── LICENSE # Description of the license
└── README.md # Presentation for the project
The project where the project will be running need to have the following too:
- Node 20 or Volta
- Npm 10 or higher
- Docker
- Docker Compose
- nx
$ npm add --global nx@latest
In dev mode, the user need to have at least mongoDB setup on his machine Before starting the development mode without docker
$ npm install
In the terminal, type the following command:
$ npm run dev
Since the production mode compile the project at every restart and since the project is small. Without any installation needed, the user can the project using:
$ npm run up
In order to be the closest of possible of the production, modify the /etc/hosts
with the following:
127.0.0.1 shortener.net mongodb server1
In the terminal, type the following command:
$ npm run up
- The project does not ensure the unicity of the short url. It means that if the short url unfortunaly has been generated with one already existing, the url will be replace through an update.
Ubuntu Version: Ubuntu 20.04.1 Node Version: v20.11.1 Npm Version: v10.5.0
The version are manage with Volta.
# Get the latest version of ubuntu
$ lsb_release -a
# Get the version of node
$ node -v
# Get the version of npm
$ npm -v
If your computer has not enough memory, in some case, the deamon of NX can fail to start or restart. In such case,
you can run the following command to clean up the project:
$ nx reset