I have the need to dynamically create mongo databases when a new user is registered on a SaaS platform.
How do I configure NestJS to run the scripts needed, to create the database, as well as how to dynamically connect with database created for each user?
And are there any better approaches to my usecase?
New User => Create New Database for them, and store details in the main SaaS database.
When user login, fetch DB details from main database, and create a connection to it, to read their data.
I don't see the need to create a new db for each user. Why not craeting a users collection and than for each user create a new object. Mongo object can store quite a lot of information.
I'm not a pro in nestjs, but I don't think that there is a way to dynamically connect more db's. When you start your app a mongoDB instance is initiated, don't forget that all classes in nestjs are singletones.
So, I was able to make it thus.
I created a database service class, in it, I used the MongooseModuleOptions to create a new connection, and injecting the Request Scope to it, I extracted the database name from the request and dynamically pass it to the uri in the createMongooseOptions method and that dynamically creates a connection.
import { Inject } from '#nestjs/common';
import { MongooseOptionsFactory, MongooseModuleOptions } from '#nestjs/mongoose';
import { REQUEST } from '#nestjs/core';
import { Request } from 'express';
export class DatabaseService implements MongooseOptionsFactory{
constructor(
#Inject(REQUEST) private readonly request: Request) {
}
createMongooseOptions(): MongooseModuleOptions {
const urii = process.env.CLUSTER+'/'+this.request.body.dbName+"?retryWrites=true&w=majority"
return {
uri: urii
};
}
}
Then inside the users module, I insert data into a collection specified inside the schema, and that automatically uses the connection to create a database, collection and entry, if it exists, it uses the database and collection.
import { Inject, Injectable } from '#nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { User, UserDocument } from './schemas/user.schema';
import { InjectConnection, InjectModel } from '#nestjs/mongoose';
import { Model } from 'mongoose';
#Injectable()
export class UserService {
constructor(#InjectModel(User.name) private userModel:Model<UserDocument>){}
async create(createUserDto: CreateUserDto) {
const createdUser = new this.userModel(createUserDto)
return createdUser.save()
}
}
That worked perfectly for me.
Thank you all 🙏🙏🙏
Related
Schema on User Management service:
model User {
id String #id #default(uuid())
username String #unique
email String #unique
password String
}
Schema on File management service:
model File {
id String #id #default(uuid())
user_id String
user User #relation(fields: [user_id], references: [id])
file_name String
file_description String
file_url String
file_hash String
}
My user management service and file management service is different repository using Nest Js, Thank You!
I want to add relation on file model to user model
To add a relation between the File and User models in your Prisma setup with NestJS, you'll need to perform the following steps:
Connect to the appropriate Prisma service(s) from your NestJS application(s).
Make sure that the User and File models in your Prisma schema have the necessary fields for the relation, as you have defined above.
In your NestJS application, create a service that retrieves the appropriate user for each file, based on the user_id field. You can use the prisma-client package to query the Prisma API for the relevant data.
In your NestJS controller(s), use the service from step 3 to fetch the relevant data and return it in the API response.
Here's an example of what the service from step 3 might look like:
import { Injectable } from '#nestjs/common';
import { PrismaClient } from '#prisma/client';
#Injectable()
export class FilesService {
private prisma = new PrismaClient();
async getFilesWithUser() {
const files = await this.prisma.file.findMany({
include: {
user: true,
},
});
return files;
}
}
And here's an example of what the controller:
import { Controller, Get } from '#nestjs/common';
import { FilesService } from './files.service';
#Controller('files')
export class FilesController {
constructor(private readonly filesService: FilesService) {}
#Get()
async getFilesWithUser() {
return this.filesService.getFilesWithUser();
}
}
I'm making an API for a web application, and I'm running into a weird problem when I try to import a module. I have a folder with a bunch of models used to get data from the database, and I have a folder named "global" with various modules used all over the project.
/
/api
/models
/Users.js
/Trainings.js
/TrainingsTypes.js
/TrainingsSubtypes.js
/global
/Functions.js
Some of the models import other models to check ID, get data, etc. I made a function in Functions.js that also needs some of the models to work. So basically, the import dependencies look like:
I must admit, this is a little crappy when showed like that. But here is my problem. As long as Functions.js doesn't import Users.js, everything is fine. The API works well and there's no crash at all. But, if I import Users.js in Functions.js, I immediatly get this error:
ReferenceError: Cannot access 'Users' before initialization
at file:///C:/Users/USERNAME/Desktop/project-api/global/Functions.js:30:10
at ModuleJob.run (internal/modules/esm/module_job.js:169:25)
at async Loader.import (internal/modules/esm/loader.js:177:24)
at async Object.loadESM (internal/process/esm_loader.js:68:5)
Here is the code of Functions.js (I put a comment to indicate line 30):
import check from "checkers";
import Users from "../api/models/Users.js";
import Trainings from "../api/models/Trainings.js";
import TrainingsTypes from "../api/models/TrainingsTypes.js";
import TrainingsSubtypes from "../api/models/TrainingsSubtypes.js";
/*****************************************************
* SQL Queries
*****************************************************/
export function fieldsToUpdate(fields) {...}
const fillers = {
"user": Users, // Line 30
"trainer": Users,
"type": TrainingsTypes,
"subtype": TrainingsSubtypes
};
export async function fillIDs(db, response) {...}
Moreover, the import itself doesn't cause problems. If I remove Users from the fillers object, there's still no crash. I've seen that might be a cyclic dependencies issue but some says that Node can handle this. I must add that I really need all my models in fillIDs() so I just can't remove all the import and I didn't want to copy/paste this code in every model. There must be a solution, but I need some help.
Have you any idea how to fix this?
Thanks
EDIT 1:
Users.js import these modules:
import bcrypt from "bcryptjs";
import generatePwd from "generate-password";
import { v4 as uuidv4 } from "uuid";
import check from "checkers";
import { fieldsToUpdate, fillIDs } from "../../global/Functions.js";
import { arrayToSerialComma } from "../../global/Converters.js";
import APIResp from "../../global/APIResp.js";
import Mailer from "../../global/Mailer.js";
import Genders from "./Genders.js";
import Roles from "./Roles.js";
import Tokens from "./Tokens.js";
import { Passwords } from "../../config/config.js";
EDIT 2:
Users.js is exported in this way:
const Users = {
isValid,
add,
logIn, getAll, getById, getByEmail, hasForgotPassword, getRolesOf,
update, updatePwd, passwordForgotten,
delete: del,
Trainers: {
getById: getTrainerById,
getAll: getAllTrainers
}
};
export default Users;
I managed to partially solve my problem. I've created a file in global exporting all the files in this folder and did the same for models.
// ROOT/global/global.js
export { default as APIResp } from "./APIResp.js";
export { default as SQLFunctions } from "./SQLFunctions.js";
export { default as Mailer } from "./Mailer.js";
export { default as ModuleConsole } from "./ModuleConsole.js";
// ROOT/api/models/models.js
export { default as Users } from "./Users.js";
export { default as Genders } from "./Genders.js";
export { default as Roles } from "./Roles.js";
export { default as Tokens } from "./Tokens.js";
export { default as Trainings } from "./Trainings.js";
export { default as TrainingsTypes } from "./TrainingsTypes.js";
export { default as TrainingsSubtypes } from "./TrainingsSubtypes.js";
I also removed some functions that was needed in both the front-end and back-end and moved them to an external module. I used madge to generate a dependency graph.
As we can see, it's not perfect yet. All the models files are actually in a big circular dependency but it's much cleaner than before and there are no more crashes.
This may have an impact on performance, and I have to be careful with that.
I mark this answer as correct for now, I may change it if someone else or I found something.
I've got a #BeforeInsert() listener in my Post entity. The listener is supposed to create a unique slug for the slug column before insertion.
For example:
export class Post extends BaseEntity {
#PrimaryGeneratedColumn()
id: number
#Column()
title: string
#Column()
slug: string
#BeforeInsert()
private updateSlug() {
this.slug = slugify(this.title)
// I will need to access the database to check whether the same slug has existsed already or not.
// How do I access the database in this listener method?
}
}
Since the slug column is supposed to be unique, I will have to check in the database to know whether the same slug already exists. If the slug already exists, I will then need to append a number behind the slug, like hello-word-1.
However, to do so, I will need to access the Post repository of the entity in the Post entity class before I can access the database. But I don't see how I can inject the repository into my Post entity class to access the database for this purpose.
How should I approach this problem?
As far as I know it's not possible to use dependency injection in typeorm entities, since they are not instantiated by nest. You can however use an EntitySubscriber instead which can inject dependencies. See the solution from this Github issue:
import { Injectable } from '#nestjs/common';
import { InjectConnection, InjectRepository } from '#nestjs/typeorm';
import { Connection, EntitySubscriberInterface, InsertEvent, Repository } from 'typeorm';
import { Post } from '../models';
#Injectable()
export class PostSubscriber implements EntitySubscriberInterface {
constructor(
#InjectConnection() readonly connection: Connection,
// Inject your repository here
#InjectRepository(Photo) private readonly postRepository: Repository<Post>,
) {
connection.subscribers.push(this);
}
listenTo() {
return Post;
}
beforeInsert(event: InsertEvent<Post>) {
// called before insert
};
}
I'm in the middle of developing a Graphql API with Nest JS (I just upgraded it to 5.0.0), in which I need to allow only certain users (by roles) to access sub-parts of queries that are public.
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
#Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const req = context.switchToHttp().getRequest();
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (!roles) // if no roles specified
return true;
console.log(req); // Displays Query() parent result instead of HTTP Request Object
return false;
}
}
Sub-parts of queries are handled by #ResolveProperty() decorated functions.
Following the documentation I started using Guards, but I just found out that when using guards, in GraphQL resolvers context, the context.switchToHttp().getRequest() is only returning the result of the parent Query()...
I don't really know if this is an expected behavior, and if it is, if there's a way to access full express Request object from a GraphQL Guard ?
I am trying to build a simple shopping cart application.
I have two components and a cart service like following.
<app-header></app-header>
<app-cart></app-cart>
In cart service has all the functionality for doing adding an item to cart, delete item in the cart, cart quantity etc.
I need to update the cart count in the header component when the user adds a product to the cart.
How to do that with a shared service.
In this case, You can use a service with subject. A service in Angular is a singleton, meaning it is managed as a single instance. So if each of the components access the service, they will access the same shared data.
export class cartService{
private prodCount = 0;
prodCountCountChange: Subject<number> = new Subject<number>();
UpdateCount(count: number) {
this.prodCount = count;
this.prodCountCountChange.next(this.prodCount);
}
}
In your component you can do this,
this._cartService.UpdateCount(this.prod.length);
In the header, you must subscribe to some event in the Shared Service. The same event will be emitted from your cart component.
Please have a look at this plunkr https://plnkr.co/edit/LS1uqB?p=preview
You should use Subject from RxJs. In your cart you will subscribe to some counter in your service so you'll able to update cart information in app-header.
In angular the components will share an instance of a service if it is from the same ancestor. For example you have your app-header and app-cart wich are part of the AppModule. So if you add SharedCartService to the providers array of your module, the two components will get the same instance of that service.
Dependency injection and hierarchical dependency injection
Create a service by :-
ng g service data
Upon running this, your output may look something like
installing service
create src\app\data.service.spec.ts
create src\app\data.service.ts
WARNING Service is generated but not provided, it must be provided to be used
The warning simply means that we have to add it to the providers property of the NgModule decorator in src/app/app.module.ts, so let's do that:
import { DataService } from './data.service';
#NgModule({
providers: [DataService],
})
Now that we have created a service, let's take a look at what the Angular CLI created:
import { Injectable } from '#angular/core';
#Injectable()
export class DataService {
constructor() { }
}
now create a function that holds your data in data.service.ts
constructor() { }
public currentData: any;
storeData(dataFromComponent){
this.currentData = dataFromComponent;
}
getData(){
return this.currentData;
}
now in app.component
import { DataService } from './data.service';
export class AppComponent {
constructor(private dataService:DataService) {
}
anytimeSaveData(){
// to store data in service call storeData function
this.dataService.storeData("Hello! I have some data here..");
}
}
to get data in another component just import our service and call getData function
export class HomeComponent {
constructor(private dataService:DataService) {
}
// to get data from service call getData function
console.log(this.dataService.getData());
}
Output in console
Hello! I have some data here..
Happy Coding