Notion-Clone API: Robust TypeScript Backend with Clean Architecture, DDD, and Test-Driven Development
The Notion-Clone API is a TypeScript-based backend project that serves as a clone of the popular note-taking and collaboration application, Notion. This API is designed to handle authentication and authorization while providing similar functionalities to Notion, allowing users to create, manage, and collaborate on various types of content such as notes, databases, and task lists. The project follows best practices in software development, including Clean Architecture, Domain Driven Design (DDD), and Test Driven Development (TDD), ensuring a well-structured, maintainable, and extensible codebase.
https://www.youtube.com/watch?v=XkFgv8AsUe0&t=13s
https://shreyasmanolkar.hashnode.dev/building-notion-clone-part-1-planning-the-architecture
- User Authentication: Secure user registration and login processes with hashed passwords and token-based authentication.
- Content Creation and Management: Create, update, and delete various content types, including notes, lists, media, superchared tables, task lists etc.
- Collaboration: Enable users to collaborate on shared content, providing granular access control and permissions.
- Rich Text Support: Allow users to create and edit content with rich text formatting options.
- Security: Ensure data security and privacy with robust authentication and authorization mechanisms.
- upload images for profile picture, cover photo and in text editor.
- robust schema validation for validating input data.
- 99% Test Coverage.
- Authentication using HTTP only cookies.
- Testing & TDD.
- API Documentation.
The app was built based on the Clean Architecture, SOLID principles, and Domain-Driven Design best practices using TypeScript.
The Clean Architecture is a way to separate the concerns of everything that goes into building complex enterprise applications.
To separate concerns, this application was built with a Clean Architecture. It is divided into Domain, Application, and Infrastructure layers: There is also a Main layer, which is the entry point of the API.
There are unit and integration tests covering each layer. The main tool used for testing is Jest.
To cover the Main layer, integration tests were created to test the HTTP requests of the API. That way, I can assure that the Express server is working correctly, all the adapters are also working as expected, and all the dependencies are being injected correctly. For all the other layers, unit tests were created, using mocks and stubs to mock the dependencies.
And for testing the MongoDB, an in-memory implementation was used as a Jest preset.
The Domain layer is the layer that contains the business logic of the application. It contains the Entities, which are the classes that represent the data of the API. This layer is isolated from outer layers concerns.
Due to limited time, I decided to take a simpler approach here. And, although some Domain-Driven Design patterns have been implemented, such as DTOs, Mappers , Entities, and the Repository pattern. Some other DDD patterns could also be implemented to enrich the application domain, and avoid illegal operations and illegal states.
Such as Value Objects, they could be used to define the minimum and maximum size, and the standards that the content of the post must follow. Not only that, but they could also be used to override all (or most) of the primitive types, such as strings, numbers, and booleans.
The Application layer is the layer that contains the application specific business rules. It implements all the use cases of the API, it uses the domain classes, but it is isolated from the details and implementation of outer layers, such as databases, adapters, etc. This layer just holds interfaces to interact with the outside world.
I also defined interfaces for each use case, in order to make the application more testable, since I'm using these interfaces to create stubs for testing the controllers and middlewares in the infrastructure layer.
The Infrastructure layer is the layer that contains all the concrete implementations of the application. It contains repositories, adapters, controllers, middlewares, etc. It also contains the validators, which are used to validate the data of the controllers.
The Main layer is the entry point of the application. It is the layer that contains the Express server, and where all the routes are defined. In this layer I also compose all the controllers, middlewares, and use cases, injecting the dependencies that are needed, since I am not using any dependency injection container.
For detailed information on each API endpoint and how to interact with the Notion Clone API, please refer to the comprehensive API documentation.
API Documentation: Notion Clone API Documentation
The API documentation contains:
- Detailed descriptions of each API endpoint, including the purpose and functionality.
- Example requests and responses for better understanding of API usage.
- Instructions on authentication and obtaining API keys for secure access.
- Overview of the data models used in the API.
- Code snippets and usage examples to help you get started quickly.
By referring to the API documentation, you can seamlessly integrate the Notion Clone API into your frontend application and leverage its powerful features for organizing and collaborating on information.
Notion is a powerful framework designed to empower users by providing unrestricted information management and control. At the core of Notion's architecture is the concept of blocks. Everything in Notion, be it text, images, lists, or even entire pages, is represented as a block. These dynamic units of information can be freely rearranged and converted into different block types, offering users precise control over their content.
Pages in Notion are collections of various blocks, each containing specific information based on its block type. They play a crucial role in organizing and managing data within databases. Databases in Notion offer different views such as kanban boards, tables, calendars, timelines, etc., with the smallest unit being a page. Each page in a database possesses properties that enable the storage of specific information, and these properties determine the desired view.
Moreover, Notion provides the concept of workspaces, where users can create multiple protected environments for collaboration. Workspaces accommodate numerous members and team spaces, granting access based on permission levels, ensuring a secure environment for collaboration and information management.
By harnessing the flexibility of blocks, the organizational power of pages, and the collaboration features of workspaces, Notion offers an unparalleled experience for users seeking comprehensive control over their information.
this is the core business logic represented in class diagram.
Create notion-clone directory
mkdir notion-clone
Go to the project directory
cd notion-clone
Clone the api project adjacent to notion-browser-client
git clone git@github.com:shreyasmanolkar/notion-api.git
Go to the project directory
cd notion-api
Install dependencies
npm install
Create .env file
cp .env-example .env
To connect DB we have 2 options
- run docker container
Run docker container
docker-compose up
- use mongodb Atlas or mongoDB locally and pass connection string / mongo URL in .env
Add mongo URL in .env
Create build
npm run build
Start the server
npm run start
Stop the server
docker-compose down --volumes
To run tests, run the following command
npm run test
```x
To run test coverage report, run the following command
```bash
npm run test:ci
To run single unit test, run the following command
update: package.json:
"test:only": "jest -- -t <test file name>",
"test:only": "jest -- -t SignUpController.spec.ts",
npm run test:only
This project is under the MIT license. See the LICENSE for more information.