AdminForth Documentation

repository·main·Indexed 19 days ago

https://github.com/devforth/adminforth

An agent-first TypeScript framework for building admin panels. AdminForth allows developers to convert databases (Postgres, MySQL, Mongo, SQLite, Clickhouse) into functional UIs with CRUD capabilities, filtering, and built-in plugins for AI assistance and audit logs. It includes a CLI for scaffolding apps, resources, and custom Vue components, as well as tools for managing database migrations via Prisma.

Tokens
174.7K
Snippets
550
Records
618
Agent score
64%

What's inside AdminForth

  1. How to work without a direct database connection

    main

    By default, AdminForth connects directly to databases like PostgreSQL, MySQL, ClickHouse, or MongoDB. However, you can decouple AdminForth from your database by routing all read and write operations through an external API layer (such as REST, GraphQL, or JSON-RPC).

    Why use an API-backed approach?

    • Enforced Constraints: Your API can enforce custom validation rules that exist outside the database schema.
    • Advanced Auditing: While AdminForth has a built-in AuditLog for data modifications, using an API allows you to log all operations, including read operations, via your own logging systems.
    • Complex Logic: You can trigger distributed workflows or complex business logic during data mutations that the database layer alone cannot handle.
    • Unsupported Databases: If you need to use a database not natively supported by AdminForth, you can implement a custom adapter.

    Implementation Strategy

    To implement this, you must extend the AdminForth data connector class and implement the specific methods responsible for data access (reads) and mutations (writes). This effectively turns AdminForth into a consumer of your API rather than a direct database client.

  2. Understand the AI Completion Adapter interface

    main

    All AI completion adapters in AdminForth implement the CompletionAdapter interface, ensuring a consistent way to interact with different AI models (OpenAI, Gemini, etc.). The core method is complete(), which accepts a configuration object for text generation, structured output, and streaming.

    Key parameters for complete():

    • content: The prompt string.
    • maxTokens: Maximum number of tokens to generate.
    • outputSchema: (Optional) Defines a JSON schema for structured responses.
    • reasoningEffort: (Optional) Controls reasoning depth ('none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'). Defaults to 'low'.
    • tools: (Optional) Array of CompletionTool objects.
    • onChunk: (Optional) A callback function for streaming responses and reasoning events.
    complete({
      content: string,
      maxTokens: number,
      outputSchema?: any,
      reasoningEffort?: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh',
      tools?: CompletionTool[],
      onChunk?: (
        chunk: string,
        event?: {
          type: 'output' | 'reasoning';
          delta: string;
          text: string;
          source?: 'summary' | 'text';
        },
      ) => void | Promise<void>,
    })
  3. Understand npm pre-release versions

    main

    Pre-release versions allow you to distribute experimental features or fixes to a subset of users without affecting the stable latest version.

    In npm, if you have version 1.2.3:

    • npm version patch bumps to 1.2.4.
    • npm version prerelease --preid=next bumps to 1.2.4-next.0.
    • Subsequent runs of npm version prerelease --preid=next result in 1.2.4-next.1, 1.2.4-next.2, etc.

    When you are ready to move from a pre-release to a stable version, running npm version patch (or minor/major) on the pre-release version will release it as a stable version (e.g., 1.2.4) without further incrementing the patch number.

    Users can install these versions specifically using the @ syntax: npm install <package-name>@next.

  4. Control injection order using afOrder

    main

    While most injections accept an array where the order is determined by the array index, you can dynamically control the rendering order using the meta.afOrder property.

    How it works:

    • The higher the afOrder value, the earlier the component is rendered.
    • This is particularly useful for plugin developers who want to allow users to position plugin-provided components within an existing injection list.
    // Using afOrder to control rendering sequence
    {
      customization: {
        globalInjections: {
          userMenu: [
            {
              file: '@@/CustomUserMenuItem.vue',
              meta: { afOrder: 10 } // Renders first
            },
            {
              file: '@@/AnotherCustomUserMenuItem.vue',
              meta: { afOrder: 20 } // Renders second
            },
            {
              file: '@@/LastCustomUserMenuItem.vue',
              meta: { afOrder: 5 } // Renders last
            },
          ]
        }
      }
    }
  5. Automate releases with semantic-release

    main

    Instead of manually updating CHANGELOG.md, bumping versions in package.json, and creating Git tags, you can use semantic-release to automate the entire lifecycle via CI/CD.

    semantic-release works by analyzing commit messages to determine the next release type:

    • feat: ... triggers a minor release.
    • fix: ... triggers a patch release.

    Key benefits include:

    • Automatic Versioning: It reads the previous version from Git tags rather than editing package.json directly.
    • Automated Release Notes: It generates GitHub release notes and changelogs based on commit messages.
    • Automated Publishing: It publishes the new version to npm automatically.
    • Pre-release Support: It can manage a next branch for pre-releases without affecting the main branch's stable releases.
  6. Conditionally display columns using showIf

    main

    You can dynamically show or hide columns in forms and views based on the values of other fields using the showIf property. This supports equality, comparison, array, and logical operators.

    Logical Operators

    Use $and and $or to combine conditions. These must contain arrays of conditions.

    Comparison Operators

    • $gt: Greater than
    • $gte: Greater than or equal to
    • $lt: Less than
    • $lte: Less than or equal to
    • $eq: Equal to (default)
    • $not: Not equal to

    Array Operators

    Note: $includes and $nincludes should only be used on columns where isArray: { enabled: true } is set.

    • $in: Value is in array
    • $nin: Value is not in array
    • $includes: Array includes value
    • $nincludes: Array does not include value

    Example: Complex Logic

    export default {
      columns: [
        {
          name: 'premium_features',
          type: AdminForthDataTypes.JSON,
          showIf: {
            $and: [
              { price: { $gte: 500000 } },
              { apartment_type: { $in: ['penthouse', 'apartment'] } }
            ]
          }
        }
      ]
    }
  7. Implement bulk actions for multiple records

    main

    When performing operations on multiple selected records, you have two patterns:

    1. Using action with bulkButton (Parallel Execution)

    If you define showIn.bulkButton: true and only provide an action handler, AdminForth will execute that action function once per selected record in parallel using Promise.all. This is suitable for simple, independent tasks.

    2. Using bulkHandler (Batched Execution)

    For efficiency (e.g., a single SQL UPDATE for all IDs), define a bulkHandler. This is called once with all selected IDs. If both action and bulkHandler are present, bulkHandler is used for bulk operations and action is used for single-record operations.

    {
      name: 'Auto submit',
      // bulkHandler receives all recordIds in one call
      bulkHandler: async ({ recordIds, adminforth, resource }) => {
        await doSomethingBatch(recordIds);
        return { ok: true, successMessage: `Processed ${recordIds.length} records` };
      },
      // action is used for single-record show/edit buttons
      action: async ({ recordId }) => {
        await doSomething(recordId);
        return { ok: true, successMessage: 'Done' };
      },
      showIn: {
        bulkButton: true,
        showButton: true,
      }
    }
  8. Understand AdminForth core concepts

    main

    AdminForth is built around several key abstractions that map database structures to an administrative UI:

    • dataSource: A connection to a specific database (e.g., MySQL, Postgres, MongoDB) identified by a unique id and a standard URI url.
    • resource: A representation of a database table or collection. It links to a dataSource via its ID and defines which table it represents.
    • column / field: A representation of a database column. While column is used in the context of a resource definition, field is used when discussing the data within a specific record.
    • record: A single row in a relational database or a single document in a document database.
    • action: Operations performed on resources or records (e.g., create, edit, delete, list, show, filter).
    • adminUser: The object representing the currently authenticated user.
    • component: A Vue frontend component used to build custom pages or extend existing AdminForth UI elements.
    • Plugin: A class used to extend AdminForth by modifying its configuration, adding frontend components, or injecting backend hooks, helping to reduce boilerplate in the main config.
  9. Use Image Analysis Adapters for AI-powered insights

    main

    Image Analysis Adapters in AdminForth are used to integrate AI capabilities into plugins that need to process visual data. These adapters allow the system to:

    • Describe image content and scenes
    • Extract text from images (OCR)
    • Identify objects and people
    • Provide detailed visual insights

    The core interface is defined by the ImageVisionAdapter class.

  10. How the CRUD Approve workflow works

    main

    The plugin implements a manual gate for sensitive operations:

    1. Interception: A user attempts a create, edit, or delete on a protected resource.
    2. Hook Execution: The resource's beforeSave hook calls crudApprovePlugin.createApprovalRequest(...).
    3. Mutation Stop: The original database mutation is halted by returning an error message (e.g., return { ok: true, error: "Action sent for manual approval" }).
    4. Queueing: A pending request is stored in the approval resource.
    5. Review: A reviewer inspects the JSON diff in the approval queue.
    6. Resolution:
      • If Rejected: The request status changes to Rejected and nothing happens to the target record.
      • If Approved: The plugin automatically executes the original operation and marks the request as Approved.
  11. How AdminForth connectors work

    main

    Connectors in AdminForth are distributed as separate packages and are loaded automatically based on your datasource URL scheme. You do not need to manually instantiate or import a connector in your application configuration.

    AdminForth uses a peer dependency loading system: it detects the protocol in your datasource URL (e.g., sqlite://...) and attempts to import the corresponding package (e.g., @adminforth/connector-sqlite). Once loaded, the connector is automatically used for schema discovery and CRUD operations.

  12. Set up the AdminForth Agent plugin

    main

    The Agent plugin allows AI to interact with your back office. To use it, you must satisfy three requirements:

    1. Persistence Layer: Create and register sessions and turns tables/resources in your database to store chat history.
    2. Plugin Attachment: Attach the @adminforth/agent plugin to your adminuser resource.
    3. Completion Adapter: Configure a completion adapter (e.g., @adminforth/completion-adapter-openai-responses) and provide the necessary environment variables (e.g., OPENAI_API_KEY).