Search code examples
nestjsprismaadminjs

NoResourceAdapterError: There are no adapters supporting one of the resource you provided (AdminJs, NestJs and Prisma)


I followed the AdminJS documentation, on how to setup AdminJS(^6.8.6) and Prisma(^4.9.0) on NestJS(9.0.0):https://docs.adminjs.co/installation/adapters/prisma#nest.js

The getting the following error:

\node_modules\adminjs\lib\backend\utils\resources-factory\resources-factory.js:93
    return resources.map(rawResource => {
                     ^
NoResourceAdapterError: There are no adapters supporting one of the resource you provided
    at D:\task-manager\node_modules\adminjs\lib\backend\utils\resources-factory\resources-factory.js:99:15
    at Array.map (<anonymous>)
    at ResourcesFactory._convertResources (D:\task-manager\node_modules\adminjs\lib\backend\utils\resources-factory\resources-factory.js:93:22)
    at ResourcesFactory.buildResources (D:\task-manager\node_modules\adminjs\lib\backend\utils\resources-factory\resources-factory.js:48:35)  
    at new AdminJS (D:\task-manager\node_modules\adminjs\lib\adminjs.js:114:39)
    at AdminModule.onModuleInit (D:\task-manager\node_modules\@adminjs\nestjs\src\admin.module.ts:136:19)
    at callModuleInitHook (D:\task-manager\node_modules\@nestjs\core\hooks\on-module-init.hook.js:51:35)
    at NestApplication.callInitHook (D:\task-manager\node_modules\@nestjs\core\nest-application-context.js:210:13)
    at NestApplication.init (D:\task-manager\node_modules\@nestjs\core\nest-application.js:97:9)
    at NestApplication.listen (D:\task-manager\node_modules\@nestjs\core\nest-application.js:155:33)

The following error occurs after adding the following resource to adminJsOptions.resources array

const prisma = new PrismaService();
        const dmmf = ((prisma as any)._baseDmmf as DMMFClass);
// ...other code
 {
                resource: { model: dmmf.modelMap.Publisher, client: prisma },
                options: {},
              }

The following code shows the app module where I instantiated AdminJS

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AdminModule } from '@adminjs/nestjs';
import * as AdminJSPrisma from '@adminjs/prisma'
import AdminJS from 'adminjs'
import { PrismaService } from './prisma/prisma.service';
import { DMMFClass } from '@prisma/client/runtime'




AdminJS.registerAdapter({
  Resource: AdminJSPrisma.Resource,
  Database: AdminJSPrisma.Database,
})

@Module({
  imports: [
    AdminModule.createAdminAsync({
      useFactory: () => {

        const prisma = new PrismaService();
        const dmmf = ((prisma as any)._baseDmmf as DMMFClass);
        return {
          adminJsOptions: {
            rootPath: '/admin',
            resources: [
              {
                resource: { model: dmmf.modelMap.Publisher, client: prisma },
                options: {},
              }
            ],
          }
        }
      },
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }


Solution

  • EDIT: I wrote an article explaining everything and optimized methods

    Below is a format of a basic Resource object. You can add as many resources as you want.

    {
      resource: { model: dmmf.modelMap[{Model name here}], client: prisma },
      options: {},
    },
    

    Make sure you replace {Model name here} with the name of the model you want to use. For example, if you want to use the User model, you would replace {Model name here} with User.

    How does this work?

    THe dmmf object is a representation of your database schema. It is used to generate the AdminJS resources. Using dmmf.modelMap['User'] we can get the User model from the dmmf object. We then pass this model to the resource property of the AdminJS resource.

    The client property is the Prisma client. This is used to connect to the database and perform CRUD operations.

    We can add as many resources as we want. For example, if we wanted to add a Post model, we would add the following to the resources array:

    [
      ...,
      {
        resource: { model: dmmf.modelMap['Post'], client: prisma },
        options: {},
      },
      ...
    ]
    

    models are the tables in your database. Defined in the schema.prisma file.

    import {
      Module,
    } from '@nestjs/common';
    import AdminJS from 'adminjs';
    import { AdminModule } from '@adminjs/nestjs';
    import { AppController } from './app.controller';
    import { Database, Resource } from '@adminjs/prisma';
    import { DMMFClass } from '@prisma/client/runtime';
    import { DatabaseModule } from '@database/database.module';
    import { PrismaService } from '@database/prisma.service';
    
    AdminJS.registerAdapter({ Database, Resource });
    
    const DEFAULT_ADMIN = {
      email: '[email protected]',
      password: 'password',
    }
    
    @Module({
      imports: [
        ...
        DatabaseModule,
        AdminModule.createAdminAsync({
          imports: [DatabaseModule],
          inject: [PrismaService],
          useFactory: async (prisma: PrismaService) => {
            const dmmf = (prisma as any)._baseDmmf as DMMFClass;
            return {
              adminJsOptions: {
                rootPath: '/admin',
                loginPath: '/admin/login',
                logoutPath: '/admin/logout',
                branding: {
                  companyName: 'Your Project',
                },
                resources: [
                  {
                    resource: { model: dmmf.modelMap['User'], client: prisma },
                    options: {},
                  },
                  {
                    resource: { model: dmmf.modelMap['Profile'], client: prisma },
                    options: {},
                  },
                  {
                    resource: { model: dmmf.modelMap['Business'], client: prisma },
                    options: {},
                  },
                  {
                    resource: { model: dmmf.modelMap['BankAccount'], client: prisma },
                    options: {},
                  },
                ],
              },
              auth: {
                authenticate: async (email: string, password: string) => {
                  if (
                    email === DEFAULT_ADMIN.email &&
                    password === DEFAULT_ADMIN.password
                  ) {
                    return DEFAULT_ADMIN;
                  }
                  return null;
                },
                cookieName: "adminjs",
                cookiePassword: "secret",
              },
              sessionOptions: {
                resave: true,
                saveUninitialized: true,
                secret: "secret",
              },
            };
          },
        }),
        ...
      ],
      controllers: [AppController],
      providers: [
        ...
      ],
    })
    export class AppModule {
    }