Splitting a monorepo

Thursday, April 27, 2023

Tags:

I recently needed to split a monorepo. Somewhere between Node 20 making standalone apps possible, deployment platforms with vanishing or punitive free tiers, and the size of my app, I wanted my API and React in separate repositories.

Preserving Git Histories

I decided I’d keep the original monorepo as is, and create two repos to work with going forward: one for the frontend, and one for the backend. Both times, I started with a clone of the monorepo and used filter-repo to only include the files and folders that were relevent to the new repo I was creating. The filter-repo isn’t included in git, but it’s a python script that can be installed with a package manager. I ran pip install git-filter-repo.

For separating out the frontend:

git clone <repo>
git filter-repo --path client
cd ..\client
git remote add origin <new-repo>
git branch -M main
git push -u origin main

For the backend, I repeated the process above with a --invert-paths flag at the end of the second line to exclude all my frontend code.

Changes to the backend code

In the monorepo, the API served the React app’s index.html and static files, so the app and API shared the same domain. I needed to make two main changes: removing the route handler serving the front end’s files, and adding CORS middleware.

The last step was to clean up my build scripts in my package.json file, and deploy the API to a new platform. I went with Cyclic since the build size was now safely under their 240MB limit.

Changes to the frontend code

For the frontend, I didn’t need to alter the code I’d already written, but I did need to add some new files.

The urls in my fetch requests were relative, and I was deploying on Netlify, so I created a _redirects file. These contents would work for most React SPAs set up using Vite:

/api/*     https://<api-domain>/api/:splat      200
/assets/*  /assets/:splat                       200
/*         /index.html                          200

By default, Netlify redirects the paths with an HTTP code of 301. This means the request with the new URL to the API still comes from the React app. By setting the status code to 200, Netlify’s servers will proxy or rewrite the request to the API instead. This is especially important since browsers are getting stricter with third-party cookies.

I also added a new .gitignore to my frontend repo to exclude node_modules and .env files.