GithubHelp home page GithubHelp logo

doug-martin / nestjs-query Goto Github PK

View Code? Open in Web Editor NEW
804.0 16.0 143.0 35.22 MB

Easy CRUD for GraphQL.

Home Page: https://doug-martin.github.io/nestjs-query

License: MIT License

JavaScript 0.31% TypeScript 99.68% Shell 0.01%
graphql nestjs nestjs-graphql typeorm typegraphql crud sequelize mongoose mongodb graphql-resolver

nestjs-query's Introduction

Nestjs-query Logo

Test Coverage Status Known Vulnerabilities

nestjs-query

Nestjs-Query is collection of packages to make crud for graphql easier.

Why?

While working on projects in nestjs it was very easy to get up and running with graphql however, there were many patterns that were common between the resolvers. In particular querying, sorting and paging.

Installation

Install Guide.

Docs

Packages

Nestjs-query is composed of multiple packages

  • @nestjs-query/core - Defines all interfaces and utility types implemented by the other packages.
  • @nestjs-query/query-graphql - Package that provides the graphql resolver and decorators for crud endpoints.
  • @nestjs-query/query-typeorm - Package that implements a Typeorm service that can be used by itself or with the graphql resolver provided by @nestjs-query/query-graphql.
  • @nestjs-query/query-sequelize - Package that implements a Sequelize service that can be used by itself or with the graphql resolver provided by @nestjs-query/query-graphql.

nestjs-query's People

Contributors

aguilar8788 avatar aleksey-tk avatar beeman avatar boxcc avatar davidevernizzi avatar doug-martin avatar fishtheoriginal avatar flusinerd avatar johannesschobel avatar johndoeplusplus avatar khawarizmus avatar koolamusic avatar marian2js avatar marojeee avatar mattleff avatar mtt88 avatar mwoelk avatar nicknisi avatar onichandame avatar psteinroe avatar renovate-bot avatar renovate[bot] avatar robertplantodro avatar ruiluntran avatar ruzz311 avatar smolinari avatar tillsanders avatar yiin avatar zackerydev avatar zhangciwu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nestjs-query's Issues

NestJs 7,0 BREAKING CHANGES

Dear @doug-martin ,

i hope you are well?!
I just found out, that nestjs v7.0 was released during the weekend. There are some really hard breaking changes, especially in the context of GraphQL. I am not sure if you already found the release notes:
https://trilon.io/blog/announcing-nestjs-7-whats-new#GraphQL-TypeScript

The most crucial change is, that the "code-first" approach with type-graphql was dropped. In this context, the nestjs core team created a similar package with a mostly identical external API. Basically, one need to change the imports from type-graphql to @nestjs/graphql - that should do the trick, that's what they write in their migration notes ๐Ÿ˜†

do you have some more information?
stay well and keep up your great work,
Johannes

[Question]: Filter By Relationships

Dear @doug-martin ,

once again - I have a question regarding the use of your library.
Consider the following example:

I have three Entities, which are linked. One Person has written many Posts, one Post belongs to one Board. Likewise, a Board contains many Posts, one Post belongs to one Person. The relationships are all set up via TypeOrm via @ManyToOne(). Furthermore, i have dedicated @RelationId() decorators to get the plain value of the referenced id.

Now i would like to create a new Custom Resolver function, which is called getPostsByPersonInBoard (awesome name, i know!)

Therefore, i followed the docs on your site, and did the following:

  • Create a new ConnectionType:
// custom.types.ts
export const PostConnection = ConnectionType(PostObject);
  • Create a new ArgsType() for this @Query()
// custom.types.ts
@ArgsType()
export class GetPostsByPersonAndBoardArgsType extends QueryArgsType(
  PostObject,
) {
  @IsString()
  @IsUUID('4')
  @Field()
  boardId: string;

  @IsString()
  @IsUUID('4')
  @Field()
  personId: string;
}

Following these 2 steps, I create the "request and response types" for this query.

  • Create the custom resolver function
    Finally, i create my custom resolver function like this:
  @Query((returns) => PostConnection, {
    name: 'getPostsForPersonAndBoard',
  })
  async getPostsForPersonAndBoard(
    @Args() query: GetPostsByPersonAndBoardArgsType,
  ) {

    const posts = await this.postsDatabaseService.query({
      paging: query.paging,
      sorting: query.sorting,
      filter: {
        // <-- what do i do here?!
      },
    });

    const transformedPosts = this.service.assembler.convertToDTOs(posts);

    return ScreeningConnection.createFromArray(
      transformedPosts,
      query.paging || {},
    );
  }

The problem is, that i need to build a filter that allows me to query for these 2 particular relationships that are passed within the query.boardId and query.personId respectively.

Problem is, that none of my approaches worked so far. What i tried was to create a filter like this:

filter: {
  personId: { eq: query.personId },
  // ...
}

This fails because there is no property PostEntity.personId. Respective relationship is called PostEntity.person (relationship) or PostEntity._personId (for the @RelationshipId() field). Changing the filter to use person or _personId has not worked so far for me either.

How can i achieve my goal?
Thank you very much for your kind help!

All the best

Many-To-Many Relationship Query Problem

Dear @doug-martin ,

and once again I write to you to describe an issue i have found ;) Sorry for bothering you again ;)
The issue i stumbled upon is with Many to Many relationships between entities.

Consider the following example:
I have two entities Organization and Doctor. Thereby, one organization has many doctors and one doctor can be a member in various organizations.

My models look like this (simplified):

@Entity('organizations', {})
export class OrganizationEntity {
  // ... various properties here

  @ManyToMany(
    type => DoctorEntity,
    member => member.organizations,
    {
      cascade: true,
      onDelete: 'CASCADE'
    }
  )
  @JoinTable({ name: 'organization_members' })
  members: DoctorEntity[];
}

@Entity('doctors', {})
export class DoctorEntity {
  // ... various properties here

  @ManyToMany(
    type => OrganizationEntity,
    organization => organization.members
  )
  organizations: OrganizationEntity[];
}

Then i set up my OrganizationResolver like this:

@Resolver(of => OrganizationObject)
export class OrganizationResolver extends CRUDResolver(OrganizationObject, {
  create: { many: { disabled: true }, CreateDTOClass: CreateOrganizationInput },
  delete: { disabled: true },
  update: { many: { disabled: true }, UpdateDTOClass: UpdateOrganizationInput },
  relations: {
    many: {
      members: {
        DTO: DoctorObject,
        relationName: 'members',
        nullable: true,
        disableRemove: true,
        disableUpdate: false
      }
    }
  }
}) {
  constructor(readonly service: OrganizationService) {
    super(service);
  }
}

Note that assigning a doctor (member) to an organization works like a charm, via the auto-generated endpoint from relations.many.members.disableUpdate: false.

However, i am NOT able to properly query (!) the members of an organization. Consider this query:

{
  organizations {
    edges {
      node {
        id,
        members {
          edges {
            node {
              id
            }
          }
        }
      }
    }
  }
}

results in this response:

{
  "data": {
    "organizations": {
      "edges": [
        {
          "node": {
            "id": "46fb76f1-781b-400b-8932-43ee9e809d1e",
            "members": {
              "edges": [
                {
                  "node": {
                    "id": "3727f166-ceb4-4693-9376-69e8be34c9d1"
                  }
                }
              ]
            }
          }
        },
        {
          "node": {
            "id": "ea23adb4-8e29-40c2-bf33-75e6064266c5",
            "members": {
              "edges": [
                {
                  "node": {
                    "id": "3727f166-ceb4-4693-9376-69e8be34c9d1"
                  }
                }
              ]
            }
          }
        },
        // ... many more entries 
      ]
    }
  }
}

See that all organizations are connected to the same doctor? well - that is certainly not true from the DB point-of-view. The organization_members table (join-table) certainly has only one relation between one doctor and one organization.

When i logged the queries sent to the database, i got this result:

query: SELECT "OrganizationEntity"."id" AS "OrganizationEntity_id", "OrganizationEntity"."createdAt" AS "OrganizationEntity_createdAt", "OrganizationEntity"."updatedAt" AS "OrganizationEntity_updatedAt", "OrganizationEntity"."version" AS "OrganizationEntity_version", "OrganizationEntity"."name" AS "OrganizationEntity_name", "OrganizationEntity"."description" AS "OrganizationEntity_description", "OrganizationEntity"."type" AS "OrganizationEntity_type", "OrganizationEntity"."address" AS "OrganizationEntity_address", "OrganizationEntity"."phone" AS "OrganizationEntity_phone", "OrganizationEntity"."email" AS "OrganizationEntity_email", "OrganizationEntity"."url" AS "OrganizationEntity_url", "OrganizationEntity"."logo" AS "OrganizationEntity_logo" FROM "organizations" "OrganizationEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10
query: SELECT "DoctorEntity"."id" AS "DoctorEntity_id", "DoctorEntity"."createdAt" AS "DoctorEntity_createdAt", "DoctorEntity"."updatedAt" AS "DoctorEntity_updatedAt", "DoctorEntity"."version" AS "DoctorEntity_version", "DoctorEntity"."keycloakId" AS "DoctorEntity_keycloakId", "DoctorEntity"."title" AS "DoctorEntity_title", "DoctorEntity"."firstname" AS "DoctorEntity_firstname", "DoctorEntity"."lastname" AS "DoctorEntity_lastname", "DoctorEntity"."phone" AS "DoctorEntity_phone", "DoctorEntity"."email" AS "DoctorEntity_email", "DoctorEntity"."url" AS "DoctorEntity_url", "DoctorEntity"."picture" AS "DoctorEntity_picture", "DoctorEntity"."lastLoginAt" AS "DoctorEntity_lastLoginAt", "DoctorEntity"."acceptedTOS" AS "DoctorEntity_acceptedTOS", "DoctorEntity"."settings" AS "DoctorEntity_settings" FROM "doctors" "DoctorEntity" LIMIT 10

Basically - it first fetches a paginated set of Organizations from the database (this is correct).
However, then it fetches n times from the doctors table - without going through the join-table relationship.

Furthermore, this query does only work "from this side". When querying from the other side (like this), this results in an error:

{
  doctors {
    edges {
      node {
        id        
        organizations {
          edges {
            node {
              id
            }
          }
        }
      }
    }
  }
}

throws this error:

error: error: column OrganizationEntity.organizationsId does not exist

and of course it does not exist, because respective column should be located within the organization_members table.

Am i doing something wrong? I guess, the many-to-many relations are not (yet) supported? Am i correct on this one?

All the best and thank you very much for your time and effort!
Johannes

It's support dataloader?

It is a really amazing nestjs graphql library!!
I tried this library for example code, and i found some problem.
I used many Relations in resolver, it work fine!, but will cause n+1 problem.
I think can add a batch option to use any batch function to resolve it.

[Suggestion] Update many input as array

Hello @doug-martin,

We are still using your awesome library but today we hit a wall with multiple update.
Let's say we want for an entity to set all entities with an id below 10 to status A, all entites with id between 10 and 20 to status B, etc.

The only we to do it we found was to make multiple call since updateMany does not take an array as input and we can't call multiple time the same mutation.

Wouldn't it be better for updateMany functions to accept arrays of updateManyXXInput instead of just one ?

[Improvement]: SoftDelete include Deleted Entries

Dear @doug-martin ,

in #54 you stated:

I couldnt not find a way to force reading deleted records, but the docs around this feature are really sparse at the moment, so the may be a way I just am unaware of it.

As promised, i looked into the issue and found a quite easy solution for this issue ๐Ÿ˜†

Please see my (very dirty, haha) solution below:

@Resolver((of) => FaqObject)
export class FaqResolver extends CRUDResolver(FaqObject, {
  // ...
}) {
  constructor(
    readonly assemblerService: FaqAssemblerService,
    readonly dbService: FaqDatabaseService,
  ) {
    super(assemblerService);
  }

  @Query((returns) => [FaqObject], {
    name: 'faqsWithDeleted',
    nullable: true,
  })
  faqsWithDeleted(): Promise<FaqObject[]> {
    const repo = this.dbService.repo;
    const data = repo.find({ withDeleted: true }); // <-- this is the key part!
    return data;
  }
}

We could provide a new parameter for the CRUDResolver similar to read and call it readDeleted. This param, in turn, would generate 2 additional query endpoints that you may call. FInally, these queries would add the withDeleted: true parameter to the repository.findXXX() call. That should actually do the trick..

However, another solution could / would be to just add an additional configuration parameter to the read stuff. What do you think?

I think it would be best to have dedicated query methods that can also be decorated with Guards and stuff like this. For example, i would like to have only admin users to call the withDeleted queries. In this course, i would highly suggest to have a similar configuration like read.

Possible solutions:

  1. With a dedicated parameter:
{
  readWithDeleted: { 
    many: { guards: [AdminGuard] }, 
    one: { guards: [AdminGuard] } 
  } 
}
  1. Within the read parameter:
{
  read: { 
    many: { disabled: false }, 
    one: { disabled: false }, 
    manyWithDeleted: { guards: [AdminGuard] },
    oneWithDeleted: { guards: [AdminGuard] }
  } 
}

What do you think?
As always, thanks for reading and giving feedback!

All the best

[Question / Feature Request] Support for filterable array fields

Is there any way to have a filterable array field? For example, I have a type/entity with an array of tags like so:

@Field(type => [String])
@Column('jsonb', { default: [] })
tags: string[]

If I change the @Field decorator to a @FilterableField decorator, it breaks with a TypeError: Cannot read property 'charAt' of undefined error. I assume this is because filterable array fields are not supported? I didn't find anything about it in the docs.

I would like to be able to query for records that contain a specific tag(s) within the tags array or that have a certain length of the tags array.

hasPreviousPage always return false

I using paging with first and after to change my cursor in my graph query but I get hasPreviousPage always return false, I think it is a bug?

[Question] Any way to catch errors from database and display in an elegant way

Hi @doug-martin
Actually, I'm a newbie with Nestjs and Graphql, so I have a question, I'm thinking about how to catch and custom errors returned by Graphql in an elegant way. Because when I try to add a unique value to database, it's failed and display a lot of info I don't want expose.

I know we can use formatError and exception filter to do that, but we will have many type of errors like validation error, authentication error, database error, vv and I just want to return in one format like this:

errors: [
  {
    statusCode: 400,
    message: "Bad request"
  }
]

I don't know best practices to follow for error handling while working with Graphql

Validate Input for Create & Update

Dear @doug-martin ,

i was trying to validate input for my CreateX and UpdateX mutations. Note that i use the default ValidationPipe provided by @nestjs/common and class-validator package.

My ValidationPipe is defined as follows:

app.useGlobalPipes(
    new ValidationPipe({
      transform: true,
      whitelist: true,
      forbidNonWhitelisted: true,
      skipMissingProperties: false,
      forbidUnknownValues: true
    })
  );

This definition, in turn, states, that ONLY defined attributes may pass, unknown properties automatically result in errors and so on. As i register the ValidationPipe as a GlobalPipe it is automatically applied to all endpoints! I can confirm, that this works for me.

While this may be better from the securities point of view, this results in the following issue: As the data sent to the backend is "wrapped" in the input param, it cannot be validated. Note, that the ValidationPipe will exit with a failure, as it detects an undeclared field input.

As i do not want to miss out on this feature, i digged into your library and found that it is possible to write custom ArgTypes for Create/Update - which is awesome ๐Ÿ‘ .

I then created my own Type like this:

import { Type } from 'class-transformer';
import { ValidateNested } from 'class-validator';
import { ArgsType, Field } from 'type-graphql';
import { CreateIdentityInput } from './modules/identity/ui/graphql/inputs/create-identity.input';

@ArgsType()
export class CreateOneUserArgs extends CreateOneArgsType(
  CreateUserInput
) {
  @Type(() => CreateUserInput)
  @ValidateNested()
  @Field({
    description: 'the resource to be created'
  })
  input!: CreateUserInput;
}

What this basically does:

  • @Type(() => CreateUserInput) maps the content of the variable to respective class (i.e., it is mapped to the CreateUserInput class).
  • @ValidateNested() tells the class-validator package, to ALSO validate the rules in the CreateUserInput class (e.g., the field email must be a valid email, username must be at least 5 chars long, ...)

So my question is:
Would it be ok for you to merge such changes into your own code-base? I guess, the Validation feature would be a significant improvement for your library. What do you think? I would be willing to make a PR - because it actually would save me a lot of time instead of creating my own custom ArgsType()?

From the first glimpse, those chances may be directly applied to the CreateOneArgsType and UpdateOneArgsType. For the CreateManyArgsType and UpdateManyArgsType we may need to use the @ValidateNested({ each: true }) option, in order to validate each element individually..

All the best,
Johannes

[Bug]: GetById(NULL)

Dear @doug-martin ,

i recently noticed a bug (at least i call it a bug) in the typeorm package with the DatabaseService / AssemblerService.getById() and .findById() methods.

Both methods return the first entity in the table if you call them with null or undefined as parameter.

For example:

const user = this.userAssemblerService.getById(null);

returns the first user from the table - which is clearly wrong ;)
What do you think, we should handle this? I can provide a PR, if you like

UPDATE
I just noticed, that there are no test-cases for this scenario. However, in your repository, the IDE correctly mentions the latter as error (if i pass null ). Are there any specific configurations you have enabled?

Add support for adding custom decorators to endpoints

@doug-martin
I found that the library has support for adding filters, pipes, guards, but I don't know if I want to add @Permissions(['READ', 'UPDATE']) to built-in endpoints. Like what I did for custom methods:

@Query(() => UserDTO)
@UseGuards(JwtAuthGuard, GqlAuthGuard)
@Permissions(['READ', 'UPDATE'])
async me(@CurrentUser() user: User): Promise<UserDTO> {
  return this.service.findById(user.id);
}

[Question]: How to properly use the AssemblerService

Dear @doug-martin ,

how can i properly use the Assembler within the Resolver?
For example, in #41 we have excessively discussed that the Service should work on Entities and not on Objects. Your newly published v 0.6.0 version already introduced these changes ๐Ÿ‘

However, i would like to use the AssemblerService within the Resolver.

My use-case looks like this:

  1. add a custom @Mutation or @Query method in my resolver
  2. this method uses a custom method in the underlying service. This method, in turn, works on the Entities and not on DTOs. That was the reason to introduce the changes described in #41 .
  3. The result of this method may be a changed Entity or whatever
  4. The latter has to be transformed to a DTO (ObjectType) in order to be returned via the GQL endpoint.

However, with the newly introduced AssemblerService that wraps the QueryService, it is not possible to achieve the latter.

The QueryService and AssemblerService as still coupled together, because the AssemblerService internally uses the QueryService. Wouldn't it be better to have a dedicated AssemblerModule that handles the transformation? Because this way it would be easy to "plug in" the Assembler where they are needed.

I would really like to see something like this (be aware, this is quite ugly dummy code, haha):

// user.module.ts
@Module({
  imports: [TypeOrmModule.forFeature([UserEntity]), AssemblerModule],
  providers: [UserResolver, UserService],
})
export class UserModule {}

// user.resolver.ts
@Resolver(of => UserObject)
export class UserResolver extends CRUDResolver(UserObject, {
  create: { ... },
  // ... custom configuration here
}) {
  constructor(
    readonly service: UserService,
    readonly assemblerService: DefaultAssembler<UserObject, UserEntity>, // or a dedicated UserAssembler if the transformation is complex!
  ) {
    super(service);
  }

  @Mutation(returns => UserObject, {...} )
  async doFancyStuff(@Args('input') input: ... ): Promise<UserObject> {
    const entity = await this.service.findById(id);
    const a = await this.service.customMethod(entitiy); // this returns a UserEntity
    return await this.assemblerService.convertToDto(a);
  }
}

What do you think?

All the best,
Johannes

[Question] - Filter on Relation fields

Hello Doug

I started working on GraphQL just this week so I am a total beginner. I came across your abstraction for CRUD operation's and it seems to fit my requirement very well. I do have a question for you. I have the following relation(Psuedo code):

class Car {
  name: string;
  @OnetoOne() manufacturer: Manufacturer
}

class Manufacturer {
  name: string;
}

Frontend client is showing all of this data as flat list meaning:

Car Name | Manufacturer Name

With the standard setup and by using Relations from your library, everything works well. However those fields should be filterable specifically the manufacturer name. Does this require me to implement my own CRUD resolver and sort of have a flat list DTO to represent the flat list the frontend expects ?

[Question / Feature Request] Add support for extending abstract object types

I currently have something like the following:

@Entity()
@ObjectType()
export class User extends BaseEntity {
  @FilterableField()
  @Column()
  name: string
  //...
}
@ObjectType({ isAbstract: true })
export abstract class BaseEntity {
  @FilterableField(type => ID)
  @PrimaryColumn()
  id: string
  //...
}

The generated schema will properly include id and other properties from BaseEntity in the User type, however, these properties are not included in generated types related to filtering and sorting, only the properties of class User are available.

Is there something I'm doing wrong or is this not supported? If not, I would like to be able to separate properties of my entities into reusable classes, please consider adding support for this.

Create Mutations are allowing Updates

Create mutations are allowing Updates to user data.

If this is what we want then it should be documented. If not then it should raise and error or just return the old data.

I think my preference is for it to return the old data.

[DOCS] Relations on an entity/dto

Add a section in the docs about entities with relations and decorating with a @Field resolver. This wont work because the relations are handled through the CRUD resolver.

[Question]: Support SoftDeleting

Dear @doug-martin ,

yesterday evening i stumbled upon this awesome PR in the typeORM repository: typeorm/typeorm#5034

This PR, basically, adds the whole soft delete stuff to the Repository classes and so on. Best thing is, it has already been merged and is ready to be used. The docs of the project, however, does not mention it (yet).

So here is my question:
Shall we add support for softDeleting entities in this package as well?
I was thinking about something like this:

// user.resolver.ts
@Resolver(of => UserObject)
export class UserResolver extends CRUDResolver(UserObject, {
  // ...
  delete: { ..., softDelete: true },
  read: { ..., includeSoftDeleted: true }
}) {
  constructor(readonly service: UserService) {
    super(service);
  }
}

For the delete prop, we could add a new property softDelete: boolean to all levels (i.e.,delete, delete.one and delete.many). Then we add some new methods to the QueryService interface and the TypeOrmQueryService respectively, to implement the following methods

export interface QueryService<DTO> {
  // ... already defined methods
  /**
   * SoftDelete a single record by id.
   * @param id - the id of the record to softDelete.
   */
  softDeleteOne(id: number | string): Promise<DTO>;

  /**
   * SoftDelete multiple records using a filter.
   *
   * @param filter - the filter to find records to softDelete.
   */
  softDeleteMany(filter: Filter<DTO>): Promise<DeleteManyResponse>;

  /**
   * Restore a single record by id.
   * @param id - the id of the record to be restored.
   */
  restoreOne(id: number | string): Promise<DTO>;

  /**
   * Restore multiple records using a filter.
   *
   * @param filter - the filter to find records to be restored.
   */
  restoreMany(filter: Filter<DTO>): Promise<UpdateManyResponse>; // i assume that it will be a UpdateManyResponse ;)
}

And in the respective service use the this.repo.softRemove() method instead the this.repo.remove().

Note that all find() and query() methods should respect the softDeleted entities by default. I.e., they are not present in the resultset anymore. If there should be additional routes to output the soft-deleted ones (i.e., only the softdeleted ones or all (non-deleted and soft-deleted)), additional find(...) methods should also be added.

Finally, the soft-delete stuff also works on relationships, which is soooooooooooooo awesome. The author of this PR added an additional cascade option to the @XToY() decorators to soft-delete over relationships.

What do you think about this feature?
All the best,
Johannes

@FilterableField() with nullable:true throws error

Dear @doug-martin ,

a

@FilterableField({
  description: 'firstname of the user',
  nullable: true
})
firstname?: string

throws an error when requesting this field in a query (and if it is actually null).

Changing from @FilterableField() to @Field() removes this issue, however, it is not possible to search by this field any more..

All the best

[Question / Request]: Expose Repository in AssemblerService

Dear @doug-martin ,

and again - it's me, haha ๐Ÿ˜† ,
do you think, it would be possible to expose the repo of the queryService of the assemblerService?

Currently, i would need to inject both, the DatabaseService and the AssemblerService in the Resolver, if i want to access the low-level repository functions. I think, it would be better to just use

this.assemblerService.queryService.repo.xyz();

what do you think? Only downside I see, would be, that it couples the implementation to a specific ORM framework (like TypeORM). However, one could create a getRepository() function in the QueryService interface. What do you think about this?

All the best,
Johannes

[Discussion] Architecture design for service classes

@doug-martin @johannesschobel I'm thinking about you should come with another solution for service classes. Service classes are now depending on DTOs, so it's hard to custom if you have multiple dtos for one entity and you must have multiple services relatively. For example if I have UserDto1 with firstName, lastName used by web app and UserDto2 with firstName, lastName, isDeleted, roles, permissions used by admin app, so I have to create 2 two services for that.

My idea: Service should only return entities, it shoudn't depend on dtos and we will have a mechanism to mapping to any dto we want to return from resolvers. I know that is a big change, but we will address a lots of problems (Get Underlying Entity Object, Restricting fields returned and fields can be filtered if the user doesn't have permissions, ...). How do you think?

[Question]: Overwrite Relation Methods

Dear @doug-martin ,

i just wanted to ask, if it is possible to overwrite the relationship methods of the Resolver class.
Consider the following example:

I have 2 ObjectTypes for User and Group with their corresponding Entitites and have my Resolver set up like this:

@Resolver(of => UserObject)
export class UserResolver extends CRUDResolver(UserObject, {
  create: { many: { disabled: true }, CreateDTOClass: CreateUserInput },
  delete: { disabled: true },
  update: { disabled: true },
  relations: {
    many: {
      groups: {
        relationName: 'groups',
        DTO: GroupObject,
        nullable: true
      },
    },
  },
}) {
  constructor(
    readonly service: UserService
  ) {
    super(service);
  }

This will auto-generate some methods to add / update / delete the assignment between User and Group. However, in my own business logic, i first need to check, if a User has accepted the "terms of service" before he actually can join a Group.
In order to do this, i need to add custom logic to the addUserToGroups method.

Basically i would need to do something like this:

@Mutation(...)
async addUserToGroups(...) {
  if (user.acceptedTOS === false) {
    // throw an exception
  }
  return super.addUserToGroups(params);
}

How can i do this? Does this work?
All the best and thanks for your help.

[Issue]: AssemblerService takes Objects Input instead of Entities

Dear @doug-martin ,

the newly added AssemblerService (added in v 0.6.0) takes an xDTO instead an xEntity.
Should this be changed to Entities?

createOne<C extends DeepPartial<DTO>>(item: C): Promise<DTO> {
const c = this.assembler.convertToEntity((item as unknown) as DTO);
return this.assembler.convertAsyncToDTO(this.queryService.createOne(c));
}

All the best,
Johannes

nested FilterableField

Hi,

I have a dto A which has a relation to dto B and dto A is offered via a query.
Is it now possible to use a "@FilterableField()" decorator within dto B, which makes it possible to filter or sort by a property?

Something like this:

{
  "Dog" :{
   "name":"string",
   "owner" :{
       @FilterableField()
       "age":"number"
     }
  }
}

Thanks for the work ๐Ÿ˜ƒ

[Improvement] SoftDelete Restore Function in Resolver

Dear @doug-martin ,

sorry to bring the SoftDelete topic back to the table. I carefully read your documentation on the TypeOrm SoftDelete feature and studied your example.

Thereby, I thought, that it may be possible to add a restore parameter to the CRUDResolver? It seems that you would re-write the same mutation over and over again to restore one or multiple entities.

Ideally, this could look like this - so it would be similar to the other methods (like update, create, ...)

@Resolver(of => UserObject)
export class UserResolver extends CRUDResolver(UserObject, {
  // ...
  restore: { many: { disabled: true } }
}) {
  constructor(readonly service: UserService) {
    super(service);
  }
}

The resolver would just call the service.restore(id) method. The latter, however, has to be implemented in respective service implementations (i.e., for TypeORM or Sequelize). Worst case could be, that a respective ORM does not support SoftDeletes, so a default implementation of the restore() method would just do nothing.

What do you think?

All the best,
Johannes

Class constructor TypeOrmQueryService cannot be invoked without 'new'

When creating a default service with nothing other than super it throws this exception:

TypeError: Class constructor TypeOrmQueryService cannot be invoked without 'new' at new QueueService (/home/coder2000/Projects/speakerr/packages/server/dist/src/modules/queue/queue.service.js:37:23) at Injector.instantiateClass (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/injector/injector.js:289:19) at callback (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/injector/injector.js:76:41) at processTicksAndRejections (internal/process/task_queues.js:97:5) at async Injector.resolveConstructorParams (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/injector/injector.js:117:24) at async Injector.loadInstance (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/injector/injector.js:80:9) at async Injector.loadProvider (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/injector/injector.js:37:9) at async Promise.all (index 3) at async InstanceLoader.createInstancesOfProviders (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/injector/instance-loader.js:42:9) at async /home/coder2000/Projects/speakerr/node_modules/@nestjs/core/injector/instance-loader.js:27:13 at async Promise.all (index 22) at async InstanceLoader.createInstances (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/injector/instance-loader.js:26:9) at async InstanceLoader.createInstancesOfDependencies (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/injector/instance-loader.js:16:9) at async /home/coder2000/Projects/speakerr/node_modules/@nestjs/core/nest-factory.js:81:17 at async Function.asyncRun (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/errors/exceptions-zone.js:17:13) at async NestFactoryStatic.initialize (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/nest-factory.js:79:13) at async NestFactoryStatic.create (/home/coder2000/Projects/speakerr/node_modules/@nestjs/core/nest-factory.js:30:9)

[Possibility] Subscription feature possibility

I have been wondering if it would be possible/planned to include subscription with this library. Subscription can create neat user experiences but creating them in manual way is cumbersome to do. Generally subscriptions can be useful for a few types of listeners. Can't they be implemented into library so they would get called upon mutations?
Sorry if my question has lack of understanding on how library works. New to GraphQL :)

It's guard and ResolveProperty still work?

I tried @ResolveProperty and guard , but I found it doesn't work any more.
I just follow document getting started and then add it.
I had add fieldResolverEnhancers: ['guards', 'interceptors'], in GraphQLModule.
Is me miss anything?

[Bug]: Defining additional UpdateDtos breaks Schema

Dear @doug-martin ,

while working on custom resolver methods, i stumbled upon an issue, i was able to reproduce in your example repository (shipped in this library).

You can take a look at the "how to reproduce" by looking at my commits here

master...johannesschobel:issue/update-dto

Description

I wanted to add a dedicated completeTodoItem mutation that only takes the completed parameter. Note that the original updateTodoItem also takes the name, and completed was set to optional. The dedicated completeTodoItem mutation should take this parameter as required.

  • I created a new Input DTO in order to provide sophisticated validations
  • I created a new InputType() that uses this dto
  • I created a new Mutation in my Resolver that uses this newly created InputType.

Note that i also committed the initial schema.gql file to see the actual changes in this file!

As you can see, the new mutation is added correctly. Also, the newly created InputType as well as the dto are added correctly. However, the dto for the regular UpdateOneTodoItem is also changed to this newly created dto - which is, obviously, wrong.

I hope you can reproduce the issue? Maybe i am doing something wrong here? As the docs were not quite accurate in this point, i was not sure if my approach is correct. I would be willing to document this feature, if we can figure out how to deal with the latter?

All the best and thanks for your time!

[Suggestion] Maybe can add more entry point to service to supply override

I think it can supply like preCreate method in ** TypeOrmQueryService** class, then I need to modify data before insert , I can override it and I don't need to override all createOne method.
This suggestion is from django rest framework ModelViewSet.
How do you think about it?

Like:

  async createOne<C extends DeepPartial<Entity>>(record: C): Promise<Entity> {
    await this.ensureEntityDoesNotExist(record);
    return this.repo.save(record);
    return this.preCreate(record);
  }

  async preCreate(record) {
      return this.repo.save(record)
  }

Support guards for read endpoint

@doug-martin I read in docs about guards for read endpoint, but it'll be unreasonale if we can't support that, because if the user don't have permissions to query an endpoint, so how to do it, it seems that the library is just for public query

[Question/Feature Request] Support for Apollo Federation

I am attempting to create two APIs using nestjs-query and combine them into a single graph using Apollo Federation.

I am having trouble when combining the two APIs when one API extends a type defined in another schema.

For example:
Say I have a user-service that exposes a User Type with the CRUDResolver. This will generate queries, mutations and input types with the prefix, User.

In another service, the reviews-service I extend the User type and add a list of reviews with another CRUDResolver.

When the gateway attempts to combine the two schemas it results in an error messages similar to the following

Field "UserFilter.and" can only be defined once.
Field "UserFilter.or" can only be defined once.
There can be only one type named "UserFilter".
Field "Mutation.deleteOneUser" can only be defined once.
Field "Mutation.deleteManyUsers" can only be defined once

I have got this working locally by adding an option to the CRUDResolver that adds a prefix to the queries, mutations, filter and input types via getDTONames. I was using the service name, this results in queries/mutations in the review service like:

Mutation.deleteOneReviewServiceUser
Query.reviewServiceUser

The gateway then exposes the normal CRUDResolver fields (defined in the user-service) and will resolve queries like:

query {
  user(id: "1") {
   # resolved from user service
    username
    # resolved from reviews service
    averageReviewScore
    reviews {
      content
    }
  }
}

This works decently well for querying a federated schema. However, I'm not sure how it should be handled from nestjs-query's perspective. The prefix could be added before the type (like above) or potentially before the queries all together (e.g. Query.reviewService_user).

Hope this makes sense, I'm happy to contribute this change, just wanted to get some feedback before I go down a particular path.

[Question] Get Underlying Entity Object

Dear @doug-martin ,

i recently had the problem, that i had to get the underlying Entity from the Service in order to call another service. For example, i have a the following situation:

export class UserService extends TypeOrmQueryService<UserObject, UserEntity> {...}
export class EvaluationService extends CoreService {...}

where the UserService is bootstrapped with your library, and the EvaluationService uses my own logic and provides other logic. Respective EvaluationService has a method which needs the UserEntity.

When i call the this.userService.getById(id) method, provided by the UserService class, however, i only get the UserObject and not the UserEntity. Note that the Object and Entity have different fields (i.e., the Entity has fields that are not present in the Object).

What is the best / proper way to get the underlying Entity from a Service?
Currently, I do it like this, however, i doubt that this is the best approach ;)

const user = await this.userService.getById(id);
const userEntity = this.userService
  .assemblerService
  .getAssembler(UserObject, UserEntity)
  .convertToEntity(user);

I know that the UserService also exposes the underlying Repository that is used for all operations. However, using the plain repository requires me to kind of re-write the queries :(

All the best and thanks a lot for your help!
Johannes

Support "Embedded Objects"

Dear @doug-martin ,

first of all, I would like to say, that this library is really (!) awesome ๐Ÿ‘ great work so far!

I recently stumbled upon an issue I am not quite sure how to solve it. Let me describe it in more detail.
Consider the following (simplified) example: I have a UserEntity and corresponding UserObjectType.
Furthermore, this User has a settings property, which is stored as JSON in the database. However, the object itself may have a proper class in order to indicate valid setting parameters, like language, color or whatever.

My update-user.input.ts looks like this (only important parts):

@InputType()
export class UpdateUserInput {
  @IsOptional()
  @IsString()
  @Field({
    description: 'The new username',
    nullable: true
  })
  username: string;

  @IsOptional()
  @ValidateNested()
  @Type(() => UpdateSettingsInput)
  @Field(type => UpdateSettingsInput, {
    description: 'Settings Object for this user',
    nullable: true
  })
  settingst: object;
}

whereas my UpdateSettingsInput class looks like this

import { CoreInput } from '@cancerlog/api/core';
import { InputType, Field } from 'type-graphql';
import { IsOptional, IsString } from 'class-validator';

@InputType()
export class UpdateSettingsInput {
  @IsOptional()
  @IsString()
  @Field({
    nullable: true
  })
  language: string;

  @IsOptional()
  @IsString()
  @Field({
    nullable: true
  })
  theme: string;

  // ... more fields here
}

Updating a User and setting the data for settings works like a charm, however, I am not able to read the data and return it to the client properly.

My UserObject looks like this:

@ObjectType('User')
export class UserObject {
  // ... some fields here
  @Field(() => SettingsObject, {
    nullable: true
  })
  settings: SettingsObject;
}

with a corresponding SettingsObject (looks similar to the UpdateSettingsInput class).

When "compiling" the app, it throws an error with:

(node:28196) UnhandledPromiseRejectionWarning: Error: Cannot determine GraphQL input type for settings

I assume, that this is some weird "black magic" stuff happening in this library for auto-generating the CRUD methods and its not properly able to do so.

Please note, that i also tried to use the relations approach on the UserResolver class to add a one relationship to settings. This is, however, not possible, because this is (obviously) not a valid TypeORM relationship, but rather an "embedded" object.

Can you please point me into the right direction?! ๐Ÿ˜ข Am i missing something, does this library not have proper support for such a feature or am i just dumb? ๐Ÿ˜†

Thank you so much for your time and advices,
Johannes

[Bug]: Mysql error "LIMIT in subquery"

Hi @doug-martin ,

Context

I'm using nestjs-query to easily implement graphql in my company and this project is really great and usefull !!

In this project we have multiple endpoints and multiple databases (all in mysql 5.7). Currently we are working on one main endpoint and some entities from other databases.

I made a repo to reproduce the issue : https://github.com/praaxe/nestquery-example .
You just need to update config/config.ts and run src/commands/generate-fixture.ts to have some data. (beware it deletes all tables in the database ><)

The issue is when querying sub-relations I have a sql error

ER_NOT_SUPPORTED_YET: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'

image

In the repo I added a patch I did to bypass this error in the resolvers' comments but I didn't find how to use dataloader so this ins't really good.

Patching

I found a way around by overiding 1-n relationships in resolvers and adding (paging: {}) to n-* sub-relations but I don't really like it and it would be better to find a definitive correction.

image

Versions

  • Mysql: 5.7
  • nestjs: 7.0.7
  • typeorm: 7.0.0
  • nestjs-query: 0.7.4

ps: thank you again for this project !

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.