Transitioning from Monorepo to Turborepo: My Development Journey with ConvoForm.com

Utkarsh Anand
8 min readFeb 6, 2024

--

Hi, I am Utkarsh Anand, This is the first article I am posting here. In this article, I am sharing my experience and some solutions to problems I faced while converting my Monorepo into Turborepo.

About my project

So recently I started an open-source side project ConvoForm (convoform.com), I started this to learn how to build a modern SaaS web app.

  • Frontend — Next.js 14 (App Router)
  • UI — shadcn/ui
  • Backend — tRPC
  • Database — Prisma with PostgreSQL
Screenshot of form editor

So this is just a simple form like Google Forms, While creating the form tell form fields of data you want to collect from the user, it could be one word like — name, email, or more descriptive like — “name of user”, “what is his contact number and email?”. Once you publish the form you will get a unique form submission link which you can share with anyone.

Screenshot of form submission page

Now come to the exciting part of this project, When the user visits the form submission link, Instead of just displaying all form fields on one page, It will do a conversation with the user by asking questions one by one for all the fields you provided, This improves the user experience throughout the entire form-filling process.

Screenshot of form responses

After the user completes the submission of the form, you can see the data in table form and also with the transcript of the whole conversation.

Github repo link — https://github.com/growupanand/ConvoForm

Please use my product once, all kinds of feedback are welcome, whether it is positive or something I can improve.

Why Turborepo?

In my two years of working experience, I understood the value of Monorepo, and how it allows every commit change to be worked on the whole app, because the test cases, linting, and type checks can be performed on the whole repo for each commit.

So why do we need Turborepo, First of all, it can cache the pipeline tasks results like linting and type checks, and if you rerun the same tasks, it will replay the results if there is no change in the code.

For example, if you have already run linting successfully for the whole app, and then you work on a bug fix. So you make some changes inside the code in Frontend folder, now when you commit these changes you don’t want to run linting for the backend folder again. So Turborepo will skip linting on Backend folder. It is a very small example, but think how much time it will save if your whole process is taking a long time.

Another benefit of Turborepo is that you can split your codebase into small packages, which you can use in your main app by importing

import { Button } from "@convoform/ui/components/ui/button";

In above @convoform/ui is a separate package folder in the same repo which have all the code related to design system components, like in my case I am using shadcn/ui. It will give you benefits if you have multiple frontend apps for your project example one is a marketing app, and the second is the main app. So you are using the same UI elements for both apps to make your design system sync with each other.

More benefits can be seen on their official site — https://turbo.build/repo

Project folder structure

Before Turborepo

├── prisma
│ └── migrations
├── public
├── src
│ ├── app
│ │ ├── (privatePage)
│ │ │ ├── (formEditorPage)
│ │ │ │ └── forms
│ │ │ │ └── [formId]
│ │ │ │ └── conversations
│ │ │ │ └── [conversationId]
│ │ │ └── (mainPage)
│ │ │ ├── dashboard
│ │ │ └── workspaces
│ │ │ └── [workspaceId]
│ │ ├── (publicPage)
│ │ │ ├── (formSubmissionPage)
│ │ │ │ └── view
│ │ │ │ └── [formId]
│ │ │ ├── (landingPage)
│ │ │ └── changelog
│ │ ├── api
│ │ │ ├── conversation
│ │ │ │ └── [conversationId]
│ │ │ ├── dashboard
│ │ │ ├── form
│ │ │ │ └── [formId]
│ │ │ │ ├── conversation
│ │ │ │ ├── conversations
│ │ │ │ ├── formFields
│ │ │ │ └── getNextFormField
│ │ │ ├── revalidate
│ │ │ ├── serverActions
│ │ │ ├── webhook
│ │ │ └── workspaces
│ │ │ └── [workspaceId]
│ │ │ └── forms
│ │ └── auth
│ │ ├── register
│ │ │ └── [[...sign-up]]
│ │ └── sign-in
│ │ └── [[...sign-in]]
│ ├── components
│ │ ├── changeLogPage
│ │ ├── common
│ │ ├── formEditorPage
│ │ │ ├── conversations
│ │ │ └── formEditor
│ │ ├── formSubmissionPage
│ │ ├── landingPage
│ │ ├── mainPage
│ │ │ ├── dashboard
│ │ │ ├── mainNavigation
│ │ │ └── workspace
│ │ ├── ui
│ │ └── wrappers
│ └── lib
│ ├── atoms
│ ├── config
│ ├── controllers
│ ├── data
│ ├── services
│ ├── types
│ └── validations
└── templates

After Turborepo

├── apps
│ └── web
│ ├── public
│ │ └── screenshots
│ └── src
│ ├── app
│ │ ├── (privatePage)
│ │ │ ├── (formEditorPage)
│ │ │ │ └── forms
│ │ │ │ └── [formId]
│ │ │ │ └── conversations
│ │ │ │ └── [conversationId]
│ │ │ ├── (mainPage)
│ │ │ │ ├── dashboard
│ │ │ │ └── workspaces
│ │ │ │ └── [workspaceId]
│ │ │ └── organizations
│ │ ├── (publicPage)
│ │ │ ├── (formSubmissionPage)
│ │ │ │ └── view
│ │ │ │ └── [formId]
│ │ │ ├── (landingPage)
│ │ │ └── changelog
│ │ ├── api
│ │ │ ├── form
│ │ │ │ └── [formId]
│ │ │ │ ├── conversation
│ │ │ │ └── getNextFormField
│ │ │ ├── og
│ │ │ │ └── _components
│ │ │ ├── revalidate
│ │ │ ├── trpc
│ │ │ │ └── [trpc]
│ │ │ └── webhook
│ │ └── auth
│ │ ├── register
│ │ │ └── [[...sign-up]]
│ │ └── sign-in
│ │ └── [[...sign-in]]
│ ├── components
│ │ ├── changeLogPage
│ │ ├── common
│ │ ├── formEditorPage
│ │ │ ├── conversations
│ │ │ └── formEditor
│ │ ├── formSubmissionPage
│ │ ├── landingPage
│ │ ├── mainPage
│ │ │ ├── dashboard
│ │ │ ├── mainNavigation
│ │ │ └── workspace
│ │ └── providers
│ ├── lib
│ │ ├── config
│ │ ├── controllers
│ │ ├── data
│ │ ├── services
│ │ ├── types
│ │ └── validations
│ └── trpc
├── packages
│ ├── api
│ │ └── src
│ │ ├── lib
│ │ ├── router
│ │ └── validators
│ ├── db
│ │ ├── prisma
│ │ │ └── migratio
│ │ └── src
│ ├── eslint-config
│ ├── tsconfig
│ └── ui
│ └── src
│ ├── components
│ │ └── ui
│ └── lib
└── templates

What challenges did I face while converting Monorepo into Turborepo?

so if you are starting a new project from scratch you can create Turborepo easily, but if you have already developed your project you will get some errors while in this process. This is the Pull request where I made my Monorepo into Turborepo. I am not giving any tutorial here for this, you can find many good articles on the internet. I will tell you some of the challenges I faced which takes my large time.

1. Next.js build error for prisma in turborepo

Type error: The inferred type of 'api' cannot be named without a reference to
'../../../../packages/db/node_modules/@prisma/client/runtime/library'. This is likely not portable. A type annotation is necessary.

This error took my whole day, I have searched it everywhere but nothing worked for me, So I will tell you how to get rid of this.

As you know we can create separate packages that can be used in apps, so when you create a db package where you put all the database schema and database ORM client-related code. So when you run it locally using dev there will be no problem. But once you try to build Next.js app it will throw this build error.

As you can see here,

You just need to use the generated client with a relative path, and if you noticed this client is generated inside the db package folder but not inside node_modules folder -packages/db/lib/generated/client . So to make Prisma generate this folder you need some changes inside your packages/db/prisma/schema.prisma , you can see here

And don’t forget to make changes in packages/db/package.json too, see here

2. Prisma client not working on production

After building success on Vercel when you open your website, It will throw an error at runtime in the browser, Prisma Client could not locate the Query Engine for runtime "debian-openssl-3.0.x"

As you can see here, you just need to use PrismaPlugin inside apps/web/next.config.mjs,

3. Vercel build not working

So for your existing project on vercel, if you try to deploy changes the build will failed because now all the folder structure is changed and vercel is not able to find build folder. You can easily fix it by modify your vercel build command in either apps/web/vercel.json file located in your project repo or in vercel web UI page setting for the project.

{
"buildCommand": "cd ../.. && pnpm build",
"outputDirectory": ".next"
}

Don’t forget to tell vercel where your Next.js app can be found, for this just give apps/web your root directory like in below screenshot.

Please comment if you found any more errors because I have put here only those that take my much time.

Finally here is a video of visualization which represents my contribution and my project structure. As you can see at the end of the video how the structure looks better.

Some useful links and repos which helped me:

What’s next?

So now I am working on moving from Prisma to Drizzle, I will post a new article for this also.

--

--