woowabros / nestjs-library-crud Goto Github PK
View Code? Open in Web Editor NEWAutomatically generate CRUD Rest API based on NestJS and TypeOrm
Home Page: https://www.npmjs.com/package/@nestjs-library/crud
License: MIT License
Automatically generate CRUD Rest API based on NestJS and TypeOrm
Home Page: https://www.npmjs.com/package/@nestjs-library/crud
License: MIT License
When I tried to use @nestjs-libary/crud version 0.4.6 in my cjs project, I got following error.
Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/dh.kang/workspace/woowa/nestjs-monorepo/node_modules/@nestjs-library/crud/dist/cjs/index.js from /Users/dh.kang/workspace/woowa/nestjs-monorepo/dist/packages/apps/opstool-hub/main.js not supported.
index.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in /Users/dh.kang/workspace/woowa/nestjs-monorepo/node_modules/@nestjs-library/crud/package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).
이번 우아콘 2023 동영상을 보고 써보고 있고 만족도가 높았습니다.
Search 시에
where : { user : { name : { operator: '=', operand: 'nickname' } } }
이렇게 relation 된 것도 검색이 되었으면 좋겠습니다.
Hi there, I have some question about exception occurred in this library.
Is there a reason for the exception thrown by the library is not instance of HttpException?
It seems that if some exception occurs while processing the request, it responses 500 internal server error, even when the exception is instance of HttpException. (ex. NotFoundException, UnprocessableEntityException, ...)
For example, if I request not existing entity data with route like /user/999
, it responses 500.
{
"statusCode": 500,
"message": "Internal server error"
}
NestJS default ExceptionFilter catch HttpException, so I expected it works, but it didn't.
I had to define my own ExceptionFilter that catches all Error, and apply it by using decorators
option of @Crud
.
@Crud({
entity: User,
routes: {
[Method.READ_ONE]: {
decorators: [UseFilters(new HttpExceptionFilter())],
}
}
})
In the catch method, I logged to check if the exception is instance of HttpException, and it says it is not instance of HttpException.
@Catch(Error)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
console.log(exception instanceof HttpException); // false
console.log(exception.name); // 'NotFoundException'
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception.getStatus(); // it works, so it has properties of HttpException
response.status(status).json({
statusCode: status,
message: "HttpException occurred",
});
}
}
Why is this happening?
If possible, I want to use default exception filter of NestJS.
Hello, Jeongmin Lee, jiho-kr , all the woowabros, and all the maintainers of this repo, first of all thank you for this amazing library.
I'm a novice in nestjs and TypeORM especially, so id really appreciate if someone can guide me on how i can help resolve the issue of not being able to use table inheritance.
The route factory is unable to resolve the entity name (i tried matching if the type of the table is not entity-child, but that led to "Cannot read properties of undefined (reading 'ownColumns')").
I started with unit tests (read-many only for now).
PR: #388
Hello, as always, thank you for the amazing lib,
Last week's feature #389 to be able to support table inheritence has rendered readMany and readOne to break, the lib wasnt able to read the metadata of the entity thus making it break them, reverted to 0.10.2 and everything works...
I can provider a minimal reproduction repo if necessary
Hello,
when using offset pagination it skips the first record in the database.
possible fixes are either allowing the offset to be 0 or using offset -1
Currently our entities are not extended from BaseEntity. is there a reason the type is BaseEntity for the services?
ive browsed through the source code and didnt find anything that would break if it allowed regular Entities.
Hello,
First off love the library and am excited to use it in a new project my company is working on. But I'm having some trouble with my user entity and I can't figure out why.
I'm following your guide, except my user is in a company NPM package and looks like this:
Extended from a BaseEntity which also extends BaseEntity:
package.json deps:
{ "@liaoliaots/nestjs-redis": "^9.0.5", "@nestjs-library/crud": "^0.8.8", "@nestjs/bull": "^10.0.1", "@nestjs/common": "^10.2.10", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.2.10", "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.2", "@nestjs/platform-express": "^10.2.10", "@nestjs/swagger": "^7.1.16", "@nestjs/throttler": "^5.0.1", "@nestjs/typeorm": "^10.0.1", "@sentry/node": "^7.85.0", "@sentry/tracing": "^7.85.0", "@swc/jest": "^0.2.29", "axios": "^1.6.2", "bcrypt": "^5.1.1", "bull": "^4.11.5", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie": "^0.6.0", "cookie-parser": "^1.4.6", "express": "^4.18.2", "fast-xml-parser": "^4.3.2", "fastify": "^4.24.3", "joi": "^17.11.0", "jsonwebtoken": "^9.0.2", "nanoid": "3", "nestjs-paginate": "^8.5.0", "nestjs-pino": "^3.5.0", "passport-apple": "^2.0.2", "passport-facebook": "^3.0.0", "passport-google-oauth20": "^2.0.0", "passport-jwt": "^4.0.1", "pg": "^8.11.3", "pino": "^8.16.2", "pino-http": "^8.5.1", "pino-webpack-plugin": "^2.0.0", "rxjs": "^7.8.1", "supertest": "^6.3.3", "typeorm": "^0.3.17" }
I'd appreciate any support you can lend since I feel I'm close and I don't wanna crud everything manually!
Hello once again, as always, thank you for the amazing work and swift response!
After some digging around, i found out that typeorm .save method going to always trigger the @BeforeInsert @AfterInsert hooks, so i was wondering if you can modify the crud.service to use .create and .update methods respectively.
If you guys see this modification fits the repo, i can submit a PR
Originally posted by woowahan-kibae February 11, 2023
CrudService에서 getRepository 라는 public getter로 저장소를 노출하고 있습니다.
get accessor 이름으로 getRepository 는 안맞는것 같아서 아래 두가지 방식 중 하나를 제안해 봅니다~
getRepository(): Repository<T> {
return this.repository;
}
constructor(protected readonly repository: Repository<T>) {...}
```</div>
Hi, there 👋🏼
I tried to use @crud with TypeORM's ViewEntity and found out that Cursor pagination of search method is not working as I expected.
I'll share the test code I have written to let you recognize the problem below.
import { Controller, INestApplication, Module } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { InjectRepository, TypeOrmModule, getDataSourceToken } from '@nestjs/typeorm';
import { IsNumber } from 'class-validator';
import request from 'supertest';
import { DataSource, Repository, ViewColumn, ViewEntity } from 'typeorm';
import { Crud, CrudService, Method } from '../../src';
import { BaseEntity } from '../base/base.entity';
import { TestHelper } from '../test.helper';
@ViewEntity('view_base_name', {
expression: `
SELECT DISTINCT id, name, type % 2 as category FROM base
`,
})
export class BaseNameView {
@ViewColumn()
id: string;
@ViewColumn()
name: string;
@ViewColumn()
@IsNumber({}, { groups: [Method.READ_MANY, Method.SEARCH] })
category: number;
}
class BaseNameViewService extends CrudService<BaseNameView> {
constructor(@InjectRepository(BaseNameView) repository: Repository<BaseNameView>) {
super(repository);
}
}
@Crud({ entity: BaseNameView, only: [Method.READ_MANY, Method.SEARCH] })
@Controller('base-name')
class BaseNameViewController {
constructor(public readonly crudService: BaseNameViewService) {}
}
@Module({
imports: [TypeOrmModule.forFeature([BaseEntity, BaseNameView])],
controllers: [BaseNameViewController],
providers: [BaseNameViewService],
})
class BaseNameViewModule {}
describe('Cursor Pagination View', () => {
let app: INestApplication;
let dataSource: DataSource;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [TestHelper.getTypeOrmPgsqlModule([BaseEntity, BaseNameView]), BaseNameViewModule],
}).compile();
app = module.createNestApplication();
await app.init();
dataSource = module.get<DataSource>(getDataSourceToken('default'));
await dataSource.query('DELETE FROM base');
await dataSource.query(`
INSERT INTO base (name, type, description) VALUES
('apple', 1, 'This is an apple'),
('bear', 2, 'This is a bear'),
('charlie', 3, 'This is charlie'),
('dog', 4, 'This is a dog'),
('elephant', 5, 'This is an elephant'),
('frog', 6, 'This is a frog'),
('giraffe', 7, 'This is a giraffe'),
('horse', 8, 'This is a horse'),
('iguana', 9, 'This is an iguana'),
('jaguar', 10, 'This is a jaguar'),
('kangaroo', 11, 'This is a kangaroo'),
('lion', 12, 'This is a lion'),
('monkey', 13, 'This is a monkey'),
('newt', 14, 'This is a newt'),
('owl', 15, 'This is an owl'),
('penguin', 16, 'This is a penguin'),
('quail', 17, 'This is a quail'),
('rabbit', 18, 'This is a rabbit'),
('snake', 19, 'This is a snake'),
('tiger', 20, 'This is a tiger'),
('unicorn', 21, 'This is a unicorn'),
('vulture', 22, 'This is a vulture'),
('whale', 23, 'This is a whale'),
('xerus', 24, 'This is a xerus'),
('yak', 25, 'This is a yak'),
('zebra', 26, 'This is a zebra')
`);
});
afterAll(async () => {
await dataSource.query('DELETE FROM base');
await app.close();
});
it('should be able to paginate', async () => {
const firstResponse = await request(app.getHttpServer())
.post('/base-name/search')
.send({ take: 5, order: { id: 'ASC' } });
expect(firstResponse.status).toBe(200);
expect(firstResponse.body.data.length).toBe(5);
const secondResponse = await request(app.getHttpServer()).post('/base-name/search').send({
nextCursor: firstResponse.body.metadata.nextCursor,
});
expect(secondResponse.status).toBe(200);
expect(secondResponse.body.data.length).toBe(5);
// each and every item in the second response should not be in the first response
for (const item of secondResponse.body.data) {
expect(firstResponse.body.data.every((firstItem: { id: any }) => firstItem.id !== item.id)).toBeTruthy();
}
});
});
I'm loving the library so far, but in order to take to take full advantage of it, it would be nice if we can provide support for injecting a custom TypeORM connection in order to implement multitenancy
routes
option should match with only
option in @crud
decorator. This could make this lib more robust I think.
@Crud({
entity: SkuBmartProductEntity,
only: [Method.CREATE, Method.UPDATE],
routes: {
create: {
decorators: [ApiOperation({ summary: 'some description' })],
},
// should show possible options only
// thus `delete?`... should not appear
},
})
Hello again woowabros, as always, thank you for this gold mine.
As for the issue, as described by the title, when using embedded entities, they are never returned in readOne and readMany, i tried adding them to the relation property but that didnt work too.
If unable to reproduce i can share a reporudction repo.
I may have made a mistake, but I was able to resolve the issue by making the following modifications.
not work:
...
@Entity()
export class User {
...
You may encounter an error message error TS2344: Type 'User' does not satisfy the constraint 'BaseEntity'.
or Error: Cannot find Entity name from TypeORM
.
try this:
import {
...,
BaseEntity
} from "typeorm";
@Entity('user')
export class User extends BaseEntity {
...
not work:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { CrudService } from '@nestjs-library/crud';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UserService extends CrudService<User> {
constructor(@InjectRepository(BaseEntity) repository: Repository<User>) {
super(repository);
}
}
try this:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { CrudService } from '@nestjs-library/crud';
import { Repository } from "typeorm";
import { User } from './user.entity';
@Injectable()
export class UsersService extends CrudService<User> {
constructor(@InjectRepository(User) repository: Repository<User>) {
super(repository);
}
}
BaseEntity
to User
Description:
When attempting to update an entity that has a PrimaryColumn decorator the update fails because it doesnt include the id of the entity in the mutation setting it to null. Tried using an interceptor to force the payload body to have he ID inferred from the path param but that resulted in a failed mutation too because it counted as a new insert that violates unique constraint.
Steps to Reproduce:
Define an entity with a PrimaryColumn primary key
Attempt to update
Expected Behavior:
Update successful and new entity returned.
Actual Behavior:
A 409 http error code exception is thrown that signals that the row's primary key is null
Environment:
Database: Postgres SQL
Library Version: v0.12.0
Node version: v21.5.0
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.