Resource

Resource is something that you can manage in AdminJS and it comes with CRUD actions (Create, Read, Update, Delete) provided out of the box.

Introduction

Resource is something that you can manage in AdminJS and it comes with CRUD actions (Create, Read, Update, Delete) provided out of the box. Most of the time it is a model from your ORM or ODM.

The idea of AdminJS is to allow you to manage resources of all kinds, be it your ORM/ODM models or your custom REST API endpoints if you decide to create an adapter for them.

Adapters

AdminJS allows you to define resources through adapters. Adapters are AdminJS extensions which provide the API to communicate with your ORM, ODM or any other kind of storage or API of your choice. All adapters must extend three base classes by implementing their methods:

  • BaseResource - it's responsible for CRUD operations on every resource (model) that your provide

  • BaseDatabase - it's responsible for loading all resources (models) defined in your database (if you choose to do so)

  • BaseProperty - it's responsible to describe your resource's (model's) attributes based on your model's metadata

The list of adapters that are officially supported by AdminJS can be found in Adapters section.

In order to use an adapter in your documentation you must first register it.

import AdminJS from 'adminjs'
import { Database, Resource } from '@adminjs/typeorm' // or any other adapter

AdminJS.registerAdapter({ Database, Resource })

You can register as many adapters as you need.

Passing resources to AdminJS

There are two options which you can choose to provide resources to AdminJS:

  1. Provide entire database connection

  2. Provide every resource explicitly

Providing entire database

This option allows you to provide an entire database and AdminJS will load all models that you have defined. However, this option may not be available for every adapter. The adapter must expose a connection or client which exposes the metadata of all models that it had loaded. @adminjs/objection is an example of an adapter which does not allow you to provide an entire database.

In order to provide an entire database for AdminJS to load, you must specify databases property when setting up your AdminJS instance. In case of @adminjs/mongoose it would look as follows:

const mongooseDb = await mongoose.connect('mongodb://localhost:27017/test')

const admin = new AdminJS({
  databases: [mongooseDb],
})

For other adapters the setup would be basically the same, you just have to pass your ORM/ODM connection (or a client).

Providing resources explicitly

This option requires you to define all resources explicitly. It is also the recommended approach since it allows you to customize every resource. In order to define resources, you must specify resources property when setting up your AdminJS instance. Example:

import User from './user.entity.js'
import Profile from './profile.entity.js'

// User and Profile are models defined in your ORM/ODM

const admin = new AdminJS({
  resources: [
    User, // you can simply pass a model
    {
      resource: Profile,
      options: { // or you can provide an object with your custom resource options
        id: 'profiles', // here the resource identifier has been renamed to "profiles"
      },
    },
  ],
})

The way you provide resources may differ between adapters. Make sure you read a detailed tutorial for an adapter which you are using.

Customizing resources

While AdminJS provides default CRUD for your application, you may want to further customize your resources. This can be done by using object definition of a resource and specifying the ResourceOptions. The example above is the simplest possible where we change the id of a resource to profiles. Below you will find several examples of resource customization.

Nesting a resource under collapsible navigation

This can be achieved by specifying navigation property in your resource's options, example:


const usersNavigation = {
  name: 'Users',
  icon: 'User',
}

const admin = new AdminJS({
  resources: [{
    resource: Profile,
    options: {
      navigation: usersNavigation,
    },
  }],
})

This will put the Profile resource under Users menu.

Changing visibility of properties

If you want specific properties be displayed or usable in a given action, there are two options to achieve this:

  1. Set isVisible option for every property

  2. Set listProperties, editProperties, filterProperties, showProperties in your resource

Setting `isVisible`

Every property that you have defined in your database model can be further customized in AdminJS. In this example we will hide bio property in list action and hide it from filters, but we will leave it enabled in show and edit:

const admin = new AdminJS({
  resources: [{
    resource: Profile,
    options: {
      properties: {
        bio: {
          isVisible: {
            edit: true,
            show: true,
            list: false,
            filter: false,
          },
        },
      },
    },
  }],
})

Settings lists of visible properties

The example with bio hidden in list and filter but visible in show and edit can also be achieved by setting listProperties, editProperties, filterProperties, showProperties

const admin = new AdminJS({
  resources: [{
    resource: Profile,
    options: {
      listProperties: ['id', 'name', 'createdAt'],
      filterProperties: ['id', 'name', 'createdAt'],
      editProperties: ['id', 'name', 'bio', 'createdAt'],
      showProperties: ['id', 'name', 'bio', 'createdAt'],
    },
  }],
})

The end result is the same but you should take note that this approach takes precedence over setting isVisible or property's position.

More examples of properties' customization can be found in Property section.

Customizing actions

Please refer to Action section for examples and explanation.

By default, when you press a resource link in the sidebar, it will navigate to resource's list action. This can be changed by configuring href option. Let's say we want users resource to open an already-filtered users list which show only users that have "active" status:

const UserResource = {
  resource: User,
  options: {
    id: 'users',
    href: ({ h, resource }) => {
      return h.resourceActionUrl({
        resourceId: resource.decorate().id(),
        actionName: 'list',
        params: {
          'filters.status': 'active',
        },
      })
    },
  },
}

Configuring sorting for the resource

By default, when you navigate to your resource's list action it will show you results in random order since the actual database query will be lacking sorting information (unless you use table UI to select a column to sort by). You can, however, define default sorting for your resource by specifying sort option:

const UserResource = {
  resource: User,
  options: {
    sort: {
      sortBy: 'updatedAt',
      direction: 'desc',
    },
  },
}

In the example above, we specified User resource to be sorted by it's updatedAt property with the latest records appearing at the top of the results list.

Resource translations

You can define translations for your resources by specifying locale. This is not done on resource-level though, but during the instantiation process of AdminJS.

Renaming a resource in sidebar

In order to rename your resource in the sidebar of the application, you have to set it's label:

const admin = new AdminJS({
  resources: [User],
  locale: {
    language: 'en',
    translations: {
      labels: {
        User: 'People',
      },
    },
  },
})

In the example above, the User resource in the sidebar has been renamed to People.

Another scenario would be where you would want to have different messages shown based on which resource the user is currently viewing. An example would be a message which appears when you enter a resource without any records to show in list action:

There are no records in this resource

In order to change it you have to define resource-specific translation as in the example below:

const admin = new AdminJS({
  resources: [User],
  locale: {
    language: 'en',
    translations: {
      resources: {
        User: {
          messages: {
            noRecordsInResource: 'There are no users to display'
          },
        },
      },
    },
  },
})

To see a list of all available locales and predefined translations, please visit locales in adminjs core repository.

Using "features"

Features are predefined pieces of code which you can import into your resource's features and they will be merged with the rest of it's configuration. An example of a feature is @adminjs/passwords. This is a package which handles passwords hashing in edit form and shows a corresponding UI.

Usage example:

import passwordsFeature from '@adminjs/passwords'
import argon2 from 'argon2'

import { User } from './user.entity.js'

const UserResource = {
  resource: User,
  features: [passwordsFeature({
    properties: { encryptedPassword: 'hashedPassword' },
    hash: argon2.hash,
  })],
}

Last updated