I'm using Prisma 3.15 with NestJS and need to split queries to two different data-sources. One is read-only replica and another is master database.
I don't want to manage these manually on each prisma call!
How can I split them automatically?
I can define two separate clients in PrismaService like this:
#Injectable()
export class PrismaService implements OnModuleInit {
constructor(
private readClient: PrismaClient,
private writeClient: PrismaClient
) {
this.readClient = new PrismaClient({
datasources: {db: {url: process.env.PRISMA_READ_DB_URL}},
});
this.writeClient = new PrismaClient({
datasources: {db: {url: process.env.PRISMA_WRITE_DB_URL}},
});
}
But how can I split queries between them?
Can I override prisma modules like this?
get user() {
// Do something with the user!!!
return this.readClient.user;
}
Related
I currently access a mongo db and it's collections in a service like so:
#Injectable()
export class ProductService {
constructor(#InjectModel('Cats') private categoriesModel: Model<CategoriesDocument>) {}
private categories: Category[] = categories;
...
Then with in a method I can call that
this.categoriesModel
All this works and I'm able to retrieve data. My question is, is there away to retrieve the data from a mongo db BEFORE my service is fired up? In example
products.module.ts
// do the mongoose call here as an async / await method. Then have it pass the data into my provider as a class BEFORE it fires up?
#Module({
imports: [MongooseModule.forFeature([{ name: 'Cats', schema: CategoriesSchema }])],
providers: [CategoriesResolver, CategoriesService],
})
export class CategoriesModule {}
I have an interface created for my model, where I only want to return specific data from the record
// code.interface.ts
import { Document } from 'mongoose';
export interface CodeI extends Document {
readonly _id: string;
readonly logs: any;
}
But when I get the result from mongo, it completely ignores what is in my interface. (I am using NestJs as framework)
//constructor
constructor(#InjectModel(Coupon.name) private couponModel: Model<CouponDocument>) {}
// function
async findOne(codeId: string): Promise<CodeI> {
const coupon = await this.couponModel.findOne({ _id: codeId }).exec();
if (!coupon) {
throw new NotFoundException([`#${codeId} not found`]);
}
return coupon;
}
TypeScript interfaces don't work this way. They can't limit the fields of an object because they don't exist at runtime, so, we can't use them to guide any runtime behavior. TypeScript interfaces are useful for compile-time type check only.
However, in your case, there are two ways you can achieve the expected behavior.
The first one is to select only the required fields which you need to return (recommended).
In your findOne, you can do something like this
async findOne(codeId: string): Promise<CodeI> {
const coupon = await this.couponModel.findOne({ _id: codeId }, '_id logs').exec();
if (!coupon) {
throw new NotFoundException([`#${codeId} not found`]);
}
return coupon;
}
Here, as you can see, I have passed an additional string type parameter to findOne function which is projection and it will select only the specified fields from the object. This will not only solve your problem but also save query time and have increase query performance. Read more about findOne here.
The other way is to create a DTO where you can define the fields you want to return from the function.
Something like this:
// CouponDto.ts
class CouponDto {
public readonly _id: string;
public readonly logs: any;
constructor(data: CodeI) {
this._id = data._id;
this.logs = data.logs;
}
}
Then, in your service file, you can do something like
return new CouponDto(coupon);
(make sure to change the return type of the function to CouponDto as well)
You can use any of these two ways. While I would recommend going with the first one, it's up to you and how you wanna structure your project.
External Links:
Mongoose FindOne Docs
At the moment to implement nestjsx/crud is very easy to create a CRUD, but what happens if I want to omit the "Delete" ?
User service
#Injectable()
export class UserService extends TypeOrmCrudService<UserService>{
constructor(#InjectRepository(User) repo) {
super(repo)
}
}
Usercontroller
#Crud({
model: {
type: User
}
})
#Controller('todo')
export class UserController implements CrudController<UserService> {
constructor(public service: UserService) {}
}
It's very simple, with that I have my CRUD operations in less than one minute, but if I have to omit a operation, in this case, delete, do I have to create one by one my method? or how I can do it?
You can exclude or include specific routes using the routes property of the #Crud decorator. exclude allows you to list routes that will not be provided, while only lets you declare which routes will be provided.
You could use the following to specifically exclude the Delete route:
#Crud({
model: {
type: User
},
routes: {
exclude: ['deleteOneBase'],
}
See the nestjsx/crud routes section of the wiki for additional routes properties.
The #Crud decorator gives you lots of control over your routes. In addition to routes, you'll find the params and query properties to be incredibly useful.
As per nestjs doc page I am using this code:
#Controller('books')
export class BooksController {
constructor(
private readonly bookService: BooksService,
) {}
#SerializeOptions({
excludePrefixes: ['_'],
})
#Get()
async getAll(): Promise<Book[]> {
return this.bookService.getAll();
}
}
trying to strip _id (and presumably __v too) properties from the documents which are coming from mongodb/mongoose. Is there any other step I need to take to make it work? I have also tried placing the #SerializeOptions decorator over the #Controller - to no avail.
(It's only an exercise, IRL I would probably map id
I would need to change where my queries are pointing in a Nestjs app.
I am working in an Angular app with versions so I have to change collections or even databases dinamically in my back end with Mongoose and Nestjs.
I suppose I have to change target in the provider and try to change it dynamically depending of what version the user have chosen in the front.
Anyway I have sth like
import { Connection } from 'mongoose';
import { TestSchema } from './schemas/test.schema';
export const testProviders = [
{
provide: 'TestModelToken',
useFactory: (connection: Connection) =>
connection.model('Test', FilterSchema, 'tests'),
inject: ['DbConnectionToken'],
},
];
And later, for example, I want to change it to dynamically point to this collection
tests_24_04_2019
My service looks like
#Injectable()
export class TestsService {
constructor(
#Inject("TestModelToken") private readonly testModel: Model<Test>,
Thanks, I appreciate any help
I am not familiar with Nestjs, but I think you should initialize the collection model on the runtime instead of creating it directly in provider
{
provide: 'TestModelToken',
useFactory: (connection: Connection) => {
getTestModel(version: string) => connection.model('Test', FilterSchema, `tests_${version}`),
}
inject: ['DbConnectionToken'],
},