nextjs-monorepo-example

repository·main·Indexed 23 days ago

https://github.com/belgattitude/nextjs-monorepo-example

A Next.js oriented monorepo example demonstrating best practices for structure, shared packages, and tool integration using Turborepo and Yarn 4. It includes a Next.js application with REST and GraphQL APIs, I18N via next-i18next, and shared workspace packages such as @your-org/db-main-prisma for database management, @your-org/common-i18n for translations, and @your-org/eslint-config-bases for composable linting configurations. The project provides full Docker Compose support for deployment and local development.

Tokens
35.5K
Snippets
82
Records
207
Agent score
83%

What's inside nextjs-monorepo-example

  1. Understand the Monorepo structure

    main

    The project follows a standard monorepo structure managed by Turborepo and Yarn 4, using TypeScript path aliases.

    Core Organization

    • apps/: Contains deployable applications.
      • nextjs-app: A Next.js application featuring SSR, i18n, API routes, and Vitest.
      • vite-app: A basic Vite application.
      • Rule: Apps should not depend on other apps; they should only depend on packages.
    • packages/: Contains shared libraries and configurations.
      • core-lib: Publishable basic TypeScript libraries.
      • db-main-prisma: Database layer using Prisma.
      • eslint-config-bases: Shared ESLint configurations.
      • ui-lib: A publishable React-based design system (using Emotion and Storybook).
      • common-i18n: Shared locales and i18n logic.
      • Rule: Packages can depend on each other.
    • static/: Contains non-code assets like images, JSON, and locales.
    • docker/: Contains Docker configurations, including a multi-stage Dockerfile for nextjs-app and docker-compose files for services like PostgreSQL.

    Key Configuration Files

    • package.json (root): Defines the workspace configuration.
    • tsconfig.base.json: The base TypeScript configuration used across the monorepo.
    • .yarnrc.yml: Yarn configuration.
  2. Understand the Next.js app directory structure

    main

    The application follows this organizational structure:

    • src/backend/*: Backend logic.
    • src/components/*: Shared UI components.
    • src/features/*: Feature-based modules (grouped by context).
    • src/pages/api: Next.js API routes.
    • public/locales/: I18n translation files.
    • next-i18next.config.mjs: I18n configuration.
    • tsconfig.json: Local path mappings for monorepo packages.
    .
    ├── apps
    │   └── nextjs-app
    │       ├── public/
    │       │   └── locales/
    │       ├── src/
    │       │   ├── backend/*     (backend code)
    │       │   ├── components/*
    │       │   ├── features/*    (regrouped by context)
    │       │   └── pages/api     (api routes)
    │       ├── .env
    │       ├── .env.development
    │       ├── (.env.local)*
    │       ├── next.config.mjs
    │       ├── next-i18next.config.mjs
    │       ├── tsconfig.json    (local paths enabled)
    │       └── tailwind.config.js
    └── packages  (monorepo's packages that this app is using)
        ├── core-lib
        ├── main-db-prisma
        └── ui-lib
  3. Dependency management strategy: Exact vs Semver

    main

    This monorepo follows a specific dependency pinning strategy to balance stability and ease of updates:

    • Apps: dependencies and devDependencies are pinned to exact versions. This ensures reproducible builds and prevents unexpected breaking changes in application deployments.
    • Packages: Dependencies use semver compatible ranges. This allows shared libraries to be more flexible when consumed by different apps.

    To maintain these dependencies, you can use the following scripts:

    • yarn deps:check: Checks for outdated dependencies.
    • yarn deps:update: Updates dependencies.

    Alternatively, you can use Renovatebot for automated updates, as the project includes a renovate.json5 configuration.

  4. Understand the App-level directory structure

    main

    Each application within the apps/ directory follows a standardized structure:

    • e2e/: (Optional) Contains end-to-end tests using frameworks like Cypress or Playwright.
    • public/: Framework-conventional public assets folder.
    • setup/: Configuration files for tooling, such as Vitest or React Testing Library (RTL).
    • src/: The main application source code.
    • package.json: Defines all dependencies required to run this specific app independently.
    • next.config.mjs, tsconfig.json, eslintrc.cjs: Framework and tooling configuration files.
    🌳 (./apps)
     └── 🍂 nextjs-app
         ├── 🏁 e2e
         ├── 👀 public
         ├── 🔩 setup
         ├── 💫 src
         ├─ eslintrc.cjs
         ├─ next.config.mjs
         ├─ package.json
         ├─ (tailwind.config.ts)
         └─ tsconfig.json
  5. Integrate Prettier with ESLint

    main

    There are two mutually exclusive ways to handle Prettier integration. Choose the one that fits your workflow:

    1. @your-org/eslint-config-bases/prettier-plugin (Recommended for simplicity): ESLint will run Prettier automatically during the lint process. This is easiest to set up.
    2. @your-org/eslint-config-bases/prettier-config (Recommended for performance): ESLint only disables rules that conflict with Prettier. You must run Prettier as a separate command (e.g., prettier --write .).

    To customize your Prettier configuration while using the provided helpers, update your .prettierrc.js:

    // .prettierrc.js
    // @ts-check
    const { getPrettierConfig } = require("@your-org/eslint-config-bases/helpers");
    
    /**
     * @type {import('prettier').Config}
     */
    module.exports = {
      ...getPrettierConfig(),
      overrides: [
        // your custom overrides
      ],
    };
  6. Understand the Source-level (src) directory structure

    main

    The src/ directory of applications follows a pattern inspired by bulletproof-react, adapted for Next.js features. Key directories include:

    • app/ or pages/: Next.js routing directories (App Router or Pages Router).
    • features/: Domain-driven feature modules (the core of the application logic).
    • components/: React components shared across the entire application.
    • hooks/: React hooks specific to this application.
    • layouts/: Layout components.
    • providers/: React Context providers.
    • server/: Server-side specific code.
    • lib/: Third-party library configurations or wrappers.
    • utils/: Utility functions (e.g., for tRPC).
    • styles/: CSS, variables, and global styles.
    • types.d: TypeScript declaration files.
    🌳 nextjs-app
     └── 💫 src
         ├── app(*)
         ├── components
         ├── config
         ├── 🎼 features
         ├── hooks
         ├── layouts
         ├── lib
         ├── pages(*)
         │   └── api(*)
         ├── providers
         ├── 🍂 server
         ├── styles
         ├── types.d
         ├── utils
         └─ (middleware.ts(*))
  7. Use Storybook for UI component development in @your-org/ui-lib

    main

    Storybook is used within the @your-org/ui-lib package to build UI components in isolation from the application's business logic, data, and context. This allows you to develop and test hard-to-reach UI states by saving them as stories.

    To learn how specific components are implemented in Storybook, you can view their code in the stories directory within the package.

  8. Understand the Docker multistage build process

    main

    The project uses a multi-stage Dockerfile to minimize image size and optimize build times by leveraging buildx caching. The process is divided into three main stages:

    1. Stage 1: deps: Installs the monorepo dependencies and makes node_modules available for subsequent stages. This stage can be skipped if the lock file hasn't changed.
    2. Stage 2: builder: Automatically runs the deps stage, copies the installed node_modules, performs the build, and removes devDependencies to keep the image slim.
    3. Stage 3: runner: The final production stage. It launches the production build and listens on http://localhost:3000 by default. You must provide a .env file with required runtime variables.

    Note: Ensure you have a .dockerignore file configured to prevent unnecessary files from being sent to the Docker daemon.

  9. Quick start with @your-org/db-main-prisma

    main

    To initialize the database package, start the database container using Docker and then run the Prisma lifecycle commands. This sequence sets up the schema, seeds the data, and prepares migrations.

    1. Start the database: docker-compose up database
    2. Navigate to the package: cd packages/db-main-prisma
    3. Execute setup commands:
      • yarn prisma-db-push (Create/Push schema)
      • yarn prisma-db-seed (Seed data)
      • yarn prisma-migrate dev (Run migrations)
      • yarn prisma-migrate-reset (Reset database)
    cd packages/db-main-prisma
    yarn prisma-db-push
    yarn prisma-db-seed
    yarn prisma-migrate dev
    yarn prisma-migrate-reset
  10. Install Docker requirements for the monorepo

    main

    To use the Docker features in this project, ensure you have the following installed:

    • docker-engine >= 20.10.0
    • docker-compose >= 1.29.0
    • Docker buildx plugin
    • Docker buildkit enabled

    Optional tools:

    • lazydocker: A TUI for managing Docker.
    • dive: For debugging layer sizes.

    Ubuntu Installation Example

    If you are on Ubuntu, you may need to remove OS defaults before installing the official Docker engine:

    sudo apt-get remove docker docker-engine docker.io containerd runc
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt-get update
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin