- Generate CRUD REST API with decorators and no BS
- Validation based on schema decorators
- Database agnostic
- Search agnostic
- Native SQL drivers for MySQL, PostgreSQL and SQLite
- MySQL Database Service
- PostgreSQL Database Service
- SQLite Database Service
- MongoDB Database Service
- Swagger doc
- Meilisearch Search Service
- Mailer Notification Service
- Slack Notification Service
- Console helper
// app.module.ts
import { Module } from "@nestjs/common";
import { DatabaseService } from "./services/db.service";
import { SearchService } from "./services/search.service";
import { UsersModule } from "./users/users.module";
import { MetaModule } from "meta-nest";
@Module({
imports: [
UsersModule, // ie we will use UsersModule
MetaModule.forRoot({
// Configure the module with your custom DatabaseService & SearchService
DatabaseService,
SearchService,
}),
],
providers: [DatabaseService, SearchService], // Import your custom DatabaseService & SearchService
})
export class AppModule {}// users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { MetaModule } from 'meta-nest';
import { UsersController } from './users.controller';
@Module({
imports: [MetaModule)], // Register simply the MetaModule in your feature module
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}// users/users.controller.ts
import { Controller } from "@nestjs/common";
import { User } from "./user.schema";
import { ApiGroup } from "../core/decorators/api-group.decorator";
import { MetaController } from "../core/decorators/meta.decorator";
// Use ApiGroup instead of ApiTags for swagger api tagging
@ApiGroup("Users")
@Controller()
// MetaController enable you to setup easily your generic CRUD api
@MetaController({
key: "users",
schema: User,
routes: ["GET", "POST", "PATCH", "DESTROY", "ACTIONS"],
})
export class UsersController {
constructor() {}
// ...
}// users/users.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { DatabaseService } from '../services/db.service';
import { SearchService } from '../services/search.service';
@Injectable()
export class UsersService {
constructor(
// Use @Inject('DatabaseService') to skip reimporting your db service in your user module
@Inject('DatabaseService') private db: DatabaseService
// Use @Inject('SearchService') to skip reimporting your search service in your user module
@Inject('SearchService') private db: SearchService
) {}
// ...
}// users/users.service.ts
// You can use class-validator & class-transformer on your schema
import { Exclude, Transform } from "class-transformer";
import { IsEmail, Length } from "class-validator";
import { ApiProperty } from "@nestjs/swagger";
import { ObjectId } from "mongodb";
import { PrimaryKey, MetaModel } from "meta-nest";
// You need to extends MetaModel to use your schema with meta-nest
export class User extends MetaModel {
// PrimaryKey let you specify the pk of your schema
// By default meta-nest will target 'id' property
@PrimaryKey()
@Transform((value) => value.obj._id.toString())
_id: ObjectId;
@Length(3, 10)
@ApiProperty({ example: "Foo Bar", description: "The name of the user" })
name: string;
@IsEmail()
@ApiProperty({
example: "api-generator@mail.com",
description: "The email of the user",
})
email: string;
@Exclude()
password: string;
@Length(3, 10)
@ApiProperty({
example: "Xx_Foo_Bar_xX",
description: "The username of the user",
})
username: string;
constructor(partial: Partial<User>) {
// Don't forget super when extending class
super();
Object.assign(this, partial);
}
}Go to /api to check your api swagger.
To use your custom DatabaseService with meta-nest your service need to implement DatabaseServiceInterface. As long as your service follows the interface, you can make any DB driver works with meta-nest.
import { Injectable, Inject } from "@nestjs/common";
import { DatabaseServiceInterface } from "meta-nest";
@Injectable()
export class DatabaseService implements DatabaseServiceInterface {
// ...
}Quark also provides native SQL drivers built from scratch. They generate parameterized SQL internally and delegate execution to the native client you install in your app.
pnpm add pg
pnpm add mysql2
pnpm add better-sqlite3
pnpm add mongodbimport {
MetaModule,
createPostgresqlDatabaseService,
createMysqlDatabaseService,
createSqliteDatabaseService,
createMongoDatabaseService,
} from "@quark/core";
const DatabaseService = createPostgresqlDatabaseService({
connection: {
host: process.env.POSTGRES_HOST,
port: Number(process.env.POSTGRES_PORT || 5432),
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE,
},
});
// MySQL
createMysqlDatabaseService({ connection: mysqlConnectionOptions });
// SQLite
createSqliteDatabaseService({ filename: "./data/app.sqlite" });
// MongoDB
createMongoDatabaseService({
uri: process.env.MONGODB_URI,
database: process.env.MONGODB_DATABASE,
});
@Module({
imports: [
MetaModule.forRoot({
DatabaseService,
SearchService,
}),
],
})
export class AppModule {}See .docs/sql-drivers.md and .docs/mongodb.md for naming, primary keys,
native relations (one-to-one, one-to-many, many-to-many) with selected
relation columns, and raw query examples.
To use your custom SearchService with meta-nest your service need to implement SearchServiceInterface. As long as your service follows the interface, you can make any Search Engine works with meta-nest.
import { Injectable, Inject } from "@nestjs/common";
import { SearchServiceInterface } from "meta-nest";
@Injectable()
export class SearchService implements SearchServiceInterface {
// ...
}// users/users.controller.ts
import { Controller } from "@nestjs/common";
import { User } from "./user.schema";
import { ApiGroup } from "../core/decorators/api-group.decorator";
import { MetaController } from "../core/decorators/meta.decorator";
// Use ApiGroup instead of ApiTags for swagger api tagging
@ApiGroup("Users")
@Controller()
// You can scope your controller and using multiple MetaController
@MetaController({
key: "teachers",
schema: User,
scope: { type: "teacher" },
routes: ["GET", "POST", "PATCH", "DESTROY", "ACTIONS"],
})
@MetaController({
key: "students",
schema: User,
scope: { type: "student" },
routes: ["GET", "POST", "PATCH", "DESTROY", "ACTIONS"],
})
export class UsersController {
constructor() {}
// ...
}