Yarn workspaces — monorepo beginner’s guide
In this guide, we will create a sample monorepo using Yarn workspaces. It will contain both back-end (express) and front-end (react) projects, using packages for dependency management.
Have you ever worked on the project, which code structure could have been better? Everybody had. And have you ever worked on the project, that should be modularized or split into multiple packages? Probably you had as well. Why then? Was it a lack of time to properly manage all dependencies? Or was it a problem with publishing your packages and keeping them up-to-date all time long?
Probably both.
Fortunately, yarn has developed a perfect tool, which exceedingly helps with packaging and dependency management, by storing all those files in the one, monolithic repository. And don’t confuse this with the monolith. It’s different. It’s monorepo.
If you’re not aware of monorepo benefits and flaws check this article out.
Why
Yarn is fast. Yarn stores packages in a cache. Yarn is parallel. But what about Yarn workspaces? That’s how Yarn explains them:
Yarn Workspaces is a feature that allows users to install dependencies from multiple package.json files in subfolders of a single root package.json file, all in one go.
(…)
Yarn can also create symlinks between Workspaces that depend on each other, and will ensure the consistency and correctness of all directories.
It means we won’t store any dependency twice, no matter how many projects use it. We save disk space and we speed up the whole installation process.
How
Let’s start building a monorepo using yarn!
mkdir yarn-monorepo & cd yarn-monorepo
Then we need to initialize the repository
yarn init
Now we want to create a frontend project using the react app template.
yarn create react-app frontend
It’s a good practice to keep consistent naming inside our workspaces. The naming convention can be as following: @repo-name/workspace-name
. That’s why you can enter the frontend directory and rename the component in the package.json file to @yarn-monorepo/frontend
.
When thinking about creating a backend repo, it seems reasonable to extract the config data somewhere. It can be a .env file, it can be a configuration package. Let’s stick with the second solution.
mkdir packages & cd packages // create a default directory for all packages we will use in the futuremkdir config & cd config & yarn init // remember the naming convention @yarn-monorepo/config
Then create index.js
file with the following content:
module.exports = {
PORT: '3000'
};
Now it’s time to prepare the backend:
mkdir backend & cd backend & yarn init
yarn add express
Open package.json
file and add "yarn-monorepo/config": “1.0.0"
to its dependencies. Then add the start command to the scripts
"scripts": {
"start": "node index.js"
}
The last step is to create the index.js
file with the following content:
As you see, now backend is responsible for serving the frontend react app in the /
path. We are also using our config, that we’ve created before.
Now it’s time to let Yarn Workspaces do the job. Let’s go to the root directory and in the main package.json add the following sections:
"private": true,
"workspaces": [
"frontend",
"backend",
"packages/*"
],
"scripts": {
"start": "yarn --cwd backend start",
"build": "yarn --cwd frontend build"
}
Build script is going to build the frontend, start script is going to start the server which will serve the frontend for us.
So the last but not least, run yarn command to install all dependencies.
yarn
Done! Let’s try to see how it works:
yarn build
yarn start
It works! Open http://localhost:3000 for the result.
Link to the repo with the full example: https://github.com/mkocik/yarn-monorepo-example
Conclusion
As you may notice from the example above, it’s super easy to create new packages using yarn. Our code becomes more modular and more elegant. We keep our project structured and reusable. So if you are not afraid of the monorepo and you consider using this approach in your current/next project, you can truly benefit from what yarn is capable of.