Closed rubiin closed 3 years ago
(dublicated from https://stackoverflow.com/questions/66306793/how-to-use-admin-bro-nestjs-with-admin-bro-typeorm-and-postgres-in-a-right-way)
The admin-bro-nestjs repository contains a comprehensive example with example with mongoose. But I need use it with typeorm and postgres. I tried to adapt this example for typeorm:
// main.ts
import AdminBro from 'admin-bro';
import { Database, Resource } from '@admin-bro/typeorm';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
AdminBro.registerAdapter({ Database, Resource });
const bootstrap = async () => {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
and
// app.module.ts
import { Module } from '@nestjs/common';
import { AdminModule } from '@admin-bro/nestjs';
import { TypeOrmModule, getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserEntity } from './user/user.entity';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'password',
database: 'database_test',
entities: [UserEntity],
synchronize: true,
logging: false,
}),
AdminModule.createAdminAsync({
imports: [
TypeOrmModule.forFeature([UserEntity]),
],
inject: [
getRepositoryToken(UserEntity),
],
useFactory: (userRepository: Repository<UserEntity>) => ({
adminBroOptions: {
rootPath: '/admin',
resources: [
{ resource: userRepository },
],
},
auth: {
authenticate: async (email, password) => Promise.resolve({ email: 'test' }),
cookieName: 'test',
cookiePassword: 'testPass',
},
}),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
But on application start I get the following error:
NoResourceAdapterError: There are no adapters supporting one of the resource you provided
Could you explain putting all these libraries together?
Hello @muturgan i made like this:
import { AdminModuleFactory } from '@admin-bro/nestjs/types/interfaces/admin-module-factory.interface';
import { UserEntity } from 'src/users/entities/user.entity';
import { Connection } from 'typeorm';
import { CurrentAdmin } from 'admin-bro';
import * as bcrypt from 'bcrypt';
export const adminFactory: AdminModuleFactory = {
useFactory: (connection: Connection) => ({
adminBroOptions: {
databases: [connection],
// resources: [UserEntity] // this too works, you dont need to inject the repository, just import
rootPath: '/admin',
},
auth: {
authenticate: async (email, password) => {
const userRep = connection.getRepository(UserEntity);
const user = await userRep.findOne({ email });
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) return;
return (user as unknown) as CurrentAdmin;
},
cookieName: 'some-secret-password',
cookiePassword: 'some-secret-password',
},
}),
inject: [Connection],
};
connection: Connection
Excellent! It works. Thank you very much!
Hello @muturgan i made like this:
import { AdminModuleFactory } from '@admin-bro/nestjs/types/interfaces/admin-module-factory.interface'; import { UserEntity } from 'src/users/entities/user.entity'; import { Connection } from 'typeorm'; import { CurrentAdmin } from 'admin-bro'; import * as bcrypt from 'bcrypt'; export const adminFactory: AdminModuleFactory = { useFactory: (connection: Connection) => ({ adminBroOptions: { databases: [connection], // resources: [UserEntity] // this too works, you dont need to inject the repository, just import rootPath: '/admin', }, auth: { authenticate: async (email, password) => { const userRep = connection.getRepository(UserEntity); const user = await userRep.findOne({ email }); const isValid = await bcrypt.compare(password, user.password); if (!isValid) return; return (user as unknown) as CurrentAdmin; }, cookieName: 'some-secret-password', cookiePassword: 'some-secret-password', }, }), inject: [Connection], };
If you want to, you can answer my question on stack owerflow (link at my post here) and I will glad to approve it :)
Doesn't work for me. I get:
TypeError: this.model.getRepository is not a function
at Resource.prepareProps (D:\dental\node_modules\@admin-bro\typeorm\lib\Resource.js:117:40)
When I checked what is this.model, it was one of entities, but not AdminEntity.
Doesn't work for me.
Hello @Zydnar ! Need more details. Is it possible to get a link to you repo? I want to try to find a problem...
@muturgan I have it on company server, so no, at least not today. However I can provide short version of what I've tried. I'm using nest with fastify so this may be main problem. I have tried two scenarios with same result.
This one is more close to solution above:
// main.ts
import { initSwagger } from '../devTools/swagger/initSwagger';
import { NestFactory } from '@nestjs/core';
import { fastifyMiddlewareFunction, setupMiddleware } from './middleware';
import * as AdminBroExpress from '@admin-bro/express';
import AdminBro from 'admin-bro';
import {Database, Resource} from '@admin-bro/typeorm';
import { validate } from 'class-validator';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import { readFile } from 'fs';
import { join } from 'path';
import * as pino from 'pino';
import { AdminEntity } from './database/entities';
Resource.validate = validate;
async function bootstrap() {
const [key, cert]: Buffer[] = await Promise.all(
[
new Promise((resolve, reject) => readFile(join(__dirname, '../..', 'https', 'private.key'), (err, file) => {
if (err) {
return reject(err);
}
return resolve(file);
})),
new Promise((resolve, reject) => readFile(join(__dirname, '../..', 'https', 'certificate.cert'), (err, file) => {
if (err) {
return reject(err);
}
return resolve(file);
})),
],
);
const https = process.env.NODE_ENV === 'production' ? {} :
{
key,
cert,
};
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({
http2: true,
https,
logger: pino({
level: 'info',
prettyPrint: {
levelFirst: true,
},
}),
}),
);
await initSwagger(app);
await setupMiddleware(app);
// Admin Bro
AdminBro
.registerAdapter({
Database,
Resource,
});
///
await app.listen(process.env.NODE_ENV === 'production' ? 80 : 5000, process.env.NODE_ENV === 'production' ? '0.0.0.0' : 'localhost');
}
bootstrap()
.then(() => console.log('Server Started!'))
.catch(console.error);
And module:
// app.module.ts
@Module({
imports: [
//...
AdminModule.createAdminAsync({
imports: [
TypeOrmModule.forFeature([AdminEntity]),
],
...adminFactory, // your factory
})
/...
And this causes the error.
Now second solution with same result, where I'm not using app.module. It's inspired by gist working on fastify: https://gist.github.com/MexsonFernandes/454ce2f552c5167bb5c979fdb82da63b#file-admin-bro-nestjs-js
//main.ts
import { initSwagger } from '../devTools/swagger/initSwagger';
import { NestFactory } from '@nestjs/core';
import { fastifyMiddlewareFunction, setupMiddleware } from './middleware';
import * as AdminBroExpress from '@admin-bro/express';
import AdminBro, { Router as AdminRouter } from 'admin-bro';
import {Database, Resource} from '@admin-bro/typeorm';
import { validate } from 'class-validator';
import { createConnection } from 'typeorm';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import { readFile } from 'fs';
import { join } from 'path';
import * as pino from 'pino';
import { AdminEntity } from './database/entities';
Resource.validate = validate;
async function bootstrap() {
const [key, cert]: Buffer[] = await Promise.all(
[
new Promise((resolve, reject) => readFile(join(__dirname, '../..', 'https', 'private.key'), (err, file) => {
if (err) {
return reject(err);
}
return resolve(file);
})),
new Promise((resolve, reject) => readFile(join(__dirname, '../..', 'https', 'certificate.cert'), (err, file) => {
if (err) {
return reject(err);
}
return resolve(file);
})),
],
);
const https = process.env.NODE_ENV === 'production' ? {} :
{
key,
cert,
};
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({
http2: true,
https,
logger: pino({
level: 'info',
prettyPrint: {
levelFirst: true,
},
}),
}),
);
await initSwagger(app);
await setupMiddleware(app);
// Admin Bro
AdminBro
.registerAdapter({
Database,
Resource,
});
const connection = await createConnection({
name: 'admin',
type: "postgres",
host: "localhost",
port: 5432,
username: "postgres",
password: "Poiu7890",
database: "dental",
entities: [AdminEntity],
synchronize: true,
logging: true,
});
AdminEntity.useConnection(connection);
const adminBro = new AdminBro({
rootPath: '/admin',
resources: [{ resource: AdminEntity }],
branding: {
companyName: 'FreeDe',
softwareBrothers: false,
},
});
const router = (AdminBroExpress as any).buildRouter(adminBro);
/* my current attempt I'm trying to write it on my own
await adminBro.initialize();
console.log("AdminBro: bundle ready");
const { routes, assets } = AdminRouter;
app.use(((()=> (req, res: HTTP2ServerResponse, next)=>{
}) as (() => fastifyMiddlewareFunction))());
*/
app.use(adminBro.options.rootPath, router);
///
await app.listen(process.env.NODE_ENV === 'production' ? 80 : 5000, process.env.NODE_ENV === 'production' ? '0.0.0.0' : 'localhost');
}
bootstrap()
.then(() => console.log('Server Started!'))
.catch(console.error);
@muturgan I have it on company server, so no, at least not today. However I can provide short version of what I've tried. I'm using nest with fastify so this may be main problem. I have tried two scenarios with same result.
1st solution
This one is more close to solution above:
// main.ts import { initSwagger } from '../devTools/swagger/initSwagger'; import { NestFactory } from '@nestjs/core'; import { fastifyMiddlewareFunction, setupMiddleware } from './middleware'; import * as AdminBroExpress from '@admin-bro/express'; import AdminBro from 'admin-bro'; import {Database, Resource} from '@admin-bro/typeorm'; import { validate } from 'class-validator'; import { FastifyAdapter, NestFastifyApplication, } from '@nestjs/platform-fastify'; import { AppModule } from './app.module'; import { readFile } from 'fs'; import { join } from 'path'; import * as pino from 'pino'; import { AdminEntity } from './database/entities'; Resource.validate = validate; async function bootstrap() { const [key, cert]: Buffer[] = await Promise.all( [ new Promise((resolve, reject) => readFile(join(__dirname, '../..', 'https', 'private.key'), (err, file) => { if (err) { return reject(err); } return resolve(file); })), new Promise((resolve, reject) => readFile(join(__dirname, '../..', 'https', 'certificate.cert'), (err, file) => { if (err) { return reject(err); } return resolve(file); })), ], ); const https = process.env.NODE_ENV === 'production' ? {} : { key, cert, }; const app = await NestFactory.create<NestFastifyApplication>( AppModule, new FastifyAdapter({ http2: true, https, logger: pino({ level: 'info', prettyPrint: { levelFirst: true, }, }), }), ); await initSwagger(app); await setupMiddleware(app); // Admin Bro AdminBro .registerAdapter({ Database, Resource, }); /// await app.listen(process.env.NODE_ENV === 'production' ? 80 : 5000, process.env.NODE_ENV === 'production' ? '0.0.0.0' : 'localhost'); } bootstrap() .then(() => console.log('Server Started!')) .catch(console.error);
And module:
// app.module.ts @Module({ imports: [ //... AdminModule.createAdminAsync({ imports: [ TypeOrmModule.forFeature([AdminEntity]), ], ...adminFactory, // your factory }) /...
And this causes the error.
2nd solution
Now second solution with same result, where I'm not using app.module. It's inspired by gist working on fastify: https://gist.github.com/MexsonFernandes/454ce2f552c5167bb5c979fdb82da63b#file-admin-bro-nestjs-js
//main.ts import { initSwagger } from '../devTools/swagger/initSwagger'; import { NestFactory } from '@nestjs/core'; import { fastifyMiddlewareFunction, setupMiddleware } from './middleware'; import * as AdminBroExpress from '@admin-bro/express'; import AdminBro, { Router as AdminRouter } from 'admin-bro'; import {Database, Resource} from '@admin-bro/typeorm'; import { validate } from 'class-validator'; import { createConnection } from 'typeorm'; import { FastifyAdapter, NestFastifyApplication, } from '@nestjs/platform-fastify'; import { AppModule } from './app.module'; import { readFile } from 'fs'; import { join } from 'path'; import * as pino from 'pino'; import { AdminEntity } from './database/entities'; Resource.validate = validate; async function bootstrap() { const [key, cert]: Buffer[] = await Promise.all( [ new Promise((resolve, reject) => readFile(join(__dirname, '../..', 'https', 'private.key'), (err, file) => { if (err) { return reject(err); } return resolve(file); })), new Promise((resolve, reject) => readFile(join(__dirname, '../..', 'https', 'certificate.cert'), (err, file) => { if (err) { return reject(err); } return resolve(file); })), ], ); const https = process.env.NODE_ENV === 'production' ? {} : { key, cert, }; const app = await NestFactory.create<NestFastifyApplication>( AppModule, new FastifyAdapter({ http2: true, https, logger: pino({ level: 'info', prettyPrint: { levelFirst: true, }, }), }), ); await initSwagger(app); await setupMiddleware(app); // Admin Bro AdminBro .registerAdapter({ Database, Resource, }); const connection = await createConnection({ name: 'admin', type: "postgres", host: "localhost", port: 5432, username: "postgres", password: "Poiu7890", database: "dental", entities: [AdminEntity], synchronize: true, logging: true, }); AdminEntity.useConnection(connection); const adminBro = new AdminBro({ rootPath: '/admin', resources: [{ resource: AdminEntity }], branding: { companyName: 'FreeDe', softwareBrothers: false, }, }); const router = (AdminBroExpress as any).buildRouter(adminBro); /* my current attempt I'm trying to write it on my own await adminBro.initialize(); console.log("AdminBro: bundle ready"); const { routes, assets } = AdminRouter; app.use(((()=> (req, res: HTTP2ServerResponse, next)=>{ }) as (() => fastifyMiddlewareFunction))()); */ app.use(adminBro.options.rootPath, router); /// await app.listen(process.env.NODE_ENV === 'production' ? 80 : 5000, process.env.NODE_ENV === 'production' ? '0.0.0.0' : 'localhost'); } bootstrap() .then(() => console.log('Server Started!')) .catch(console.error);
how are you using this on fastify
@rubiin https://github.com/SoftwareBrothers/admin-bro/blob/master/src/backend/utils/router/router.ts was very helpful - there is list of all static files and actions you have to register in fastify routing. Edit: note also fastify usually supports express middleware plugins.
Do you have a minimal repo you can share
I will by the end of this week. I have to remove all unrelated plugins I use in my project.
cool, do let me know. That would be a big help
@rubiin My sister is getting married this weekend, I hoped I will have more time so I'm keeping at least partially my promise. Bellow is code with all requests you need to make Admin Bro working on fastify. What I haven't finished are API requests, however views work perfectly fine. So what is left - you have to create in all API requests request and response with express.js properties. Admin session is easy part - you know how to make protected route.
AdminController code: https://pastebin.com/3QSyxsHZ
@rubiin My sister is getting married this weekend, I hoped I will have more time so I'm keeping at least partially my promise. Bellow is code with all requests you need to make Admin Bro working on fastify. What I haven't finished are API requests, however views work perfectly fine. So what is left - you have to create in all API requests request and response with express.js properties. Admin session is easy part - you know how to make protected route.
AdminController code: https://pastebin.com/3QSyxsHZ
Thanks its a great help
hi there, i woul like to see how to integrate with typeorm