A set of opinionated NestJS extensions and modules
- @nestjsx/common - A set of NestJs decorators, interfaces and configuration for application modules bootstraping.
npm run test
NestJs CRUD for RESTful APIs
Home Page: https://github.com/nestjsx/crud/wiki
License: MIT License
A set of opinionated NestJS extensions and modules
npm run test
Hello,
Is it possible to mock my services with RepositoryService extended ?
Exemple I have a service and I want to test my customMethod
user.service.ts
@Injectable()
export class UsersService extends RepositoryService<User> {
constructor(
@InjectRepository(User)
private readonly usersRepository: Repository<User>,
) {
super(usersRepository);
}
public findByCognitoId(cognitoId: string): Observable<User> {
return from(
this.usersRepository.findOneOrFail({ cognitoId: cognitoId }),
);
}
public customMethod() {
return null;
}
}
user.service.spec.ts:
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{ provide: getRepositoryToken(User), useClass: Repository },
],
}).compile();
service = module.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
But I get an error : TypeError: Cannot read property 'columns' of undefined
thanks
Please provide support for the gateways in same way controller works in this package with an option like enableControllers(default true), enableGateways(default false).
For example, we would emit 'heroes' and it would return the array of heroes and 'heroes::heroId' would return hero with the specified id.
This would allow accessing rest over WebSockets. If possible allow its usage with WsAdapter instead of socket.io for performance.
I started implementing the mongoose.
@Crud(Role)
@Controller('roles')
export class RolesController implements CrudController<RolesService, Role> {
constructor(public service: RolesService) {}
}
PATCH /roles/2
Works and returns { id:2, ...updatedFields}
@Crud(Role)
@Controller('roles')
export class RolesController implements CrudController<RolesService, Role> {
constructor(public service: RolesService) {}
get base(): CrudController<RolesService, Role> {
return this;
}
@Override()
updateOne(@Param('id') id, @Param() params, @Body() dto) {
return this.base.updateOneBase(id, params, dto);
}
}
PATCH /roles/2
Does not work and returns { id:20, ...newField}
I think it might be correlated with this issue: #4
Motivation: #68
TypeORM has an ability to pass data as an additional free-shape param to repository operations, that is available on the query runner instance.
// user sends request during entity save
postRepository.save(post, { data: { request: request } });
// in logger you can access it this way:
logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
const requestUrl = queryRunner && queryRunner.data["request"] ? "(" + queryRunner.data["request"].url + ") " : "";
console.log(requestUrl + "executing query: " + sql);
}
The example is from the end of:
https://github.com/typeorm/typeorm/blob/master/docs/logging.md#using-custom-logger
If the passed data will be configurable somehow (maybe an async callback that gets request context of something more high-level/) it will be possible to implement #68 with
TL; DR;
I want to manually add/remove rows from other table in the public async updateOne(
of a spesific entity, say, Brands, in the same transaction of the builtin .save
operation,
With overriding little of code possible.
General motivation/background:
I have mysql database that the schema is created and managed manually.
I have Many To Many relations there, that i cannot express using TypeORM properly,
As TypeORM junction table is generated internally with a specific and and schema, and configureable.
So i can't teach it to use that table.
So, i've setup a OneToMany <-> ManyToOne relation between each of the entities and my custom Junction table.
(bt_brands <-> bt_brands_to_categories)
(bt_categories <-> bt_brands_to_categories)
Now the problem starts:
When posting new array to brands.categories (lets say empty array)
The generated query is
UPDATE bt_brands_to_categories SET bt_brand_id = null WHERE bt_brand_id = 123
My expectation is that to be a DELETE QUERY.
But ok, i left that (maybe you can help?)
i figured i can just DELETE / UPDATE rows myself, but i want to do it as part of the .save transaction.
And as i must completely override the updateOne
operation, and i can't use the super.updateOne for that
The lib is looking for field named id
automaticity,
my id name is different or sometimes i even don't have one/some times its composited
Thanks!
It it possible to hide some endpoints from a Crud controller? For example, if I wanted to expose a read only API, could I disable all end points accept for the GET
endpoints?
There is no option in the services or controller to specify eager relations? (include eager relations by default without specifying join
in the query params?)
Having a few issues starting with the example code outlined in the base readme.
It seems that when I import Crud before my entity in my controller I get a stack trace
TypeError: typeorm_1.Entity is not a function
at Object.<anonymous> (\src\app\posts\post.entity.ts:3:2)
import { Crud } from '@nestjsx/crud';
import { Post } from './post.entity';
However, when I import Post entity before Crud it seems to work.
Additionally, when I try to POST { "content": "blah" } it returns with a 500 with stack trace
TypeError: Cannot read property 'createOne' of undefined
at PostsController.createOneBase (\src\decorators\crud.decorator.ts:207:25)
Code:
Entity:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 500 })
content: string;
}
Controller
import { Post } from './post.entity';
import { Crud } from '@nestjsx/crud';
import { Controller } from '@nestjs/common';
import { PostsService } from './posts.service';
@Crud(Post)
@Controller('posts')
export class PostsController {
constructor(private readonly postService: PostsService) {}
}
Am I missing something obvious?
Thanks!
Hey,
I recently tried the demo project of nestjsx/crud (nestjsx/crud/integration/typeorm).
I recognized that the swagger documentation contains an additional id parameter for every route - even for "get all" or "create one", where it's not necessary to provide such an id at all.
In the code, the params
attribute is explicitly set to {}
(empty).
Is this a bug?
// ...
@Crud(Company, {
params: {},
routes: {},
options: {
join: {
users: {
exclude: ['password'],
},
},
sort: [{ field: 'id', order: 'DESC' }],
maxLimit: 5,
cache: 10000,
},
})
@ApiUseTags('companies')
@Controller('companies')
export class CompaniesController {
// ...
[EDIT]
In helpers.js
in function setDefaultCrudOptions()
the parameter id: number
is used by default:
function setDefaultCrudOptions(crudOptions, target) {
const check = (obj) => shared_utils_1.isNil(obj) || !shared_utils_1.isObject(obj) || !Object.keys(obj).length;
if (check(crudOptions.params)) {
crudOptions.params = { id: 'number' };
}
// ...
}
Is this really necessary? Since not every route has an id parameter.
High level goal:
I want to log all the DB operations with the email of the user that made them. in the same line.
TypeORM can accept a custom logger, but of course, TYPEORM has no concept of request/active user/ what so ever.
I can log the email of the request start. but i have no guarantee that no concurrent sql queries from different request will mix in the log.
If a particular optional relation is not present for the entity, the entry is not included in the join result.
crud/src/typeorm/repository-service.class.ts
Line 336 in 69da293
currently it returns list only. for paging, there needs more info like total
.
see https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/domain/PageImpl.java or https://github.com/pagehelper/Mybatis-PageHelper/blob/master/src/main/java/com/github/pagehelper/PageInfo.java
validation error shall be wrapped into an standard error object with fields name
, message
and other more rather than an array. In case, clients can decide the kind of the error easily.
Hi there, I was trying to implement some entity property modification method.
In this case, I was going to use @BeforeInsert and @BeforeUpdate decorators to add functionality to my entity to check if a new password was given, and if so, to hash it.
I have found a workaround by using @OverRide updateOne in controller.
But... I think it would be great if crud would support the entity events entirely.
What do you think about that? Are there plans to support these?
After upgrading to NestJS 6 the generated GET / and GET /:id will always return an empty object.
I realized by prototyping your solution that it does not support mongodb through TypeOrm.
Would it be too much work for me to implement it on mongodb? What would be your suggestion?
bye and congratulations
When I define my model in TypeORM as:
@Entity()
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ default: false })
isActivated: boolean;
}
I can use POST
without any problem. However when I use PATCH
it is expecting the id
to be a numeric string:
{
"statusCode": 400,
"error": "Bad Request",
"message": "Validation failed. Param 'id': numeric string is expected"
}
The body I've sent is:
{
"id": "6aca65b8-ac87-4db8-a492-f4461655b5fa",
"isActivated": true
}
Lines 5 to 14 in e255120
Only the last generated controller is effect. Previous controllers are ignored.
I want to add constraints on some fields of an entity should not be submitted for update but allowed for create
Is there a recommended approach ?
E.g, don’t allow user to update username
Here we have class A
, B
and C
class A{
a:string;
b:B;
}
class B{
b:string;
c:C;
}
class C{
c:string
}
I want to query A
, but join B
and C
. I want a result like...
[
{
"a": "a1",
"b": {
"b": "b1",
"c": {
"c": "c1"
}
}
},
{
"a": "a2",
"b": {
"b": "b2",
"c": {
"c": "c2"
}
}
}
]
but I can only join table with 1 level, missing all the c
in b
As of now, createOne
method in RepositoryService
requires a data
param to match EntityType
. What I propose is using DeepPartial<EntityType>
instead.
Current createOne:
createOne(data: EntityType, params: ...)
Using DeepPartial:
createOne(data: DeepPartial<EntityType>, params: ...)
Reasoning:
Would allow a service class to be used by a different controller without any errors regarding required columns which would be auto-generated by the Entity class.
Eg: AuthController using UsersService to create a new user. Now UsersService relies on UserEntity which has columns like createdAt and updatedAt which while are required columns but are auto-generated using the BeforeInsert
decorator in TypeOrm
Consistency with createMany({data: { bulk: DeepPartial<T>[] }} ...)
. There could be some reasoning behind it which I'm clearly not aware of but createMany
uses DeepPartial
This lib is very cool, but for my project I need to have a certain response format.
Currently, when we request a ressource, we have an array of this ressource as response.
Could we have for the futur some options like the total number of ressources, the number of pages, etc etc...
Because currently can't have a real pagination because we don't have this kind of information.
Thx.
Is it normal that the DELETE endpoint returns an empty response ? Shouldn't it return the deleted object or at least its id ? Tell me what you think.
support leftJoinAndMapOne
and leftJoinAndMapMany
http://typeorm.io/#/select-query-builder/joining-and-mapping-functionality
so we can flatten the results
Hello there,
You have made a great work in this so I was wondering if there's any way I can use sequelize instead of typeorm? and if so can you tell how and where to change? I've used sequelize many times and I'm aware of mostly all of its features and I'm not ready to try another ORM..
When use only query interceptor by adding @UsePathInterceptors('query')
, the @ParsedOptions()
will get undefined
. And pass it to base method will cause TypeError
: 'Cannot read property 'options' of undefined'.
The parsedOptions
shows below is undefined
.
crud/src/decorators/crud.decorator.ts
Line 70 in bbdadf9
Is this a feature or bug?
public async update(paramId: any, entity: T): Promise {
const exists = await this.getOne(paramId);
return await this.save(entity);
}
Here you are getting exists but not used. Can you please fix this.
consider this schema.
color:{ ..., car: { id: 1, . . . } }
when I query URL/color?join=car
it's give me joined objects but when I add the filter param to get color with specific car Id by query url/color?join=car&filter=car.id||eq|1
I get response { "statusCode": 400, "error": "Bad Request", "message": "Invalid column name 'car.id'" }
In the readme at https://github.com/nestjsx/crud#example-project there is a "here" link pointing to https://github.com/nestjsx/crud/tree/master/restful/integration/typeorm which doesn't exist anymore.
because of this
crud/src/decorators/crud.decorator.ts
Line 96 in e255120
Step 5 of example app is failing error given below. am I missing any step.
E:\git_clones\NestJS\crud\integration\typeorm>npm run serve
> [email protected] preserve E:\git_clones\NestJS\crud\integration\typeorm
> npm run db:sync -- -f=orm && npm run db:seeds -- -f=orm
> [email protected] db:sync E:\git_clones\NestJS\crud\integration\typeorm
> ts-node -r tsconfig-paths/register node_modules/.bin/typeorm schema:sync "-f=orm"
E:\git_clones\NestJS\crud\integration\typeorm\node_modules\.bin\typeorm:2
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
^^^^^^^
SyntaxError: missing ) after argument list
at new Script (vm.js:79:7)
at createScript (vm.js:251:10)
at Object.runInThisContext (vm.js:303:10)
at Module._compile (internal/modules/cjs/loader.js:657:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
at Object.<anonymous> (E:\git_clones\NestJS\crud\integration\typeorm\node_modules\ts-node\src\bin.ts:157:12)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] db:sync: `ts-node -r tsconfig-paths/register node_modules/.bin/typeorm schema:sync "-f=orm"`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] db:sync script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\TJ\AppData\Roaming\npm-cache\_logs\2019-02-01T07_35_46_818Z-debug.log
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] preserve: `npm run db:sync -- -f=orm && npm run db:seeds -- -f=orm`
npm ERR! Exit status 1
It doesn't load options
crud/src/module/crud.module.ts
Lines 9 to 11 in bbdadf9
crud/src/decorators/helpers.ts
Lines 322 to 326 in bbdadf9
Hello,
nice job dude!
i have a problem with the POST route.
i'm trying to send a json to the POST endpoint using the Postman app, the json looks like this:
{
"login": "nature",
"senha": "123",
"nome": "Nature"
}
but i receive a 400 error saying this:
{
"statusCode": 400,
"error": "Bad Request",
"message": "null value in column "login" violates not-null constraint"
}
it looks like the json that i'm trying to send is not being understood by the app server.
Can you help ?
PS: the other endpoints (GET) are working pretty well
@Crud(Role)
@Controller('roles')
export class RolesController implements CrudController<RolesService, Role> {
constructor(public service: RolesService) {}
}
PATCH /roles/2
Works and returns { id:2, ...updatedFields}
@Crud(Role)
@Controller('roles')
export class RolesController implements CrudController<RolesService, Role> {
constructor(public service: RolesService) {}
get base(): CrudController<RolesService, Role> {
return this;
}
@Override()
updateOne(@Param('id') id, @Param() params, @Body() dto) {
// id, params and dto are good here
return this.base.updateOneBase(id, params, dto);
}
}
PATCH /roles/2
Does not work because it returns a new record { id:14, ...newField}
I think it might be correlated with this issue: #4
The library is awesome it's working really well. Fields, Filters, they are working really well. but I tried join it didn't work.
Request:
http://localhost:3000/companies?join=users
Response:
[
{
"id": 1,
"name": null,
"description": null
},
{
"id": 2,
"name": null,
"description": null
},
{
"id": 3,
"name": "Sampath Bank",
"description": null
}
]
There is a users
relation in the company entity.
@OneToMany(type => User, a => a.company)
users?: User[];
Tried Filter, Fields and others. They are working. And there is data too. It worked when I try with typeorm directly.
I tried to implement a custom validator with class-validator https://github.com/typestack/class-validator#custom-validation-classes. I used the example project from this repository.
I added the following class:
import { ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator';
import { Injectable } from '@nestjs/common';
import { UsersService } from './users.service';
@ValidatorConstraint({ name: 'doesUserAlreadyExist', async: true })
@Injectable()
export class DoesUserAlreadyExist implements ValidatorConstraintInterface {
constructor(private usersService: UsersService) {}
async validate(text: string) {
const users = await this.usersService.getMany({
filter: [
{
field: 'email',
operator: 'eq',
value: text
}
]
});
return users.length === 0 ? true : false;
}
}
And added the validator to the User class (Entity):
...
@Validate(DoesUserAlreadyExist, {
message: 'User already exists',
})
@Column({ type: 'varchar', length: 255, nullable: false, unique: true })
email: string;
...
I get the following error:
/Users/kai/Development/NodeJS/crud/integration/typeorm/node_modules/@nestjs/typeorm/dist/common/typeorm.utils.js:8
return `${entity.name}Repository`;
^
TypeError: Cannot read property 'name' of undefined
at Object.getRepositoryToken (/Users/kai/Development/NodeJS/crud/integration/typeorm/node_modules/@nestjs/typeorm/dist/common/typeorm.utils.js:8:22)
at Object.exports.InjectRepository (/Users/kai/Development/NodeJS/crud/integration/typeorm/node_modules/@nestjs/typeorm/dist/common/typeorm.decorators.js:5:72)
at Object.<anonymous> (/Users/kai/Development/NodeJS/crud/integration/typeorm/src/users/users.service.ts:12:16)
at Module._compile (internal/modules/cjs/loader.js:738:30)
at Module.m._compile (/Users/kai/Development/NodeJS/crud/integration/typeorm/node_modules/ts-node/src/index.ts:439:23)
at Module._extensions..js (internal/modules/cjs/loader.js:749:10)
at Object.require.extensions.(anonymous function) [as .ts] (/Users/kai/Development/NodeJS/crud/integration/typeorm/node_modules/ts-node/src/index.ts:442:12)
at Module.load (internal/modules/cjs/loader.js:630:32)
at tryModuleLoad (internal/modules/cjs/loader.js:570:12)
at Function.Module._load (internal/modules/cjs/loader.js:562:3)
If I remove the injected UsersService
dependency injection from the DoesUserAlreadyExist
class everything works again.
Is it not possible to use the services which extends the RepositoryService
service? Or is there another way to execute the repository methods (getMany, getOne, etc.)?
query param ?filter=isOn||false
will generate sql isOn = 'false'
due to the false
being traded as string
routing-controllers
can even support json in query parameters.
Maybe we can do something like this when content-type
is application/json
:
try {
value = JSON.parse(value);
} catch (ignored) {}
A common need is to filter the entires returned by rest list, and to paginate over them.
It will be very use-full to have a build-in way to do it
This is really cool library.
I have some question.
I already have some role pipe to check user can access some api.
How to do this on this library??
My think is override method and set pipe to do this?
please give me some idea.
thanks!!
@zMotivat0r In my projects, it's often export file. And it shall be equivalent to table query. So I reuse getMany
method.
Do you have better solution?
this is 2.1.0
version
@ApiProduces(mime.getType('.xlsx'))
@UseInterceptors(RestfulQueryInterceptor)
@Get('/export/list.xlsx')
async export(@Res() res: any, @Param() params: ObjectLiteral, @Query() query: RestfulParamsDto) {
delete query.page;
delete query.limit;
const list = await this.base.getManyBase(params, query);
// do something e.g. generate an excel file
}
Is there a way to limit and filter joined entities?
I have entity relations:
I have GET /users/:user/reservations/:reservation/borrowings
endpoint.
BorrowingsController is looking for a field named user
in the Borrowing entity, which doesn't exist. So I get Invalid column name 'user'
error.
How can I achieve this ?
Hello~
I use this library and with swagger
I create a User entity with id and name fields
But I found the Models in swagger will shown User Model, but no any field in it.
How to fix it?
Thanks!
I want some of my entities to make specific joins by default.
is there a way to declare that?
Hello everyone, need help to solve this problem
In my case a have a table budgets
, with two OneToMany
relation for materials
and works
, this last two tables have a relation to units
, so i want to do join of everything
but i assume that RepositoryService use the last key as alias and they repeat, and return the error of the first pic.
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.