Home » Blog » Build a Full Stack Todo Web App using MERN (Part 2)

Build a Full Stack Todo Web App using MERN (Part 2)

  1. Build a Full Stack Todo Web App using MERN (Part 1)
  2. Build a Full Stack Todo Web App using MERN (Part 2)

Great ! In the last part, we came up with the process, also we designed UI Mocks using Figma, which you can found in dribbble here.

In this part, we gonna continue to setup our App’s workspace :

Setting up our Monorepo with Yarn Workspaces and Lerna

When coupled together, Lerna and Yarn Workspaces can ease and optimize the management of working with multi-package repositories.

Lerna makes versioning and publishing packages to an NPM Org a painless experience by providing helpful utility commands for handling the execution of tasks across multiple packages.

Yarn Workspaces manages our dependencies. Rather than having multiple node_modules directories, it intelligently optimizes the installing of dependencies together and allows for the cross-linking of dependencies in a monorepo. Yarn Workspaces provide tools, like Lerna, the low-level primitives it needs to manage multi-package repositories.

Setup yarn workspaces

yarn config set workspaces-experimental true

Setup lerna

yarn add -D lerna 
npx lerna init

When we setup lerna, packages folder is automatically created, inside this we will move our server and client app.

So now we have packages/web-client and packages/server-api (2 packages under packages folder).

In order to set up Lerna with Yarn workspaces, we need to configure the lerna.json
Let’s add yarn as our npmClient and specify that we’re using yarn workspaces. For this tutorial we’ll be versioning our packages independently.

// lerna.json
  "packages": ["packages/*"],
  "version": "independent",
  "npmClient": "yarn",
  "useWorkspaces": true

At this point we should only have a root package.json. In this root package.json we need to add workspaces and private to true. Setting private to true will prevent the root project from being published to NPM.

// package.json
  "name": "fullstackopen-js",
  "private": true,
  "workspaces": [

If you have common dev dependencies, it’s better to specify them in the workspace root package.json. For instance, this can be dependencies like Jest, Husky, Storybook, Eslint, Prettier, etc. We had installed eslint, prettier and husky in server, but we need to make sure that its installed in the root, -W tells yarn that install the dependency in workspace root folder.

yarn add husky --dev -W

Let’s now clean up the nodepackages in our web-client and server-api folder. run the following command:

npx lerna clean -y

It will clean up all the node_modules in the packages/web-client and packages/server-api.

Then to install all the packages:

lerna bootstrap 

usually, its same like doing yarn install in the workspace root folder.

That’s it ! Let’s add few scripts to tell lerna for the commands to execute:

yarn add -D concurrently

we are using concurrently to simultaneously run server and client:

    "name": "fullstackopen-js",
    "private": true,
    "version": "0.0.1",
    "workspaces": [
    "scripts": {
          "server:lint": "yarn workspace fullstackopen-server prettify",
        "server": "yarn workspace fullstackopen-server start",
        "client": "yarn workspace fullstackopen-client start",
        "start": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\"",
        "prepare": "husky install packages/server-api/.husky",
        "lint": "lerna run lint --scope=fullstackopen-server"
    "devDependencies": {
        "husky": "^6.0.0",
        "lerna": "^4.0.0"
    "dependencies": {
        "concurrently": "^6.2.0"

Now, the package.json looks like this, –scope, refers to which package you want to explictly run the command for. As you can see yarn lint will only run the command for server

Now we can run to start our app :

yarn start