I want to update an existing column in a table with a value. but I'm not sure how to translate sql to nestjs on the queryRunner. I can't find update examples with this QueryRunner
import {MigrationInterface, QueryRunner} from "TypeORM";
export class Student12345 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`UPDATE "students"("status") SET "Active"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`UPDATE COLUMN "status" SET NULL`);
}
}
Can anyone help?
Related
This is not a duplicate Q. Please don't mark this as that.
Following is not I want
import { EntityRepository, Repository } from "typeorm";
import { Test } from "./test.model";
import { Injectable } from "#nestjs/common";
#EntityRepository(Test)
export class TestRepository extends Repository<Test> {}
the #EntityRepository decorator is now deprecated.
I also don't want to make a fake repository like in here:
https://stackoverflow.com/a/73352265/5420070
Don't want this either as I've to extract manager from dataSource, I don't want this because I think this is not the best way.
export const UserRepository = dataSource.getRepository(User).extend({
// ^^^^^^^^^^ from where this came from
findByName(firstName: string, lastName: string) {
return this.createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName })
.andWhere("user.lastName = :lastName", { lastName })
.getMany()
},
})
Found above in: https://orkhan.gitbook.io/typeorm/docs/custom-repository#how-to-create-custom-repository
I don't think this is in NestJS context.
What I want
Want to know right way to make custom repository in latest version of NestJS (v9) & TypeORM (v0.3). In #EntityRepository deprecation note, they said that need to extend the repo to create custom repo like someRepo.extend({}). I want to know how to do it in NestJS way
In order to achieve what you want, you could do something like the following.
This solution is inspired by the official NestJS docs related to this topic, with some customization.
Steps to achieve it:
Create your TypeOrm entity as usual, let's say UserEntity (user.entity.ts file)
Create a UserRepository class (user.repository.ts file)
Create a UserService class as usual (user.service.ts file)
Import the UserRepository into your UserService
Update UserModule in order to provide the UserRepository and needed UserEntity
Detailed implementation example
1. user.entity.ts file
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Column({ default: true })
isActive: boolean;
}
2. user.repository.ts file
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserEntity } from './user.entity';
export class UserRepository extends Repository<UserEntity> {
constructor(
#InjectRepository(UserEntity)
private userRepository: Repository<UserEntity>
) {
super(userRepository.target, userRepository.manager, userRepository.queryRunner);
}
// sample method for demo purposes
async findByEmail(email: string): Promise<UserEntity> {
return await this.userRepository.findOneBy({ email }); // could also be this.findOneBy({ email });, but depending on your IDE/TS settings, could warn that userRepository is not used though. Up to you to use either of the 2 methods
}
// your other custom methods in your repo...
}
3. & 4. user.service.ts file
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { UserRepository } from './user.repository';
import { UserEntity } from './user.entity';
#Injectable()
export class UserService {
constructor(
private readonly userRepository: UserRepository, // import as usual
) {}
findAll(): Promise<UserEntity[]> {
return this.userRepository.find();
}
// call your repo method
findOneByEmail(email: string): Promise<UserEntity> {
return this.userRepository.findByEmail({ email });
}
findOne(id: number): Promise<UserEntity> {
return this.userRepository.findOneBy({ id });
}
async remove(id: string): Promise<void> {
await this.userRepository.delete(id);
}
// your other custom methods in your service...
}
5. Updating UserModule (user.module.ts file)
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { UserEntity } from './user.entity';
#Module({
imports: [TypeOrmModule.forFeature([UserEntity])], // here we provide the TypeOrm support as usual, specifically for our UserEntity in this case
providers: [UserService, UserRepository], // here we provide our custom repo
controllers: [UserController],
exports: [UserService, UserRepository] // add this only if you use service and/or custom repo within another module/service
})
export class UserModule {}
With this in place, you should be able to import the UserModule in your AppModule and be able to both implement custom methods in the UserRepository and use them in the UserService. You should also be able to call the manager and queryRunnner of the custom repository.
Additional Note
If you need to directly call your UserRepository methods from within another module/service, then update UserModule to export the UserRepository
Hope it helps, don't hesitate to comment.
import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from "typeorm";
import { CustomBaseEntity } from "../core/custom-base.entity";//custom-made
#Entity({name: 'rcon_log', schema: 'dbo'})
export class LogEntity extends CustomBaseEntity{
------------your code-----------
}
My problem is that, I have this ActivityArticle entity that is has a OneToMany relationship to ActivityArticleImage but findManyEntities is does not include the ActivityArticleImage, but when I create a custom method it works.
Here is my service that needs to work:
#Injectable()
export class ActivityArticleService extends TypeOrmCrudService<ActivityArticle> {
constructor(
#InjectRepository(ActivityArticle)
private activityArticleRepository: Repository<ActivityArticle>,
) {
super(activityArticleRepository);
}
async findManyEntities(options: FindOptions<ActivityArticle>) {
return this.activityArticleRepository.find({
relations: ['articleImage'],
where: options.where,
});
}
async getArticlesWithImage() {
return await this.activityArticleRepository.find({
relations: ['articleImage'],
});
}
}
Controller code:
#Controller({
path: 'activity-article',
version: '1',
})
export class ActivityArticleController
implements CrudController<ActivityArticle>
{
constructor(public service: ActivityArticleService) {}
get base(): CrudController<ActivityArticle> {
return this;
}
#ApiOperation({ summary: 'Get articles with image.' })
#Get('article-with-image')
#HttpCode(HttpStatus.OK)
public async getArticlesWithImage() {
return this.service.getArticlesWithImage();
}
}
Sample output for findManyEntities
Sample output for getArticlesWithImage
How can I possibly work for findManyEntities because I need its this functionality that getArticlesWithImage doesnt have
Edit:
FindOptions code:
import { EntityCondition } from './entity-condition.type';
export type FindOptions<T> = {
where: EntityCondition<T>;
};
Activity Article Entity:
#Entity()
export class ActivityArticle extends EntityHelper {
#PrimaryGeneratedColumn('uuid')
id: string;
/*other fields here*/
#OneToMany(() => ActivityArticleImage, (articleImg) => articleImg.article)
articleImage: ActivityArticleImage[];
}
Acitivity-Article-Image entity:
#Entity()
export class ActivityArticleImage extends EntityHelper {
#PrimaryGeneratedColumn('uuid')
id: string;
/*other fields here*/
#ManyToOne(() => ActivityArticle, (article) => article.articleImage)
article: ActivityArticle;
}
I'm posting here because I have been stuck on a problem for few hours now.
I am creating an API using Nest JS 8 and MongoDB, and I test it using Postman. When I want to execute a POST request (http://localhost:3000?nom=Antoine) to insert an object in my database, I have an error (500 : Internal server error) message that says "Client validation failed: nom: Path 'nom' is required (nom is the name of my object's property).
I've wandered every topic about this kind of issue, tried to upgrade my version of Nest, to use a middleware, to make sure the right version of every depedency was installed.
I don't want to remove the "required: true" property because i think it is necessary. I tried to set it to "false", which enabled me to insert the object in the database but without my property 'nom' (name in french).
If you guys have any help, here's my schema :
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type ClientDocument = Client & Document;
#Schema()
export class Client {
#Prop({ required: true })
nom: string;
}
export const ClientSchema = SchemaFactory.createForClass(Client);
And here is my controller :
import { Body, Controller, Delete, Get, Param, Post, Put} from '#nestjs/common';
import { ClientService } from './client.service';
import { ClientDto } from './dto/client.dto';
import { CreateClientDto } from './dto/create-client.dto';
import { UpdateClientDto } from './dto/update-client.dto';
#Controller('/client')
export class ClientController {
constructor(private readonly clientService: ClientService) {}
#Get()
async index(){
return await this.clientService.findAll();
}
#Get(':id')
async find(#Param('id') id: string) {
return await this.clientService.findOne(id);
}
#Post()
async create(#Body() createClientDto: CreateClientDto) {
console.log(createClientDto);
return await this.clientService.create(createClientDto);
}
#Put(':id')
async update(#Param('id') id: string, #Body() updateClientDto: ClientDto) {
return await this.clientService.update(id, updateClientDto);
}
#Delete(':id')
async delete(#Param('id') id: string) {
return await this.clientService.delete(id);
}
}
Thanks for looking
I found the solution (i still don't know why it works this way tho).
In my client.service.ts, i updated the create function from this :
async create(createClientDto: CreateClientDto): Promise<Client> {
return await new this.model({createClientDto}).save();
}
To this
async create(createClientDto: CreateClientDto): Promise<Client> {
return await new this.model({
...createClientDto,
createdAt: new Date(),
}).save();
}
Thanks for taking the time to answer, I hope this will help
I create POST endpoint to create a new entity.
I also created schema for mongoose with field userId (to connect this entity to specified user) and DTO which I use on my POST method.
#UseGuards(JwtAuthGuard)
#Post("/")
createAction(#Request() req, #Body() createActionDto: CreateActionDto) {
return this.actionService.createAction(req?.user?.userId, createActionDto);
}
DTO:
import { IsString, IsNumber, IsUrl } from 'class-validator';
export class CreateActionDto {
userId: string;
#IsString()
name: string;
#IsNumber()
timeStart: number;
}
Schema:
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
#Schema()
export class Action extends Document {
#Prop()
userId: string;
#Prop()
name: string;
#Prop()
timeStart: number;
}
export const ActionSchema = SchemaFactory.createForClass(Action)
In the req property I have userId. What is the best way to create an entity and attach userId extracted from token?
Should I pass req to the service, and in the service set userId property on DTO like this?:
#Injectable()
export class ActionService {
constructor(
#InjectModel(Action.name) private actionModel: Model<Action>,
) { }
async createAction(req: string, createActionDto: CreateActionDto) {
createActionDto.userId = req.user.userId
// ... save to mongoose createActionDto
}
}
Is it a correct solution or there is another, a better way to deal with it?
Personally I would set the userId in the controller in order to not having to pass it around:
#UseGuards(JwtAuthGuard)
#Post("/")
createAction(#Request() req, #Body() createActionDto: CreateActionDto) {
createActionDto.userId = req?.user?.userId;
return this.actionService.createAction(createActionDto);
}
If you have many different controllers and DTOs that require the userId you could also define an Interceptor and do it there in order to reduce duplication:
#Injectable()
export class SetUserIdInterceptor implements NestInterceptor {
public intercept(_context: ExecutionContext, $next: CallHandler): Observable<any> {
const request: any = _context.switchToHttp().getRequest(); //instead of any you could also define a super-class for all DTOs that require the `userId`-property
request.body?.userId = req?.user?.userId;
return $next;
}
}
You can then use this interceptor on your route as follows:
#UseGuards(JwtAuthGuard)
#Post("/")
#UseInterceptors(SetUserIdInterceptor)
createAction(#Body() createActionDto: CreateActionDto) {
return this.actionService.createAction(createActionDto)
}
I'm using NestJs framework with Sequelize Typescript for Node where I'm trying to create a many to many relation between a user and a webpage where many users can have the same site in their favorites.
Now my problem is that when I make the query it limits the result to a single entry, while querying the database directly with the exact same query returns all expected entries.
This is my NestJs favorites entity where I define the favorite table:
// favorite.entity.ts
import { Table, Column, Model, PrimaryKey, ForeignKey, BelongsTo, NotNull } from "sequelize-typescript";
import { IDefineOptions } from "sequelize-typescript/lib/interfaces/IDefineOptions";
import { UserEntity } from "../users/user.entity";
import { SiteEntity } from "../sites/site.entity";
const tableOptions: IDefineOptions = {
timestamp: true,
tableName: "favorites",
schema: process.env.DB_SCHEMA,
} as IDefineOptions;
#Table(tableOptions)
export class FavoriteEntity extends Model<FavoriteEntity> {
#BelongsTo(() => UserEntity)
user: UserEntity;
#ForeignKey(() => UserEntity)
#PrimaryKey
#NotNull
#Column
userId: number;
#BelongsTo(() => SiteEntity)
site: SiteEntity;
#ForeignKey(() => SiteEntity)
#PrimaryKey
#NotNull
#Column
siteId: number;
}
And my service where I make the Sequelize query:
// favorite.service.ts
import { Inject, Injectable } from "#nestjs/common";
import { Model } from "sequelize-typescript";
import { IFavoriteService } from "./interfaces";
import { FavoriteEntity } from "./favorite.entity";
#Injectable()
export class FavoriteService implements IFavoriteService {
constructor(
#Inject("FavoriteRepository") private readonly favoriteRepository: typeof Model,
#Inject("SequelizeInstance") private readonly sequelizeInstance,
) {}
public async findByUserId(userId: number): Promise<FavoriteEntity | null> {
return await FavoriteEntity.scope().find<FavoriteEntity>({
logging: console.log,
where: { userId },
});
}
}
The logged out SQL statement is:
Executing (default): SELECT "userId", "siteId" FROM "public"."favorites" AS "FavoriteEntity" WHERE "FavoriteEntity"."userId" = '1';
And results in a single entry as result (there are many rows in the db)...
{
"userId": 1,
"siteId": 1650
}
Have I made some mistake in my entity or might it be in my sequelize query?
Thanks!
what is your result if you use findAll instead of find.but i am agree the query looks nice
mottosson are you sure the same sql query returns many rows? Because it will be odd.