JavaScript Monorepo with Lerna

Erzhan Torokulov
Jan 6, 2019 · 3 min read

JavaScript nowadays is almost everywhere: on the backend, frontend, desktop, mobile, tooling etc.

If your project consists of multiple JavaScript repositories, now, it’s much better to move them into a single/mono repository and control them using Lerna.


What is Lerna?

Lerna is a tool for managing JavaScript projects with multiple packages.

I recommend you take a look at lerna commands before we proceed.

Why Monorepo?

Monorepo is NOT about mixing everything together inside a project(do not confuse with monolith).
Monorepo is about having source codes of multiple applications, services, and libraries that belong to one domain inside a single repository.

Note: Monorepo can be organized in any comfortable way, directory tree, it’s up to developer/team.

Pros:

  • Fixed versioning for the whole system (apps/services/libraries).
  • Cross-project changes. For instance, a feature that changes multiple parts of the system (libraries/services/apps) can be done in one Pull Requests.
  • Single clone. No need to clone several repositories.
  • Access to all parts of the system. Simplified refactoring of the whole system.

Cons:

  • Version control system performance downside.
  • Security. Access to all parts of the system by all developers.

Requirements

  • Node.JS version 8 or above

Get Started

For the purpose of this example, we will create a simple app consisting of:

  • API: API service (backend)
  • frontend: frontend/web app

Also, in order not to mix all logic together, we’ll create separate packages:

  • validator: custom validation library/package
  • logger: custom logging library/package

The overall file structure of our monorepo project would be:

packages/        # directory for our custom libraries
../validator # custom validation helpers
../logger # custom logger library
apps/ # directory for our apps/services
../api # API backend
../frontend # frontend/web

1. Initialize Monorepo

# install lerna globally
npm i -g lerna
# create a new dir for project
mkdir my-project && cd my-project
# initialize a new lerna managed repository
lerna init

and Edit

{
"packages": [ "packages/*", "apps/*"],
"version": "0.1.0"
}

Note: We’ll use npm scoped package naming for all our apps and packages.
Example:

Let’s start with library packages.

2. Create a

  1. Create and initialize package:
# create library directory and cd inside
mkdir -p packages/validator && cd packages/validator
# initialize library with scope name
npm init --scope=my-project --yes

2. Add with the following content:

3. Create a “logger” library package

  1. Create and initialize package:
# From the root directory of the repository# create library directory and cd inside
mkdir -p packages/logger && cd packages/logger
# initialize library with scope name
npm init --scope=my-project --yes

2. Add with the following content:

4. Create an “api” application package

  1. Create and initialize package:
# From the root directory of the repository# create app directory and cd inside
mkdir -p apps/api && cd apps/api
# initialize app with scope name
npm init --scope=my-project --yes
# install express
npm i express --save
# add our logger library as dependency to our api app
lerna add @my-project/logger --scope=@my-project/api

2. Add file:

3. Add start script to :

"scripts": {
"start": "node index.js"
...
}

4. Run app: and open

5. Create a “frontend” application package

  1. Create using :
# From the root directory of the repository# create frontend app using create-react-app
cd apps && npx create-react-app frontend

2. Edit :

{
"name": "@my-project/frontend",
...
"proxy": "http://localhost:8080"
}

3. Add our validator as a dependency to our frontend.

# Add validator library as a dependency to frontend 
lerna add @my-project/validator --scope=@my-project/frontend

4. Add :

5. Add somewhere inside :

...
import {Greeting} from './Greeting';
class App extends Component {
...
render() {
return (
<div className="App">
<header className="App-header">
...
<Greeting />
</header>
</div>
);
}
}
...

6. Run frontend app: and open .


Conclusion

As you can see, a mono repository can contain as many apps, libraries as needed for the project. The important thing is to keep everything loosely coupled:

  • One app/service/library -> One package

Common logic, utils or helpers can be placed in a separate package. Thus, we can build highly independent packages, which is much simpler to understand, maintain and refactor.

See full source code on Github.

Also, you can look at the more advanced monorepo example here.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade