Initially, the project's root structure appeared like this:
.
# folders
├── .git/ # keep in the root
├── .vscode/ # keep in the root
├── node_modules/ # delete
├── playwright/
├── public/
├── scripts/
├── src/
# files
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierrc
├── .stylelintrc
├── README.md
├── alias.path.js
├── babel.config.js
├── config-overrides.js
├── dependency-suppressions.xml
├── nyc.config.js
├── package.json
├── playwright.config.ts
├── pnpm-lock.yaml
├── tsconfig.json
├── tsconfig.path.json
└── vite.config.tsNext, I began by relocating all the application-specific files into a folder named apps/web-client following this route:
- Removed
node_modulesto prevent copying a folder of vast size. - Maintained
.gitin the root directory.
.
├── .git/
├── .vscode/
└── apps/web-clientTo establish the pnpm workspace configuration, I created a pnpm-workspace.yaml file in the root directory with the following content:
packages:
- "apps/*"Then, I initiated pnpm in the root directory by executing the following commands:
pnpm init
pnpm installUpon completion, A pnpm-lock.yaml file was generated in the root directory.
Once the pnpm monorepo setup was established, I introduced NX to manage the project more efficiently. Here's how I integrated NX into the workflow:
First, I initiated NX by running:
npx create-nx-workspace@latestDuring initialization, I skipped specifying scripts execution order but configured cacheable scripts such as build, test, lint, and tsc. I designated the build folder for the output of the build script and left others blank.
Upon successful initialization, nx.json was created in the root directory.
Setting up commands
To streamline command execution across the monorepo, I updated the scripts field in the root's package.json:
"scripts": {
"dev": "npx nx run-many --targets dev",
"test": "npx nx run-many --targets test",
"lint": "npx nx run-many --targets lint",
"tsc": "npx nx run-many --targets tsc"
},I verified the functionality of these commands by running them from the root directory:
pnpm run dev
pnpm run test
pnpm run lint
pnpm run tscThanks to NX's caching mechanism, subsequent executions were notably faster.
Adding Another App
Integrating an additional app, such as an api-gateway, was straightforward. I simply copied the target repository into {root}/apps/, maintaining a similar structure to the existing web-client.
Next, I added another repository, user-service, following a similar procedure. This service was responsible for retrieving user information.
.
├── .vscode
├── apps
│ ├── web-client
│ ├── api-gateway
│ └── user-service
├── .gitignore
├── .npmrc
├── nx.json
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yamlOrchestrating All Apps Together
To ensure seamless orchestration, I updated each app's server port to avoid conflicts. I centralized the port variables in the root directory by creating a .env.local file:
WEB_CLIENT_PORT=3000
API_GATEWAY_PORT=3001
USER_SERVICE_PORT=3002With these configurations in place, the monorepo was now ready for efficient development and testing, with NX managing the intricate dependencies between the various apps.