<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jean-David Bonicel</title>
    <description>The latest articles on DEV Community by Jean-David Bonicel (@jeandavid_bonicel_32a0b2).</description>
    <link>https://dev.to/jeandavid_bonicel_32a0b2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3883913%2F04a90c5f-a2e6-49e8-bafd-b0ccc398a865.jpg</url>
      <title>DEV Community: Jean-David Bonicel</title>
      <link>https://dev.to/jeandavid_bonicel_32a0b2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeandavid_bonicel_32a0b2"/>
    <language>en</language>
    <item>
      <title>I built a CLI that scaffolds complete multi-tenant SaaS apps</title>
      <dc:creator>Jean-David Bonicel</dc:creator>
      <pubDate>Fri, 17 Apr 2026 07:39:27 +0000</pubDate>
      <link>https://dev.to/jeandavid_bonicel_32a0b2/i-built-a-cli-that-scaffolds-complete-multi-tenant-saas-apps-5fl</link>
      <guid>https://dev.to/jeandavid_bonicel_32a0b2/i-built-a-cli-that-scaffolds-complete-multi-tenant-saas-apps-5fl</guid>
      <description>&lt;p&gt;After building the same multi-tenant platform architecture over and over -- React shell, micro-frontends, Spring Boot backends, API gateway, shared UI kit, tenant isolation, auth -- I decided to automate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;apps-generator&lt;/strong&gt; is a Python CLI that scaffolds a complete full-stack tenant app from a few commands. You describe your resources, and it generates everything: backend CRUD with tenant isolation, typed API client, data-fetching frontend pages with charts, Docker Compose, Kubernetes manifests, and CI/CD pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it generates
&lt;/h2&gt;

&lt;p&gt;6 templates that wire together automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;platform-shell&lt;/strong&gt; -- React host app with Module Federation, Clerk/OIDC auth, org switcher, i18n (EN/FR)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;frontend-app&lt;/strong&gt; -- React micro-frontends with data-aware pages (list tables, create forms, dashboards with Recharts)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;api-domain&lt;/strong&gt; -- Spring Boot 3 backends with DDD architecture, PostgreSQL, Hibernate tenant filter, CRUD from resource schema&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;api-gateway&lt;/strong&gt; -- Spring Cloud Gateway with JWT validation, tenant header forwarding, correlation IDs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;api-client&lt;/strong&gt; -- Typed TypeScript fetch client shared across all MFEs, with auto-generated types from resource schema&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ui-kit&lt;/strong&gt; -- 26 shadcn/ui components + Recharts charts + Storybook&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate infrastructure&lt;/span&gt;
appgen generate ui-kit &lt;span class="nt"&gt;-o&lt;/span&gt; ./ui-kit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-ui-kit
appgen generate api-client &lt;span class="nt"&gt;-o&lt;/span&gt; ./api-client &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-api-client
appgen generate api-gateway &lt;span class="nt"&gt;-o&lt;/span&gt; ./gateway &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-gateway &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;basePackage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.example.gateway

&lt;span class="c"&gt;# Generate a backend with CRUD resources&lt;/span&gt;
appgen generate api-domain &lt;span class="nt"&gt;-o&lt;/span&gt; ./product-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;product-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;basePackage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.example.products &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'resources=[{
    "name": "product",
    "fields": [
      {"name": "name", "type": "string", "required": true},
      {"name": "price", "type": "decimal", "required": true},
      {"name": "stock", "type": "integer"}
    ]
  }]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--gateway&lt;/span&gt; ./gateway &lt;span class="nt"&gt;--api-client&lt;/span&gt; ./api-client

&lt;span class="c"&gt;# Generate the shell + a micro-frontend with pages&lt;/span&gt;
appgen generate platform-shell &lt;span class="nt"&gt;-o&lt;/span&gt; ./shell &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-platform &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--uikit&lt;/span&gt; ./ui-kit &lt;span class="nt"&gt;--api-client&lt;/span&gt; ./api-client

appgen generate frontend-app &lt;span class="nt"&gt;-o&lt;/span&gt; ./products &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;products &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'pages=[
    {"path":"dashboard","label":"Dashboard","resource":"product","type":"dashboard"},
    {"path":"list","label":"Products","resource":"product","type":"list"},
    {"path":"new","label":"New Product","resource":"product","type":"form"}
  ]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--shell&lt;/span&gt; ./shell &lt;span class="nt"&gt;--uikit&lt;/span&gt; ./ui-kit &lt;span class="nt"&gt;--api-client&lt;/span&gt; ./api-client

&lt;span class="c"&gt;# Generate Docker Compose and start everything&lt;/span&gt;
appgen docker-compose &lt;span class="nb"&gt;.&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost&lt;/code&gt; and you have a working multi-tenant app with auth, CRUD, tenant isolation, charts, and i18n.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it different
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tenant isolation at the ORM level
&lt;/h3&gt;

&lt;p&gt;Every entity extends &lt;code&gt;TenantAwareEntity&lt;/code&gt; which has a Hibernate &lt;code&gt;@Filter&lt;/code&gt; that automatically adds &lt;code&gt;WHERE tenant_id = :tenantId&lt;/code&gt; to every query. Even &lt;code&gt;findAll()&lt;/code&gt; is tenant-scoped. You cannot leak data across tenants.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resource schema is the single source of truth
&lt;/h3&gt;

&lt;p&gt;You define fields once in JSON. The CLI generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Java entity with JPA annotations&lt;/li&gt;
&lt;li&gt;Spring Data repository with tenant-scoped queries&lt;/li&gt;
&lt;li&gt;Service layer with CRUD operations&lt;/li&gt;
&lt;li&gt;REST controller with validation&lt;/li&gt;
&lt;li&gt;Create/Update/Response DTOs with Bean Validation&lt;/li&gt;
&lt;li&gt;Liquibase database migration&lt;/li&gt;
&lt;li&gt;Integration test with Testcontainers&lt;/li&gt;
&lt;li&gt;TypeScript interfaces in the shared API client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Backend and frontend types match by construction -- no manual sync needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data-aware pages
&lt;/h3&gt;

&lt;p&gt;When you specify &lt;code&gt;"type": "list"&lt;/code&gt; or &lt;code&gt;"type": "form"&lt;/code&gt; on a page, the generator creates a real component with data fetching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;List pages&lt;/strong&gt; get a shadcn Table with pagination and &lt;code&gt;useQuery&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form pages&lt;/strong&gt; get typed inputs with validation and &lt;code&gt;useMutation&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboard pages&lt;/strong&gt; get stat cards and Recharts bar charts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Everything wires together
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--shell&lt;/code&gt; registers MFEs in the shell's &lt;code&gt;remotes.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--gateway&lt;/code&gt; registers routes in the gateway's &lt;code&gt;routes.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--uikit&lt;/code&gt; links the shared component library with the Tailwind theme&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--api-client&lt;/code&gt; generates TypeScript types and links the shared fetch client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No manual configuration between projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shell&lt;/td&gt;
&lt;td&gt;React 18, Module Federation, Clerk/OIDC, i18next&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MFEs&lt;/td&gt;
&lt;td&gt;React 18, Vite, TanStack Query, TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI Kit&lt;/td&gt;
&lt;td&gt;26 shadcn/ui components, Recharts, Tailwind CSS, Storybook&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gateway&lt;/td&gt;
&lt;td&gt;Spring Cloud Gateway, JWT, correlation IDs, security headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Spring Boot 3, JPA/Hibernate, PostgreSQL, Liquibase, Testcontainers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infra&lt;/td&gt;
&lt;td&gt;Docker Compose, Kubernetes (Kustomize), GitHub Actions CI/CD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Built-in security
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Correlation ID tracing across the full stack (shell -&amp;gt; gateway -&amp;gt; backend)&lt;/li&gt;
&lt;li&gt;Security headers: CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy&lt;/li&gt;
&lt;li&gt;CORS configuration with environment-based allowed origins&lt;/li&gt;
&lt;li&gt;OAuth2/JWT validation with dev escape hatch (&lt;code&gt;@Profile("local")&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Structured error responses with correlation IDs for debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open source
&lt;/h2&gt;

&lt;p&gt;The project is GPL v3 and on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/jeandbonicel/apps-generator" rel="noopener noreferrer"&gt;https://github.com/jeandbonicel/apps-generator&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;89+ automated tests&lt;/li&gt;
&lt;li&gt;Full EN/FR i18n support&lt;/li&gt;
&lt;li&gt;Comprehensive docs covering architecture, security, multi-tenancy, and deployment&lt;/li&gt;
&lt;li&gt;Contribution guide with PR templates and CI on every PR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build multi-tenant SaaS apps and are tired of scaffolding the same architecture every time, give it a try. Feedback and contributions welcome.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>react</category>
      <category>java</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
