# A public repo's schema file
https://github.com/docusealco/docuseal/blob/master/db/schema.rb
```

You can generate an ER diagram by inserting `liambx.com/erd/p/` into the URL:

```
https://liambx.com/erd/p/github.com/docusealco/docuseal/blob/master/db/schema.rb
      👾^^^^^^^^^^^^^^^^^👾
```

For detailed instructions, check out [Web version](/docs/web).

### Private Project Setup

For internal or private repositories, run this command to start an interactive setup:

```npm
npx @liam-hq/cli init
```

Then follow the prompts to build a static version of your diagrams.

For more info, see [CLI version](/docs/cli).

## Need More?

- **Feature Requests & Ideas**: Share your thoughts on our [GitHub Discussions](https://github.com/liam-hq/liam/discussions).
- **Roadmap**: Check our latest progress on the [Roadmap](https://github.com/orgs/liam-hq/projects/1/views/1).


---
title: CI/CD Integration
---

import { File, Folder, Files } from "fumadocs-ui/components/files";

## Overview

By integrating Liam ERD into your CI/CD pipeline, you can **automatically generate ER diagrams on every commit and host their latest versions**.
This ensures that your entire team and all stakeholders can consistently access the most up-to-date schema.

### Why is Hosting the Latest ER Diagram Important?

- **Streamlined Onboarding**
  When new or cross-functional team members need to understand the schema, there’s no need to dig through source code or spreadsheets. The information is consistently available and always current.
- **Improved Communication**
  Visual representations of the data structure make it easier to discuss, clarify, and align on database design among developers, product managers, customer support, data analysts, and more.
- **Drawbacks of Manual Updates**
  Manually maintaining ER diagrams can lead to oversights, typos, and inconsistent documentation, making it **difficult to stay up to date**. By automatically generating and hosting ER diagrams via CI/CD, you reduce human errors and management overhead, guaranteeing **accurate information at all times**.

## Example: GitHub Actions + Static Hosting

Because Liam ERD is built as a Vite-based SPA, it can be deployed on **any static hosting service**. Below is an example showing how to automatically build and deploy ER diagrams using GitHub Actions:

```yaml
name: Deploy ERD on every commit

on:
  push:
    branches:
      - main
    # If you only want to trigger this when the schema file is updated:
    # paths:
    #   - db/schema.rb
    #   - db/structure.sql
    #   - prisma/schema.prisma

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # 1. Install the Liam ERD CLI
      - name: Install Liam ERD CLI
        run: npm install -g @liam-hq/cli

      # 2. Generate the ER diagram
      - name: Generate ER Diagram
        # You can specify a custom output directory with --output-dir option if needed
        run: liam erd build --input ./db/schema.rb --format=schemarb

      # 3. Publish the build artifacts (dist folder) to a static hosting service
      #    Here, we show an example using GitHub Pages.
      # NOTE: To keep your GitHub Pages site private,
      # your organization must be on GitHub Enterprise Cloud.
      - name: Deploy to GitHub Pages
        uses: actions-gh-pages/action@v2
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: dist
```

<Callout title="info" type="info">You can use either `npx @liam-hq/cli erd build` or `liam erd build` (if installed globally) to run the Liam ERD CLI.</Callout>

The same approach can be applied to hosting services such as [Cloudflare Pages](https://www.cloudflare.com/developer-platform/products/pages/), [Vercel](https://vercel.com/), and [Netlify](https://www.netlify.com/).

## Liam ERD’s HTML Structure (Vite-based SPA)

When you run `npx @liam-hq/cli erd build` or `liam erd build`, a `./dist` directory (or the directory specified by `--output-dir` option) is created with the following structure. Here, `index.html` acts as the single entry point for your **single-page application (SPA)**:

<Files>
  <Folder name="dist" defaultOpen>
    <Folder name="assets" defaultOpen>
      <File name="favicon-xxxxxxxx.ico" />
      <File name="index-xxxxxxxx.css" />
      <File name="index-xxxxxxxx.js" />
    </Folder>
    <File name="favicon.ico" />
    <File name="index.html" />
    <File name="schema.json" />
    <File name="serve.json" />
  </Folder>
</Files>

The `schema.json` file contains the parsed schema data in JSON format, which `index.html` reads to render the ER diagram.

Additional notes:

- You can place these files under a subdirectory if needed.
- This output is similar to a typical Vite-built SPA. Refer to [Deploying a Static Site (Vite official documentation)](https://vite.dev/guide/static-deploy.html) for more details on deployment to common hosting providers.

## Using the `init` Command

The `init` command in the [Liam ERD CLI](/docs/cli) provides an interactive way to generate configuration files or GitHub Actions templates tailored to your project, automatically handling tasks like:

- Specifying the input file (`--input`) and schema format (`--format`)
- Generating sample GitHub Actions workflows

If you’re new to Liam ERD or unsure about configurations, simply run:

```npm
npx @liam-hq/cli init
```

Follow the on-screen prompts to set up Liam ERD quickly.

## Example: Prisma + GitHub Actions + Cloudflare Pages

Below is a more advanced example using `schema.prisma` with GitHub Actions to generate and deploy ER diagrams to Cloudflare Pages.
Cloudflare Pages supports simple access restrictions via Cloudflare Access, enabling you to limit diagram visibility to internal team members only.

```yaml
name: Deploy ERD (Prisma) to Cloudflare Pages

on:
  push:
    branches:
      - main
    # NOTE: If you only want to trigger this when the schema file is updated:
    paths:
      - prisma/schema.prisma

jobs:
  build-and-deploy-erd:
    runs-on: ubuntu-latest

    permissions:
      contents: read
      deployments: write

    steps:
      - uses: actions/checkout@v4
      - name: Generate ER Diagrams
        # You can specify a custom output directory with --output-dir option if needed
        run: npx @liam-hq/cli erd build --input prisma/schema.prisma --format prisma
      - name: Deploy ERD to Cloudflare Pages
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_SAMPLE_PRISMA }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID_SAMPLE_PRISMA }}
          command: pages deploy ./dist --project-name=prisma-with-cloudflare-pages
          gitHubToken: ${{ secrets.GITHUB_TOKEN }}
```

We plan to share more examples for other static hosting platforms. The above workflow is adapted from [Deploy ERD to Cloudflare Pages Sample](https://github.com/liam-hq/liam-erd-samples/blob/main/.github/workflows/prisma-with-cloudflare-pages.yml). To see more examples, visit [Liam ERD Samples](https://github.com/liam-hq/liam-erd-samples).

## Common Pitfalls

### Security and Privacy
- If you are using a static hosting service like Cloudflare Pages, Vercel, or Netlify, **avoid exposing your private repository schema** by default.
- Configure organizational authentication so that **only internal members** can view it.

## Conclusion

Integrating Liam ERD into your CI/CD pipeline is a **powerful way** to share **“the most up-to-date ER diagram based on your latest DB schema”** across the entire team.

- By automatically building and hosting ER diagrams, you **reduce manual workload and human errors** while ensuring that database documentation is always current.
- This approach improves development, maintenance, and onboarding, ultimately boosting overall team productivity.

Try incorporating Liam ERD into your CI/CD workflow and experience the benefits of **automated ER diagram management** firsthand!


---
title: Liam ERD CLI
---

import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; // For package-install code blocks

## Quick Start

The fastest way to get started with Liam ERD is using the interactive setup command:

```npm
npx @liam-hq/cli init
```

## Manual Setup

For more control over the ERD generation process, you can use the `erd` command directly:

### Basic Usage

Generate an ERD from your schema file using the following command:

```npm
npx @liam-hq/cli erd build --input <path|url>
```

This command processes your schema file and generates interactive ERD visualization files in the `dist` directory. The schema format is automatically detected (see [Format Auto-Detection](/docs/parser/supported-formats#format-auto-detection)), but you can override it using the `--format` option if needed. Also, output directory can be specified by `--output-dir` option.

Once the ERD is generated, you can view it by serving the files using a local HTTP server:

```npm
npx serve dist/  # or your custom output directory
```

The server will start and provide you with a local URL (typically http://localhost:3000) where you can view your ERD in a web browser.

You can use any hosting service of your choice to serve the generated files.

### Options

- `--input <path|url>`: Path to your schema file or URL
- `--format <format>`: (Optional) Override the auto-detected schema format
- `--output-dir <path>`: (Optional) Specify the output directory for generated files (default: "dist")

### From GitHub Public Repository

You can directly specify URLs to schema files stored in public GitHub repositories. Using raw URLs allows you to generate ERDs directly from remote schema files.

```npm
npx @liam-hq/cli erd build --input https://github.com/user/repo/blob/main/examples/schema.sql --format postgres
```

```npm
npx @liam-hq/cli erd build --input https://raw.githubusercontent.com/user/repo/main/examples/schema.sql --format postgres
```


### Output

The command generates a simple web application using [Vite](https://vite.dev/), which includes JavaScript, CSS, and HTML files, in the `dist` directory of your current working directory (or the directory specified by `--output-dir` option).

To view the generated ERD, serve the output directory using any HTTP server:

```npm
npx serve dist/  # or your custom output directory
```

## Sample Projects

For sample projects and setup examples, check out our [liam-erd-samples](https://github.com/liam-hq/liam-erd-samples) repository.


---
title: Community Resources
description: A collection of community-created resources and articles about Liam ERD
---

## Using Liam ERD with Databases

### MySQL

- [Liam ERD で tbls からサクッと MySQL の ER 図を作成してみた](https://zenn.dev/nextbeat/articles/liam-erd-tbls-mysql)
  - February 2024 • zenn.dev • [translated](https://zenn-dev.translate.goog/nextbeat/articles/liam-erd-tbls-mysql?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=ja&_x_tr_pto=wapp&_x_tr_hist=true)
  - A guide to generating MySQL ER diagrams using Liam ERD with tbls
  - Includes practical examples using the sakila sample database

### Ruby on Rails

- [ER図自動生成のためにLiam ERDを試験導入してみました！](https://tech.gree-x.com/automated-er-diagram-generation/index.html)
  - May 2025 • tech.gree-x.com • [translated](https://tech-gree--x-com.translate.goog/automated-er-diagram-generation/index.html?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=ja&_x_tr_pto=wapp)
  - A practical guide to generating ER diagrams from Ruby on Rails `schema.rb` files using Liam ERD.
  - Covers CLI usage, Docker-based hot-reload setup, and automated S3 deployment with GitHub Actions.

## Cloud Deployment

- [Liam ERDで生成したER図をCloud Run on GCSで公開する](https://zenn.dev/sikeda107/articles/8da7d91277f9aa)
  - August 2025 • zenn.dev • [translated](https://zenn-dev.translate.goog/sikeda107/articles/8da7d91277f9aa?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=ja&_x_tr_pto=wapp&_x_tr_hist=true)
  - A step-by-step guide to deploying Liam ERD-generated diagrams on Google Cloud Platform
  - Covers Cloud Storage setup, Cloud Run deployment with Nginx, and public URL configuration

## Articles Featuring Liam ERD

- [6 принципов архитектуры ПО для старта проекта](https://habr.com/ru/articles/888266/)
  - March 2025 • habr.com • [translated](https://habr-com.translate.goog/ru/articles/888266/?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=ru&_x_tr_pto=wapp)
  - Uses Liam ERD as an example of self-descriptive code and architecture principles
  - Shows how auto-generating interactive database documentation creates living architecture documentation
- [Top 4 Free, Open Source Database Schema Diagram Tools to Visualize Database Easier in 2025](https://www.bytebase.com/blog/top-database-schema-diagram-tools/)
  - May 2025 • bytebase.com
  - Compares Liam ERD with DrawDB, ChartDB, and Azimutt as top open-source schema visualization tools
  - Highlights Liam ERD's zero-setup approach for Rails, Prisma, and PostgreSQL schemas

## Contributing

Have you written about Liam ERD? We'd love to add your content to this list! Please submit a PR with the following information:

- Title of your article/resource
- Publication date (Month YYYY), domain, translated version(if not in English)
- Brief description (1-3 lines)


---
title: 20241003 - Use CSS Modules for Styling
---

## Status

- [ ] Proposed
- [x] Accepted
- [ ] Rejected
- [ ] Deprecated
- [ ] Superseded

## Context

Frontend styling approaches significantly impact development efficiency, maintainability, and performance. Our project required a styling solution that meets the following requirements:

1. **Design System Consistency** - Effective management of design tokens for a unified UI
2. **Implementation Efficiency** - Leveraging Figma style export to accelerate development
3. **AI Utilization** - Maximizing AI-assisted coding support
4. **Code Quality** - Preventing conflicts through scoped styling

We considered the following options to meet these requirements:

- CSS Modules
- Tailwind CSS
- CSS in JS

## Decision

We have decided to adopt CSS Modules as our styling approach. The key factors behind this decision are:

### 1. Stability and Future-Proofing with Standard Technologies

- **Standard CSS Specification** - Most stable styling approach using direct CSS
- **Immediate Access to New CSS Features** - Seamless utilization of features like CSS Variables
- **Tool Chain Compatibility** - Standard support in major bundlers (Webpack, Vite)
- **Technology Longevity** - Long-term viability independent of frameworks

### 2. Efficient Design System Implementation

- **Consistent Design Token Management** - Global variable management via CSS Variables
- **Direct Figma Integration** - Efficient conversion from design to CSS
- **Easy Theme Switching** - Simplified dark mode implementation through variable changes
- **Rapid Design Change Propagation** - Changes in one place affect the entire system

### 3. Team Development Optimization

- **Parallel Development via Component Isolation** - Independent work areas through scoping
- **Automatic Name Collision Avoidance** - Component-level CSS modularization
- **Minimal Learning Curve** - Development possible with standard CSS knowledge only
- **Code Review Simplification** - Improved readability through separation of styles and logic

### 4. High Compatibility with AI-Assisted Coding

- **High-Precision Generation Based on Standard Specifications** - Improved code generation accuracy
- **Abundant Reference Information** - Leveraging global information as learning data
- **Predictable Structure** - Streamlined debugging and modifications
- **Adaptation to Future AI Technology Evolution** - Continuous improvement expected for standard technologies

### 5. Balance Between Performance and Quality

- **Build-Time Optimization** - Reduced runtime overhead
- **Easy Code Splitting** - Component-level code splitting
- **Caching Strategy** - Efficient caching design
- **Bundle Size Optimization** - Automatic elimination of unnecessary styles

## Consequences

### Positive Impacts

1. **Design Quality and Consistency**

   - UI unification through design tokens
   - Visual coherence between components
   - Simplified multi-theme support

2. **Development Productivity**

   - Accelerated implementation from direct Figma export
   - Enhanced development efficiency through AI assistance
   - Parallel development at component level

3. **Code Quality**

   - Prevention of style conflicts through scoping
   - Clear responsibility separation at component level
   - Predictable style application

4. **Technical Stability**

   - Long-term maintainability through standard technologies
   - Tool chain compatibility
   - Performance optimization

### Negative Impacts

1. **UI Component Library**

   - Lack of compatibility with Tailwind CSS-based libraries (shadcn/ui, etc.)
   - Development and maintenance costs of custom UI component libraries
   - Additional effort for third-party integration

2. **Style Sharing and Global Management**

   - Complexity in designing shared styles between components
   - Need for special management of global styles
   - Construction of style override strategies

3. **Team Adaptation Costs**

   - CSS Modules convention adoption
   - Promoting understanding of the design token system
   - Maintaining consistent implementation style

### Technical Flexibility

1. **Possibility of Gradual Evolution**

   - Room for combining with other technologies in the future
   - Tolerance for different approaches in specific components
   - Natural adaptation to standard CSS evolution

2. **Development Environment Interoperability**

   - Integration with TypeScript
   - Standard integration with CI/CD environments
   - Compatibility with existing tool chains


---
title: 20241112 - Use React Flow for ERD Visualization
---

## Status

- [ ] Proposed
- [x] Accepted
- [ ] Rejected
- [ ] Deprecated
- [ ] Superseded

## Context

For our Entity Relationship Diagram (ERD) implementation, we needed a solution that meets the following requirements:

- Ability to render 100+ tables efficiently
- Interactive navigation features (pan, zoom)
- Programmatic focus on specific nodes
- Auto-formatting capabilities for table layouts

Two main implementation approaches were considered:

1. Using React Flow (@xyflow/react)
2. Custom implementation using Canvas API

Performance with large datasets was a primary concern, as was the ability to deliver a smooth user experience with features like panning, zooming, and automatic layout optimization.

## Decision

We have decided to adopt React Flow as our ERD visualization engine.

The decision was based on successful performance testing showing that React Flow can handle 100+ tables with acceptable rendering performance. While we initially experienced some performance issues with edge animations (using stroke-dasharray), we were able to optimize this by implementing custom animated edges using SVG elements.

React Flow also satisfies our UX requirements:

- Built-in pan and zoom functionality
- Support for programmatic node focusing
- Integration with ELK.js for automatic layout formatting

## Consequences

### Positive

- Faster development time compared to building a custom Canvas-based solution
- Built-in support for essential features like pan, zoom, and node selection
- Strong TypeScript support ensuring type safety across our implementation
- Active community and robust documentation
- Extensible architecture allowing for custom nodes and edges
- Performance optimization capabilities when needed (as demonstrated with our edge animation improvements)

### Negative

- We depend strongly on React Flow and call the API directly
  - But even if the React Flow is not optimal for us, we have decided that it is faster to rewrite everything.
- Some performance tuning was necessary for optimal results with large datasets
- Limited by React Flow's architecture and update cycle for future features

### Neutral

- Team needed to learn React Flow's API and concepts
- Integration with ELK.js for auto-layout required additional development effort, but would have been necessary regardless of the chosen solution

Our implementation has proven that React Flow, with appropriate optimizations, can efficiently handle our ERD visualization needs while providing an excellent user experience.


---
title: 20241128 - Use Fumadocs for Documentation Site
---

## Status

- [ ] Proposed
- [x] Accepted
- [ ] Rejected
- [ ] Deprecated
- [ ] Superseded

## Context

The Liam project required a documentation site framework that would provide a high-quality developer and user experience while being cost-effective and maintainable by the team. We needed a solution that would:

- Support static site generation for optimal performance
- Provide robust search capabilities
- Allow for customization and extension
  - As a design-focused team, we wanted the ability to create impactful UI components for our documentation
- Offer a good developer experience for both code contributors and documentation writers

We evaluated several popular documentation frameworks to determine the best fit for our needs, focusing on [Fumadocs](https://fumadocs.vercel.app/), [Nextra](https://nextra.site/), and [Mintlify](https://mintlify.com/).

## Decision

We have decided to adopt Fumadocs as our documentation site framework.

The decision was based on a comprehensive evaluation of multiple factors:

### Feature Comparison

| Feature                    | Fumadocs                               | Nextra               | Mintlify                               |
| -------------------------- | -------------------------------------- | -------------------- | -------------------------------------- |
| Static Generation          | ✅                                     | ✅                   | ✅                                     |
| Caching                    | ✅                                     | ✅                   | ✅                                     |
| Light/Dark Mode            | ✅                                     | ✅                   | ✅                                     |
| Syntax Highlighting        | ✅                                     | ✅                   | ✅                                     |
| Table of Contents          | ✅                                     | ✅                   | ✅                                     |
| Full-Text Search           | ✅ Free, with Algolia migration option | ✅ FlexSearch only   | ✅                                     |
| Internationalization       | ✅                                     | ✅                   | ✅                                     |
| Last Edit Date             | ✅                                     | ✅                   | ✅                                     |
| Page Icons                 | ✅                                     | ✅                   | ✅                                     |
| React Server Components    | ✅ App Router support                  | ❌ Pages Router only | - (Users manage MDX only)              |
| Remote Sources             | ✅                                     | ✅                   | ✅                                     |
| SEO                        | ✅                                     | ✅                   | ✅                                     |
| Built-in Components        | ✅ Rich variety                        | ✅                   | ❌ Limited                             |
| Custom Components          | ✅                                     | ✅                   | ❌                                     |
| OpenAPI Integration        | ✅                                     | ❌                   | ✅                                     |
| TypeScript Docs Generation | ✅                                     | ❌                   | ❌                                     |
| TypeScript Twoslash        | ✅                                     | ✅                   | ❌                                     |
| Styling                    | ✅ Flexible                            | ✅ Flexible          | ❌ Paid for custom CSS/JS              |
| Web Editor                 | ❌                                     | ❌                   | ✅ In development                      |
| Thumbs up feedback         | ❌                                     | ❌                   | ✅                                     |
| Pricing                    | ✅ Free                                | ✅ Free              | ❌ $150/month + $100/month for preview |

### Key Adoption Factors

- **Cost Efficiency**: Fumadocs is open-source and free to use, aligning with our resource constraints
- **Design Quality**: Provides high-quality UI components with minimal code requirements
- **Functionality**: Offers comprehensive features needed for Liam documentation (full-text search, built-in components, theme switching)
- **Extensibility**: Being Next.js-based allows for extension with custom React components
- **Hosting and Operations**: Leverages our team's existing Next.js expertise for hosting and maintenance
- **Marketing Opportunity**: Potential visibility through listing on the [Fumadocs showcase](https://fumadocs.vercel.app/showcase)

### Why Other Options Were Not Selected

- **Mintlify**:

  - Prohibitively expensive pricing model
  - Usage-based billing for AI chat features beyond 250 queries with no apparent way to disable
  - Slower deployment process

- **Nextra**:
  - Lacks App Router and React Server Components support
  - Limited search capabilities (FlexSearch only)
  - Fumadocs addresses these limitations while maintaining Nextra's positive attributes

## Consequences

### Positive

- No licensing fees, reducing project costs
- Modern documentation site with excellent UX for our users
- Integration with our existing Next.js knowledge and tooling
- Flexibility to customize and extend as our documentation needs evolve
- App Router and RSC support enables better performance and development experience
- Rich built-in components accelerate documentation creation

### Negative

- Tailwind-based styling differs from Liam's [CSS Modules approach](https://liambx.com/docs/contributing/adr/20241003-use-css-modules-for-styling), creating some inconsistency
- Requires self-hosting and domain management (though this is also an advantage for control)
- As a newer framework, the community and ecosystem are smaller than more established alternatives

### Neutral

- Regular maintenance will be required to keep the documentation site updated
- The team will need to develop expertise in Fumadocs-specific features and configurations


---
title: 20241128 - Use libpg-query for PostgreSQL SQL Parsing
---

## Status

- [ ] Proposed
- [x] Accepted
- [ ] Rejected
- [ ] Deprecated
- [ ] Superseded


## Context

We needed a robust and efficient SQL parser for PostgreSQL to integrate into our system. Several parsing options were evaluated, including Azimutt, ANTLR, and ``libpg-query``. While each had its advantages, we prioritized parsing accuracy, performance, and maintainability.

## Decision

We chose to use ``pg-query-emscripten``, which is a WebAssembly-compiled version of ``libpg-query``. This decision was based on the following factors:

**Parsing Speed**: Benchmarks showed that ``libpg-query`` (including its WebAssembly variant) outperformed alternatives like ANTLR in terms of parsing speed.

**PostgreSQL Compatibility**: Since ``libpg-query`` is based on PostgreSQL's internal parser, it provides the highest accuracy and support for PostgreSQL-specific syntax and extensions.

## Consequences

### Positive Impacts

- High Accuracy: Ensures correct parsing of PostgreSQL-specific syntax.
- Performance: Faster than alternatives, reducing query parsing overhead.
- Lightweight & Portable: WebAssembly eliminates the need for native binaries.

### Negative Impacts

- Limited to PostgreSQL: Unlike ANTLR, which supports multiple SQL dialects, ``libpg-query`` is PostgreSQL-specific.
- WebAssembly Dependency: Requires handling WebAssembly execution within our environment.

### Neutral Impacts

- Potential for Future Expansion: While ANTLR remains a viable option for broader SQL dialect support, ``libpg-query`` meets our immediate PostgreSQL needs effectively.


---
title: 20241203 - Use Prism for schema.rb Parsing
---

## Status

- [ ] Proposed
- [x] Accepted
- [ ] Rejected
- [ ] Deprecated
- [ ] Superseded

## Context

As part of our [Node.js-based unified database schema parsing approach](https://liambx.com/docs/contributing/adr/20241206-node-js-based-unified-db-schema-parsing), Liam needs a specific parser for Ruby's `schema.rb` files. This ADR focuses on the selection of the appropriate Ruby parser that would work within our Node.js environment.

We evaluated several parser options to determine the best fit for our needs, focusing primarily on [PEG.js](https://github.com/pegjs/pegjs) and [Prism](https://github.com/ruby/prism).

## Decision

We have decided to adopt Prism as our parser for processing Ruby's `schema.rb` files.

The decision was based on a comprehensive evaluation of multiple factors:

### Parser Comparison

| Feature                 | Prism                                     | PEG.js                                    |
| ----------------------- | ----------------------------------------- | ----------------------------------------- |
| Parser Type             | Dedicated Ruby parser                     | General parser generator                  |
| Development             | By Shopify for Ruby                       | Community-maintained general tool         |
| Error Tolerance         | ✅ High (designed for editor integration) | ❌ Limited                                |
| Learning Curve          | ✅ Low for TypeScript developers          | ❌ Higher (requires grammar definition)   |
| TypeScript Support      | ✅ Full TypeScript definitions            | ❌ Limited                                |
| AST Traversal           | ✅ Built-in Visitor pattern               | ❌ Manual implementation needed           |
| npm Package             | ✅ Available                              | ✅ Available                              |
| Ruby-specific Features  | ✅ Native understanding of Ruby syntax    | ❌ Requires custom grammar implementation |
| Implementation Effort   | ✅ Lower (ready to use for Ruby)          | ❌ Higher (needs custom grammar)          |
| Community/Documentation | ✅ Growing, backed by Shopify             | ✅ Established                            |

### Key Adoption Factors

#### 1. Development Efficiency

- **Lower Learning Curve**: Prism doesn't require writing custom grammar definitions
- **Intuitive for TypeScript Developers**: The AST can be manipulated directly using familiar TypeScript patterns
- **Ready for Ruby**: Built specifically for parsing Ruby code, eliminating the need for custom grammar development

#### 2. Type Safety

- TypeScript definitions provided out-of-the-box
- Enhanced IDE support with autocompletion and type checking
- Reduced risk of runtime errors through compile-time type checking

#### 3. Implementation Pattern Unification

- Using AST-based parsing approach creates consistency across the project
- Visitor pattern for AST traversal promotes clean, maintainable code
- Reusable patterns can be established for various parsing needs

#### 4. Contribution Accessibility

- Lower barrier to entry for TypeScript developers to contribute
- Clear documentation and typing makes the codebase more approachable
- Potential for contribution back to the Prism ecosystem

#### 5. Marketing Opportunities

- Novel use case of Prism in Node.js environments
- Potential for conference presentations (e.g., RubyKaigi)
- Opportunity to showcase Liam's innovative approach to Ruby tooling

## Consequences

For broader implications regarding our Node.js-based parsing approach, including WASM considerations and performance concerns, see the [Node.js-Based Unified DB Schema Parsing ADR](https://liambx.com/docs/contributing/adr/20241206-node-js-based-unified-db-schema-parsing).

### Prism-Specific Consequences

#### Positive

- Faster development cycle due to lower learning curve and TypeScript integration
- Improved code quality through static typing and better IDE support
- Lower barrier to entry for TypeScript developers to contribute

#### Negative

- Limited control over the underlying parser implementation compared to a custom grammar
- Dependency on Prism's development roadmap and feature support


---
title: 20241206 - Node.js-Based Unified DB Schema Parsing
---

## Status

- [ ] Proposed
- [x] Accepted
- [ ] Rejected
- [ ] Deprecated
- [ ] Superseded

## Context

Liam ERD needs to handle multiple database schema formats (e.g., PostgreSQL DDL, Rails `schema.rb`, and Prisma’s `schema.prisma`) and convert them into a unified internal structure for generating ER diagrams. The goal is to minimize user setup requirements—ideally enabling them to prepend a specific URL (for example, `liambx.com/erd/p/`) to a schema file’s location and immediately render an ER diagram.

Many existing ER diagram tools introduce high operational overhead or require multiple dependencies (Java runtime, Graphviz, or issuing DML commands on a database). To achieve the desired ease of use and portability, Liam ERD must avoid these external dependencies whenever possible.

However, multiple languages and formats complicate the parsing process. Each format may require its own parsing logic. Relying on multiple language runtimes (e.g., Ruby, PHP) increases complexity. The team prefers to keep the entire parsing and rendering workflow in a single environment—Node.js—potentially using WASM parsers to handle different schemas while avoiding extra runtime installations.

## Decision

We will perform all DB schema parsing within a Node.js environment. Specifically:

1. **Node.js as the Server-Side Runtime**

   - Use Node.js exclusively on the server side to perform schema parsing and ER diagram generation.
   - Deliver the rendered ER diagrams to the client via our web application (e.g., React Server Components) without running Node.js in the browser.

2. **Existing Parsers**

   - For Prisma schemas (`schema.prisma`), we can leverage existing Node.js libraries.
   - For Rails’ `schema.rb`, we plan to use [ruby/prism](https://github.com/ruby/prism) in a form that can run under Node.js (e.g., via WASM).
   - For PostgreSQL DDL, we aim to use a WASM-compatible SQL parser tailored to PostgreSQL syntax.

3. **Unified Data Model**

   - Map every schema format to a common `Schema` type so that subsequent ER diagram generation remains format-agnostic.

4. **Future Growth**
   - If new formats emerge, we will first look for a WASM or JavaScript solution. Only if absolutely necessary will we consider introducing new runtimes or custom parser generators.

## Consequences

- **Positive Impacts**

  - **Simplicity**: Users only need to modify the URL (e.g., prepend `liambx.com/erd/p/`) to get a rendered ER diagram, avoiding additional installs.
  - **Consistent Tech Stack**: All parsing and server-side code is in Node.js, reducing learning and operational overhead.
  - **Testability**: We can write tests using familiar tooling (e.g., Vitest), and integrate easily with CI/CD pipelines.
  - **Extensibility**: A unified internal model (`Schema`) makes it simpler to add support for new formats later.

- **Negative Impacts**

  - **Parser Maintenance**: Each new format or language feature may require a dedicated parser and integration work.
    - Even for unsupported formats or languages, users can migrate their schema to PostgreSQL and use `pg_dump` as a workaround for standardized parsing.
  - **WASM Overhead**: Using WASM-based parsers may incur performance overhead under high load or large schemas, although this is not currently a major concern.
  - **Dynamic Code Limitation**: Static parsing only - cannot execute dynamic features (variables, loops, conditionals) in schema files. Affects Rails' `schema.rb` and other ORM schemas that use runtime execution.


---
title: 20250116 - Use DMMF for Prisma Schema Parsing
---

## Status

- [ ] Proposed
- [x] Accepted
- [ ] Rejected
- [ ] Deprecated
- [ ] Superseded

## Context

We need to parse Prisma schema files to extract schema information. Several approaches were considered:

1. JavaScript parsers (e.g., Acorn, Babel)
2. Custom parsers using parser generators (e.g., PEG.js)
3. [DMMF (Data Model Meta Format)](https://github.com/prisma/prisma/blob/main/ARCHITECTURE.md#the-dmmf-or-data-model-meta-format) from @prisma/internals

Each approach has different implications for maintenance, accuracy, and development effort. JavaScript parsers were immediately ruled out as they are designed for parsing JavaScript code, not Prisma's custom schema format.

## Decision

We will use DMMF from @prisma/internals to parse Prisma schema files. DMMF is Prisma's internal representation format used for schema parsing and validation. Using DMMF is more reliable and accurate for parsing Prisma schema files compared to custom parsers.

Key factors in this decision:

- Official support from Prisma team
- Comprehensive parsing of models, fields, and relationships
- Built-in validation and type safety
- Automatic compatibility with future Prisma updates
- De facto stability demonstrated by widespread usage in the Prisma ecosystem

## Consequences

### Positive

- Reliable and accurate parsing of Prisma schemas
- Reduced development and maintenance effort (no need to implement custom parsers)
- Future-proof against Prisma syntax changes
- Proven stability through widespread usage in other tools
  - [Prisma Editor](https://github.com/mohammed-bahumaish/prisma-editor)
  - [Prisma ERD Visualizer](https://github.com/skn0tt/prisma-erd)
  - [prisma-uml](https://github.com/emyann/prisma-uml)
  - [DBML Generator](https://github.com/notiz-dev/prisma-dbml-generator)
  - [Prismaliser - Visualise your Prisma schema models and relations](https://prismaliser.app/)
  - [Prisma Editor - A powerful tool to visualize and edit Prisma Schema](https://github.com/mohammed-bahumaish/prisma-editor)

### Negative

- Additional dependency on @prisma/internals
- No official recommendation to use @prisma/internals
  - ref: [Discussion about @prisma/sdk rename to @prisma/internals](https://github.com/prisma/prisma/issues/13877)

### Neutral

- Need to transform DMMF output to match our internal schema format
- Learning curve for working with DMMF API


---
title: 20250205 - Use fuse.js
---

## Status

- [ ] Proposed
- [x] Accepted
- [ ] Rejected
- [ ] Deprecated
- [ ] Superseded

## Context

To implement the Command Palette (triggered via ⌘-K / Ctrl-K), there is a requirement to enable cross-searching of table names and column names within the ER diagram.

Liam ERD currently has both a web version (erd-web) and a CLI version. At least initially, we want them to operate on the same codebase. Therefore, the solution should run on the client side rather than relying on a Node.js runtime.

We considered the following technologies as the primary tools for browser-based search functionality:

1. fuse.js
2. Orama

In Pull Request [#652](https://github.com/liam-hq/liam/pull/652), we created a prototype search box using radix-ui (as used in fumadocs) and tested the speed and user experience using a benchmark project (mastodon/mastodon).

While Orama's pre-indexing offers high-speed performance, we observed that fuse.js, despite its O(n) search complexity, delivered comparable search speeds. Please refer to Pull Request [#652](https://github.com/liam-hq/liam/pull/652) for detailed performance metrics.

## Decision

We will adopt fuse.js, at least for the initial implementation.

See the positive aspects listed below for the rationale behind this decision.

## Proof of Concept (PoC) Summary

In our PoC (Pull Request [#652](https://github.com/liam-hq/liam/pull/652)), we compared **fuse.js** and **Orama** for client-side search functionality. The key findings are as follows:

> - **fuse.js** provides efficient fuzzy search capabilities out-of-the-box, without needing additional configuration for stemming or indexing.
> - **Orama** excels in search speed due to pre-built inverted indexes and offers O(1) search performance, while fuse.js operates with O(n) complexity.
> - Despite the theoretical performance differences, practical benchmarks on a project of Mastodon's scale (99 tables) revealed that both libraries deliver comparable performance, with search times ranging from 0.1ms to 1.0ms.
> - Orama's pre-indexing time was approximately 30ms-100ms, initiated upon loading the application. fuse.js does not require pre-indexing, leading to simpler integration and faster initial load times.
> - Both libraries successfully handled partial matches during live typing. However, Orama struggled with exact matches for incorrectly typed queries (e.g., "expire_at" instead of "expires_at").

Based on these observations, **fuse.js** was chosen for its ease of implementation and sufficient performance, especially for initial deployments where the data scale is moderate.

## Consequences

### Positive

- Simple configuration
- Enables easy fuzzy search
  - No need for stemming configuration

### Negative

- Search speed may become an issue when handling larger RDBMS table sizes
- Search performance might degrade when dealing with a more diverse set of searchable items


---
title: 20250421 - Apply RLS to All Tables with Organization-based Policies
---

## Status

- [ ] Proposed
- [x] Accepted
- [ ] Rejected
- [ ] Deprecated
- [ ] Superseded

## Context

We have considered enhancing the security of our application by leveraging Supabase's Row-Level Security (RLS) feature.  
Currently, the application accesses Supabase using the anon key on the client side, which poses a risk: tables without RLS enabled can be accessed regardless of user authentication.

Furthermore, the application is built with a multi-tenant architecture, requiring **authorization control based on organizations**.  
Many tables are several relationships away from the central organization_id. This creates a challenge where RLS policies must traverse multiple tables through foreign key relationships to reach the organization_id.

For example, a policy might need to include multiple nested SELECT statements to verify that a record belongs to the user's organization.

As a result, authorization policies can become complex. If RLS is only partially applied, alternative access paths through related tables may remain, creating potential security loopholes.

## Decision

**Enable Row-Level Security (RLS) on all tables** and apply **organization-based policies** by default.

- **Basic rules**:
  - Each authorized table must have an organization_id column used as a condition in the RLS policy
  - Authenticated users should only access data associated with their own organization

- **Tables requiring special treatment**:
  - Tables like user information, which may relate to multiple organizations, will have flexible, custom policies
  - For external service integrations (e.g., GitHub), where edits or deletions should be restricted, access will be limited to read-only as necessary

- **Backend exceptions**:
  - For system jobs or backend operations that do not go through user authentication, access beyond RLS restrictions will be needed. This will be handled by using specific authorization tokens or dedicated connection roles, allowing restricted access.

### Example Implementation

The projects table already implements this approach with the following policies:

```sql
-- Enable RLS on the projects table
ALTER TABLE "public"."projects" ENABLE ROW LEVEL SECURITY;

-- Policy for SELECT operations
CREATE POLICY "authenticated_users_can_select_org_projects" ON "public"."projects" 
FOR SELECT TO "authenticated" 
USING (("organization_id" IN ( 
  SELECT "organization_members"."organization_id"
  FROM "public"."organization_members"
  WHERE ("organization_members"."user_id" = "auth"."uid"())
)));

-- Similar policies exist for INSERT, UPDATE, and DELETE operations
```

These policies ensure that users can only access projects belonging to organizations they are members of, by joining through the organization_members table.

## Consequences

### Positive

- Greatly reduces security risks by consistently enforcing RLS on all tables
- Prevents configuration oversights at the table level and eliminates dependency on manual enforcement
- Unified authorization policy simplifies development and operations, improving implementation efficiency

### Negative

- **Increased implementation cost on the application side**:
  - All INSERT/UPDATE operations must accurately inject the correct organization_id
  - Requires architectural review and standardization to consistently access organization_id when needed

- Some tables will require custom policy design (e.g., external integrations, user data)

### Neutral

- Performance impact is expected to be minimal. PostgreSQL's RLS evaluation is relatively fast (milliseconds to tens of milliseconds), and deep JOINs are unlikely to pose significant issues


---
title: Architecture Decision Records
description: Documented architecture decisions for the Liam project
navigation: 2
---

import { source } from "../../../../lib/source"
import { DocsCategory } from "fumadocs-ui/page"

<DocsCategory page={source.getPage(["contributing", "adr"])} from={source} />


---
title: ADR Template
---

This template is based on [Documenting architecture decisions - Michael Nygard](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions).

Please include the following sections in each ADR file.

---