AdminJS Documentation

repository·master·Indexed 26 days ago

https://github.com/softwarebrothers/adminjs

AdminJS is an automatic admin interface for Node.js applications (version 7.8.17) that generates a management UI based on provided database models. It provides CRUD operations, custom business actions, form validation, and a customizable dashboard. The framework allows developers to implement custom database, record, and resource adapters by extending base classes like BaseDatabase and BaseResource, and offers decorators for fine-tuning actions, properties, and resources.

Tokens
15.9K
Snippets
30
Records
101
Agent score
92%

What's inside AdminJS

  1. Overview of AdminJS

    master
    AdminJS is an automatic admin interface designed to be plugged into Node.js applications. By providing your database models (e.g., posts, comments, products), AdminJS automatically generates a UI that allows developers or trusted users to manage application content. It is inspired by tools like Django Admin and Rails Admin.
  2. Understand the AdminJS Data Model and Flattening

    master

    AdminJS uses a flattened data model for the params property in both the backend (BaseRecord#params) and the frontend (RecordJSON.params).

    Instead of storing raw nested objects or arrays, AdminJS converts them into a flat { [key: string]: any } structure using dot notation for nested properties and index notation for arrays. This approach ensures that updates to specific nested fields (e.g., via an ORM like Mongoose) do not accidentally overwrite entire parent objects, and it allows for efficient data transmission using the FormData format (which requires string-based keys).

  3. Key Features of AdminJS

    master

    AdminJS provides several core capabilities for managing application data and logic:

    • CRUD operations: Perform Create, Read, Update, and Delete actions on any data resource.
    • Custom actions: Implement specific business logic as actions on your resources.
    • Form validation: Automatically validate forms based on the schema defined in your resources.
    • Dashboard: A full-featured dashboard that supports custom widgets.
    • Resource decorators: Apply custom decorators to your resources to modify their behavior or appearance.
  4. Use the onChange callback in custom components

    master

    When building custom property type components for the edit or filter views, you can use the onChange callback to update the record's state. The onChange function accepts two different signatures:

    1. Single argument: Pass an entire RecordJSON object to replace the current record state.
    2. Two arguments: Pass the property.path (string) as the first argument and the new value as the second argument.

    This is useful for creating components that trigger changes to other properties on the same record (e.g., a button that updates a name field).

    import React from 'react'
    import { Button, Box } from '@adminjs/design-system'
    
    const ValueTrigger = (props) => {
      const { onChange, record } = props
    
      const handleClick = (): void => {
        // Passing the entire updated record as a single argument
        onChange({
          ...record,
          params: {
            ...record.params,
            name: 'my new name',
          },
        })
      }
    
      return (
        <Box mb="xxl">
          <Button type="button" onClick={handleClick}>Set Name</Button>
        </Box>
      )
    }
    
    export default ValueTrigger
  5. React dependency requirements for AdminJS v6

    master
    AdminJS v6 requires react and react-dom version v18.1.0+. If your project uses React outside of AdminJS, you must upgrade your project's React version to match. AdminJS v6 also uses styled-components version v5.3.5 to ensure compatibility with React 18.
  6. Override default property rendering logic

    master

    You can customize how specific properties are rendered in the AdminJS UI by passing a custom component to PropertyOptions. The BasePropertyComponent automatically selects components based on property type and context (list, edit, show, or filter), but you can override this behavior for specific properties.

    To override a component, define a components object within the property's configuration in your resource options. You can target specific UI contexts such as show, edit, list, or filter.

    const AdminJS = require('adminjs')
    const ResourceModel = require('./resource-model')
    const AdminJSOptions = {
      resources: [{
        resource: ResourceModel,
        options: {
          properties: {
            name: {
              components: {
                show: 'MyReactComponent',
              },
            },
          },
        },
      }],
    }
  7. Implement custom routing and authentication logic

    master

    If you are not using a standard plugin like @adminjs/express, you can manually implement the AdminJS router in your own HTTP framework.

    To do this, you must:

    1. Iterate through Router.routes and map them to your framework's routing system. Note that you must convert AdminJS path parameters from {param} syntax to your framework's syntax (e.g., :param for Express).
    2. Instantiate the route.Controller with the admin instance and the currentAdmin (the authenticated user).
    3. Call the controller method corresponding to route.action, passing the request, params, query, and a payload containing fields and files.
    4. Iterate through Router.assets to serve static files using the provided asset.src path.
    5. Handle authentication by wrapping the route handlers with your own logic (e.g., checking a session).
    const { Router } = require('adminjs')
    const { routes, assets } = Router
    const router = new express.Router()
    
    // 1. Handle Routes
    routes.forEach((route) => {
      // Convert {param} to :param for Express
      const expressPath = route.path.replace(/{/g, ':').replace(/}/g, '')
      
      const handler = async (req, res, next) => {
        try {
          // Implement authentication logic here
          const currentAdmin = null 
    
          const controller = new route.Controller({ admin }, currentAdmin)
          const { params, query } = req
          const method = req.method.toLowerCase()
          const payload = {
            ...(req.fields || {}),
            ...(req.files || {}),
          }
          
          const html = await controller[route.action]({
            ...req,
            params,
            query,
            payload,
            method,
          }, res)
    
          if (route.contentType) {
            res.set({ 'Content-Type': route.contentType })
          }
          if (html) {
            res.send(html)
          }
        } catch (e) {
          next(e)
        }
      }
    
      if (route.method === 'GET') router.get(expressPath, handler)
      if (route.method === 'POST') router.post(expressPath, handler)
    })
    
    // 2. Handle Assets
    assets.forEach((asset) => {
      router.get(asset.path, async (req, res) => {
        res.sendFile(path.resolve(asset.src))
      })
    })
  8. Update AdminJS to v6

    master

    To upgrade to version 6, use npm to install the latest version of adminjs. This will update both adminjs and adminjs-design-system to the newest beta versions. If you have adminjs-design-system explicitly listed in your dependencies, ensure you update it accordingly.

    npm install adminjs
  9. Populate references using the populator utility

    master

    The populator utility from adminjs is used to automatically populate all fields marked as 'reference' (via PropertyOptions#reference) within a set of records. This is particularly useful when creating custom action handlers where you need to retrieve data directly from the database and want the returned JSON to include the full objects for referenced entities instead of just their IDs.

    const { populator } = require('adminjs')
    
    // action handler for showing product with categories
    const showProductsHandler = async (request, response, context) => {
      const { payload } = request
      const { _admin, currentAdmin } = context
      const ProductResource = _admin.findResource('Product')
    
      const product = await ProductResource.findOne()
      // product.populated is empty
    
      const [populatedProduct] = await populator([product])
      // populatedProduct.populated - has a categoryId filled with Category params
    
      return {
        record: record.toJSON(currentAdmin) // returns RecordJSON with populated field as well
      }
    }
  10. Define and customize AdminJS Actions

    master

    Actions in AdminJS can be applied to an entire resource, a specific record, or a set of selected records (bulk). You can create new actions or override existing built-in actions (like new, edit, show, delete, etc.) within the options.actions object of a resource configuration.

    Action Types:

    • resource: Performed for an entire resource (e.g., new, list, search).
    • record: Invoked for a single record (e.g., edit, show, delete).
    • bulk: Invoked for a set of records (e.g., bulkDelete).

    Built-in Actions:

    • show, edit, list, delete, bulkDelete, new, search.
    const AdminJSOptions = {
      resources: [{
        resource: User,
        options: {
          actions: {
            // Overriding an existing action
            new: {
              icon: 'Add'
            },
            // Creating a new resource action
            myNewAction: {
              actionType: 'resource',
              handler: async (request, response, context) => {
                // implementation
              }
            }
          }
        }
      }]
    }
  11. RichText input changes in AdminJS v6

    master
    AdminJS v6 has migrated the RichText implementation from Quill to TipTap due to security and support requirements. As a result, all previous Quill-related configurations are no longer valid and should be removed or updated to the new TipTap-based implementation.