crudy
    FeaturesUse CasesDocumentationPricingAboutContact
    Get started free
    Documentation
    • Introduction
    • Installation
    • Quickstart
    • CLI Reference
    • Overview
    • Core Packages
    • App Structure
    • Modules & Routes
    • Module Anatomy
    • Backend Module
    • Frontend Module
    • Quotas
    • Full Example
    • Overview
    • Docker
    • Environment Variables
    • Licensing
    Modules

    Module Anatomy

    Inside a Crudy module: shape, files, lifecycle.

    A module is a single npm package with two entry points: one for the backend, one for the frontend. The app wires the backend half into createCrudyServer and the frontend half into CrudyProviders. The framework handles everything in between.

    On-disk layout

    plaintext
    my-module/
      package.json
      tsconfig.json
      src/
        backend/index.ts
        frontend/index.ts

    crudy module create <name> produces exactly this layout.

    package.json

    The package declares two entry points through the exports map:

    json
    {
      "name": "@crudy-modules/my-module",
      "version": "0.1.0",
      "type": "module",
      "exports": {
        "./backend": "./src/backend/index.ts",
        "./frontend": "./src/frontend/index.ts"
      },
      "peerDependencies": {
        "@crudy/backend": "latest",
        "@crudy/frontend": "latest"
      }
    }

    @crudy/backend and @crudy/frontend are peer dependencies. They are not bundled into the module; the host app provides them. This avoids version skew when multiple modules are installed.

    src/backend/index.ts

    Exports a single BackendModule constant. The minimum shape:

    ts
    import type { BackendModule } from '@crudy/backend';
     
    export const MyModuleBackend: BackendModule = {
        manifest: { id: 'my-module', name: 'My Module', version: '0.1.0' },
        migrations: [],
        routes: [],
    };

    The manifest.id is the canonical module identifier. It appears in the license claims, in app.modules, in app.org_modules, and in audit log entries. It must be stable across versions.

    src/frontend/index.ts

    Exports a single FrontendModule constant:

    ts
    import type { FrontendModule } from '@crudy/frontend';
     
    export const MyModuleFrontend: FrontendModule = {
        manifest: { id: 'my-module', name: 'My Module', version: '0.1.0' },
        navItems: [],
        routes: [],
        settingsTabs: [],
    };

    The two halves must agree on manifest.id. The framework uses it to correlate the backend module's enablement state with the frontend module's UI.

    Optional globals.css

    A module that ships custom Tailwind layers can include a globals.css and document that the host app imports it from its own app/globals.css:

    css
    @import "@crudy/frontend/globals.css";
    @import "@crudy-modules/my-module/globals.css";

    Each package owns its own Tailwind. Do not import tailwindcss in your module unless you also configure the layers correctly; in practice modules consume the framework's host UI primitives and skip styling entirely.

    How the two halves connect

    Nothing in the package itself glues the halves together. The app imports them separately:

    ts
    // backend/server.ts
    import { MyModuleBackend } from '@crudy-modules/my-module/backend';
    await createCrudyServer({ modules: [MyModuleBackend], appConfig });
    tsx
    // frontend/app/providers.tsx
    import { MyModuleFrontend } from '@crudy-modules/my-module/frontend';
    const MODULES = [MyModuleFrontend];

    The framework treats the matching manifest.id as the contract between the two halves.

    Lifecycle

    When the backend boots:

    1. The license is verified.
    2. The module's migrations are applied alongside other modules' migrations.
    3. app.modules and morbac.activities / morbac.views are upserted from the module's manifest and morbacRequirements.
    4. app.enable_audit is called for each entry in auditedTables.
    5. Routes are registered, gated by enablement and quota preHandlers.
    6. Background tasks are scheduled.

    When the frontend renders:

    1. CrudyProviders registers each module's nav items, settings tabs, slot contributions, and routes.
    2. The catch-all route resolves URLs against the registered route paths.
    3. Permission and license checks run on every render of a non-public page.

    Continue with Backend Module for the full field reference.

    PreviousModules & RoutesNext Backend Module
    crudy

    Build internal tools your teams actually use. Powered by a framework built for developers.

    Product

    • Features
    • Use Cases
    • Pricing

    Company

    • About
    • Contact

    © 2026 Crudy. All rights reserved.

    crudy.fr