Creating a Full Typescript Monorepo with NX
Overview
NX Monorepo is a tool that allows us to build scalable applications within a single repository where one of its main benefits is modularization and code reuse.
The beginning can be a bit confusing and the worst path you can take to get introduced to this tool or framework is to run them and start creating a single repository with the "Get Start".
Although one of its principles is to save developers time in certain cases, it seems to be quite the opposite.
Therefore in this article I will describe all the necessary concepts, principles and details to understand how it works and in this way be able to create any monorepo using this standard.
What's the point
The benefit is that it provides tools that automatically allow us to configure a local environment with all the elements that contribute to good software development practices.
By executing commands it is possible to configure unit tests, from integration, continuous integration, continuous deployment, modularization, component reuse and much more.
This is done incrementally, allowing us to gradually add these tools until we obtain a satisfactory result. All this in an automated and modular way.
NX Bulding Blocks
Like any software tool, there are several elements that make up its architecture, understanding the usefulness of each of them will provide us with a better vision and decision making to face the problem of creating a single repository.
All those tools that were mentioned below are found within a workspace, which according to the documentation is nothing more than the structured environment that NX follows, which follows in x in a standardized way to organize and develop the software in a collaborative way.
NX Console
Nx Console is a plugin that can be added to different text editors, which works as a visual interface that allows the execution of nx commands without having to type them directly within the terminal.
NX Cloud
I also tried a cloud-based service designed for better efficiency and behavior of the workflows that exist in the mono repository, the advantage of this is that in a very simple way it is possible to connect github with NX Cloud and in a very quickly build a development, testing and production environment almost immediately.
Remote Caching
One of the main benefits, because there are two developers working on the same functionality and the tests take a considerable time to run the first time.
It will not be necessary to wait the same amount of time the second time the tests are run, because the results of these will be cached in the cloud, being able to synchronize the local environment of any developer with the latest results and reduce the amount of time waiting.
Not only can you cache tests, but also the results of other tasks such as build. Which allows you to increase the speed of development, without spending so much time on configuration and reducing continuous integration and continuous delivery costs.
Once a task result is saved in the cache, it cannot be changed. This ensures that the results are reliable and consistent, which is important for maintaining the quality of the builds.
Distributed Task Execution
Distributed task execution means that when developers need to run tasks (like building or testing their code), these tasks can be split up and run on multiple machines at the same time.
This parallel execution of tasks allows a large task to be subdivided into smaller ones, and this responsibility is spread among indifferent machines to achieve the same objective in a faster and more efficient way.
Structured Logs
Structured logs are detailed records of what is happening at the time tasks are being executed, which provides enough detailed information to find points of failure if a task cannot be completed successfully.
For example, when the production build is generated and it fails for some reason, determining the cause can be quite simple due to the clarity shown in the logs for that specific task.
VCS Integration
VCS integration refers to how Nx Cloud connects with version control systems (VCS) like GitHub and GitLab, that is, NX Cloud is aware of the changes that exist within the single repository that has been connected to cache previous results and in this way facilitate the display of future results.
Normally, when a Pull request is created, a series of tasks are executed that are the responsibility of nx, the result of these tasks is saved in cache so that when new changes are uploaded to this same Pull request, all the processes can be executed again but in one faster way taking into account the previously saved information.
NX Plugins
Nx plugins are npm packages that extend the functionality of an Nx monorepo by providing generators, executors, and utilities for specific tools and frameworks.
This is the strategy that allows you to gradually increase and modulate the structure and organization of a mono repository based on needs that arise in the future, that is, having the tools that allow you to configure all types of applications or libraries can lead to excessive project size locally.
It is quite necessary to have all these scripts when you will simply use a couple of them, so they can be installed as npm packages, each one according to the need that arises.
There are different types of plugins in relation to their origin, the official plugins are those that are developed and maintained by the Nx core team.
In addition to these, there are also some created by the community that can be reused and are found in the Nx Plugin Registry or other Custom Plugins created by individuals or organizations dedicated to solving internal problems.
We can also classify the plugins according to their functionality, to this we have the following:
Generators are those that allow you to create projects, libraries, components, etc. They are plugins that allow you to create with all the necessary configurations that are required.
The executors are in charge of efficiently initializing and finishing different tasks such as build, test, server, initialization and among many others.
Migrations are scripts that automatically allow us to update dependencies, configure new parameters when there is an update, in short. It allows you to carry updated Legacy code without worrying about all the problems that this entails.
DevKit
They are a series of tools, libraries and utilities that are shared across all projects within the single repository. This allows you to create standard practices for the development process and obtain the best results in code quality, simply using a general configuration.
In this way, all the projects and libraries that can be found within the repository will follow the same practices, so we will not have to worry about discrepancies in the future since all developers will follow the same guidelines.
Within this group of tools we can find several, such as prettier, eslint, husky, etc. But not simply this but also component libraries that can be used in different projects without code duplication, in addition to documentation and among other elements that guarantee development consistency.
Nx Capabilities
It could be said that these are the general capabilities that nx-remonopo has, which can already be intuited from what was mentioned above, however I am going to mention a couple of details.
Task Running
The ability to execute various commands (like building, testing, or serving applications) efficiently. Nx provides a command-line interface (CLI) that allows developers to run tasks for specific projects within the workspace.
Distribution
Allows tasks to be executed across multiple machines or environments. This feature is particularly useful in continuous integration (CI) setups, where tasks can be distributed to speed up the overall build and test processes.
Workspace Analysis
Workspace analysis involves examining the structure and dependencies within an Nx workspace. Nx provides tools to visualize the relationships between projects, libraries, and their dependencies, helping developers understand how changes in one part of the codebase might affect others.
Caching
Mechanism that stores the results of previously executed tasks. When a task is run, Nx checks if the result is already cached and, if so, reuses that result instead of executing the task again.
Code Generation
Code generation in Nx allows developers to quickly scaffold new applications, libraries, components, or other code structures using predefined templates.
Migration
The process of upgrading dependencies, tools, or configurations within the workspace.
Creating a monorepo
It is time to start with the creation of the mono repository, which will be focused on building a full stack application that will use the following tools:
- NextJS
- NestJS
- Schadcn
- Typescript
- Prisma
Workspace
The following command will perform all the essential configurations to configure the mono repository, it is important that you define the name of the app although it is optional.
npx create-nx-workspace@latest app-monorepo --package-manager=pnpm --app-name=app-name
The nx.json
is a configuration file used in an Nx workspace (monorepo) that contains important settings and metadata related to the workspace's structure and behavior.
While it can be modified manually, it is generally recommended to use the nx command to add the configurations.
You can add a fixed package manager by adding the --package-manager=pnpm
flag.
Eslint, Prettier, Husky
I was looking for a way to configure these tools directly using an nx command and apparently it is not possible so far, anyway I have done the configuration manually.
pnpm add -D eslint eslint-config-prettier eslint-plugin-eslint-comments eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks husky prettier lint-staged @typescript-eslint/parser
pnpm add -D @nx/eslint
npx eslint --init && nx g @nx/eslint
Add NextJS
Add NestJS
Add Typescript Library
Add Schadcn
Add Prisma Library
Configure Project Dependencies
Define Dependencies: Specify dependencies for each application and library, ensuring that the Next.js app can access the Shadcn component library and the NestJS app can utilize the TypeScript library.
Manage Inter-project Dependencies: Use Nx’s dependency graph to visualize and manage the interdependencies between projects effectively.
Set Up Code Generation and Configuration
Implement Code Generation: Utilize Nx generators to create boilerplate code for components, services, and modules in both the Next.js and NestJS applications.
Configure ESLint and Prettier: Set up ESLint and Prettier for consistent code formatting and linting across the entire monorepo.
Establish Testing Frameworks: Integrate testing frameworks (e.g., Jest for unit tests and Cypress for end-to-end tests) for both the frontend and backend applications.
Implement Caching and Task Running
Enable Caching: Configure Nx caching to optimize build and test processes, ensuring that tasks are only executed when necessary.
Set Up Task Running: Define tasks for building, serving, and testing each application and library, leveraging Nx’s task runner for efficient execution.
Establish CI/CD Pipeline
Configure Continuous Integration: Set up CI workflows to automatically run tests and builds whenever changes are pushed to the repository.
Implement Continuous Deployment: Define deployment strategies for both the Next.js and NestJS applications, ensuring that updates are deployed seamlessly.
Documentation and Best Practices
Document the Project Structure: Create comprehensive documentation outlining the project structure, coding standards, and best practices for contributing to the monorepo.
Establish Conventions: Define conventions for naming, file organization, and code structure to maintain consistency across the codebase.
Monitor and Optimize Performance
Analyze Workspace Performance: Use Nx’s analysis tools to monitor the performance of the workspace, identifying bottlenecks and areas for optimization.
Refactor and Improve: Continuously refactor code and improve configurations based on feedback and performance metrics.