DEV Community

alony
alony

Posted on

3 1

Monorepo Backend Application with Bundled Packages

I spent hours searching and struggling to set up a server within a monorepo alongside private packages, trying various scripts and methods. Little did I know, the solution was right in front of me the whole time, simple and straightforward to implement.

The frustrating part is, when you're working on a Next.js project within a monorepo, adding your module to the transpilePackages entry in the configuration is all it takes. However, for a backend applications with a custom build step, it's not as straightforward.

I began by creating a monorepo using turbo with applications and packages. It took me 5 minutes with everything installed and working like a charm! Then I was setting up the backend application, utilizing swc to transpile TypeScript into CommonJS format.

My goal was to facilitate type sharing between my web application and backend, utilizing a shared library named "common". Implementing this in the web application, built with Next.js, was straightforward. However, I encountered challenges with the backend. While the transpilation process worked seamlessly on project files, the bundled version required a TypeScript file from the common package, causing issues.

Determined to find a solution, I rolled up my sleeves and delved into options for transpiling my common module. Despite exhaustive searching, swc didn't offer the necessary functionality, and I wasn't keen on resorting to Babel. Then, I stumbled upon esbuild, which promised to transpile all my code into a single bundle. It was a game-changer!

The Solution

To build the project I had to create a build.mjs file.
To transpile and bundle the project, simple run:

> node build.mjs
Enter fullscreen mode Exit fullscreen mode

This is the content of the file:


import * as esbuild from "esbuild";
import packageJson from "./package.json" assert { type: "json" };

// Bundle internal dependencies
const external = Object.keys(packageJson.dependencies).filter(
  (dep) => !dep.startsWith("@repo")
);

await esbuild.build({
  entryPoints: ["./src/index.js"],
  sourcemap: true,
  bundle: true,
  tsconfig: "tsconfig.json",
  platform: "node",
  outfile: "dist/index.js",
  external,
});
Enter fullscreen mode Exit fullscreen mode

Why mjs ?

The build process is waiting for the result, and I'm utilizing import statements. While I could include "type": "module" in my package.json file, doing so risks the bundle targeting esm, which would stress all the dependencies to use esm. However, it's worth noting that some libraries still don't support esm, which can be frustrating, but it's the reality we face.

External Dependencies

Initially, I attempted to run the build process without including the external entry. This led to lots of errors as it attempted to bundle all external dependencies into my bundle. I had hoped for a more robust solution. Consequently, I sought a method to bundle only my private packages, leaving the external ones outside.

Iterating through the dependencies and excluding my private ones, identifiable by their known names, assures me that they will be included in the bundle. For instance, @repo/common will be bundled, while @fastify/cors will not.

The Result

A bundle with my private packages inside!
There is no need to a build step in the common package, only on the top level. The checks I do have in the packages is lint, type checking and tests.

I hope that by sharing this article, I've saved someone valuable time in their search. This approach offers a practical method for managing a backend application within a monorepo while leveraging private packages.

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more