Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • Aug 24 18:40
    ldiego08 commented #369
  • Aug 24 17:14
    j commented #369
  • Aug 24 10:14
    MichalLytek labeled #251
  • Aug 24 10:14
    MichalLytek commented #251
  • Aug 24 03:00
    tonyxiao commented #197
  • Aug 24 02:47
    tonyxiao commented #197
  • Aug 23 22:10
    j commented #401
  • Aug 23 18:57
    quaj commented #406
  • Aug 23 18:56
    dzfranco commented #386
  • Aug 23 18:51
    quaj commented #406
  • Aug 23 18:49
    quaj commented #406
  • Aug 23 18:49
    quaj commented #406
  • Aug 23 18:42
    19majkel94 labeled #406
  • Aug 23 18:42
    19majkel94 labeled #406
  • Aug 23 18:42
    19majkel94 unlabeled #406
  • Aug 23 18:42
    19majkel94 unlabeled #406
  • Aug 23 18:42
    19majkel94 unlabeled #406
  • Aug 23 18:41
    quaj closed #406
  • Aug 23 18:41
    quaj commented #406
  • Aug 23 18:22
    19majkel94 labeled #396
Rafał Chłodnicki
@rchl

I wonder if someone has recommendation on how to best combine those use cases...

I'm using @ArgsType decorator with a class defined like so (example more or less from documentation):

@ArgsType()
class GetRecipesArgs {
  @Field(type => Int)
  id: number;

  @Field(type => Int, { nullable: true })
  skip?: number = 1;
}

@Resolver()
class RecipeResolver {
  @Query(returns => [Recipe])
  async recipes(@Args() { id, skip }: GetRecipesArgs) {
    // ...
  }
}

Field skip has default value of 1. There is no problem when using graphql but same models are exposed as a plain JS API and I wonder if there is better way to use those than creating GetRecipesArgs instance manually.

So basically, for default value of the field skip to apply, I need to do this:

const args = new GetRecipesArgs();
args.id = 11111;
const recipes = await resolver.recipes(args);

while I find that too verbose and I would prefer to just do this:

const recipes = await resolver.recipes({ id: 11111 });

but in that case I don't pass skip with its default (1) value.

Eric Webster
@QuantumKing
Hey all, so I am trying to keep my TypeORM models separate from TypeGraphQL and am wondering if theres a good approach people are using to do so. So I'd like to be able to keep some kind of type linkage between the database model and the graphql type, so that if I change something on the db model, I'll know if there's a downstream effect on the graphql type. Right now I have this working using interfaces, but doing it this way generates a useless graphql interface which is only used in one type. Basically, how do I add the @Field decorator to a pre-existing class?
import { ObjectType, Field, InterfaceType } from 'type-graphql';
import * as db from '@api/db';

@InterfaceType()
abstract class IBook {
  @Field()
  public id: number;

  @Field()
  public title: string;

  @Field({ nullable: true })
  public content?: string;
}

@ObjectType({ implements: IBook })
export class Book extends db.Book implements IBook {}
Michał Lytek
@19majkel94

@AntonPuko

Hey guys. Is there a way to extend or override @Authorized() decorator somehow?

The authorized and authChecker are basically just a middleware. If your business logic doesn't match well with the standard implementation, just use a custom middleware that will get the user id from context and compare it with root.ownerId, along the basic role based access.

@rchl
You can write just:

@Field(type => Int)
skip?: number;

And then you can use the ES6 syntax - async recipes(@Args() { id, skip = 1 }: GetRecipesArgs)
And if you need the default value to show up in introspection query (GraphQL Playground docs), you can provide @Field(type => Int, { defaultValue: 1 })

@QuantumKing
The main gol of TypeGraphQL was to have only one source of truth - single class with ORM/GraphQL decorators. Storing them separately defeat the whole purpose of this framework - maybe it's better to use a schema-fist apporach then? Or just consider single class for entity and object type.
Rafał Chłodnicki
@rchl
@19majkel94 thanks for that idea. It does sound like a decent solution although then I have to maintain default value in two separate places which is not ideal. I was thinking to instead create a method decorator that would convert arguments to an instance of class if it's not already. Like so:
import { plainToClass } from 'class-transformer';

function ConvertArgsType(classType: { new(): any }): MethodDecorator {
    return function(target, propertyKey, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = function(...args: any[]) {
            if (args.length && !(args[0] instanceof classType)) {
                args[0] = plainToClass(classType, args[0]);
            }

            return originalMethod.apply(this, args);
        };
    };
}

class TestArgs {
    @Field(() => Int)
    public id!: number;

    @Field(() => Int, { nullable: true })
    public test: number = 1;
}

class TestClass {
    @Query()
    @ConvertArgsType(TestArgs)
    public test(@Args() { id, test }: TestArgs): boolean {
        console.assert(id);
        console.assert(test === 1);
        return true;
    }
}

(new TestClass).test({ id: 2 });
Which seems to work but I wonder if there is a way to improve it by leveraging types collected by Args decorator so that I don't have to repeat type in @ConvertArgsType decorator?
Rafał Chłodnicki
@rchl
I was thinking with getParamInfo API (https://github.com/19majkel94/type-graphql/blob/816ac6113b181fedfeaea0fe97216671796b0545/src/helpers/params.ts) but it doesn't seem to be exposed
Michał Lytek
@19majkel94
Don't dig in internals as they might change without any notice
Rafał Chłodnicki
@rchl
yes, thought so
Eric Webster
@QuantumKing

@19majkel94 BTW, I was going through your dependency injection section in the docs and noticed your "dirty trick" of using formatResponse in apollo server to clean up the container. I believe you can just create independent container instances which are not added to the list of instances in the main Container object:

const container = new ContainerInstance('no need for requestId, this isnt used');

That way it'll just be garbage collected at the end of the request.

Thomas Reggi
@reggi
Anyone know I can call a resolver directly? Without calling new()? Looking for something similar to how typeorm has getRepository.
Thomas Reggi
@reggi
Ok I think what I am looking for is Container.get(Resolver).
David
@davounet_twitter
Hello,
Do you have any idea on how I can set a cookie with typegraphql ?
Thank you
David
@davounet_twitter
I think I found a way through the context object
David
@davounet_twitter
Nope, no way to set headers, am I missing something ?
Dan Caddigan
@goldcaddy77
You'd do this in middleware of whatever is running your server (Apollo server, yoga, etc...)
David
@davounet_twitter
I'm using apollo-server-lambda and cant find a way to set a cookie. I'll store my jwt in localstorage for now
Mehmet N Yarar
@mehmetnyarar

Hey guys. I use MongoDB as a database. I've tried my luck with TypeORM, but got typeorm/typeorm#4520. I decided to use Typegoose but I again got another problem regarding id fields.

I followed the example on GitHub but there _id is being used. How can I get id to work?

@ObjectType()
export class User extends Typegoose {
  readonly _id: ObjectID

  // Not working, returns null
  // @Field(() => ID)
  // readonly id: ObjectID

// Not working, returns null
  @Field(() => ObjectIDScalar)
  readonly id: ObjectID
}
Mehmet N Yarar
@mehmetnyarar
I've tried using virtual getter, it seems to work. If there's better approach, I'd like to hear.
{
  @Field(() => ID)
  get id () { return this._id.toHexString() }
}
Mehmet N Yarar
@mehmetnyarar
By the way I'd appreciate if you can take a look at my issue in TypeORM.
Hendrik Roth
@HendrikRoth
am i right that i can't use class-validator for nested InputTypes?
@InputType()
class Address {
  @Field()
  @IsNotEmpty()
  street: string;
}

@InputType()
class User {
   @Field(() => Address)
   address: Address
}
georgyfarniev
@georgyfarniev
Hello. How can I use context in Subscription filter method? It's just undefined in args. In mutations context are injected properly using Ctx decorator however. Should context be available in Subscription filter?
I need it because I want to filter event to specific user, whom session is stored in koa context
georgyfarniev
@georgyfarniev
I submitted it as bug, I think it's bug since context parameter with undefined value are present in args
georgyfarniev
@georgyfarniev
After study code of type-graphql I think it's problem in apollo.
Simon
@lookapanda

Hello hello! I'm (like some other people) dealing with serverless-offline and I just noticed something very weird. For hot reloading to work I need to build the schema on every request (just locally ofc), otherwise my changes are not there and I need to restart the whole process. I noticed that if I have a class and I use it statically, it creates this class "newly" on every request ONLY if I build the schema newly too. If I cache the schema, the class stays the same. I made a repo where you can see what I mean: https://github.com/lookapanda/type-graphql-serverless-offline-import-bug

Any idea what is going on?? @19majkel94

Michał Lytek
@19majkel94

I believe you can just create independent container instances

@QuantumKing
I tried, I failed, I found this workaround. Will try to investigate this trick again, thanks!

Michał Lytek
@19majkel94

am i right that i can't use class-validator for nested InputTypes?

@HendrikRoth
You should be able but it's a bug and it's not working :disappointed: See #133

@lookapanda I haven't use lambda nor serveless, can't help
Artur Tagisow
@3nuc
@ArgsType()
class ParkingSpotsArgs {
  @Field(type => Int, { nullable: true })
  first: number;
}

@Resolver(ParkingSpots)
export class ParkingSpotsResolver {
  @Query(returns => [ParkingSpots])
  async parkingspots(@Args() { first }: ParkingSpotsArgs) {
    return LaunchService.launches(first);
  }
}
Can I somehow avoid creating this single arg ArgsType? I would like to use something like async parkingspost(@Arg(returns => Int)) {}. Is it possible? Couldn't find it in the Arguments docs
(I could just use @Arg("first"): first: number; but that would make the argument's type in GraphQL "Float", I want "Int")
Michał Lytek
@19majkel94
@3nuc
@Arg("first", type => Int) first: number
Thomas Reggi
@reggi
I am looking for a way to have a database call be made once for an entire query, and multiple resolvers use the data within context. The issue I am running into is that middleware seems to be called per-resolver and not once.
Is it possible to have middleware / some code only fire once per request, rather then fore each resolver?
Currently, the only I am achieving this is with external express-based middleware...
Thomas Reggi
@reggi
Is it possible to pass a variable into Class-based Middleware?
Michał Lytek
@19majkel94
@reggi no, all you can is to read the info param to get info if it's a type or query resolver, to execute the code only once, or set some variable in context at first execution
@reggi you can use DI to inject some variable into class based constructor, or create a class factory like with generic resolvers
Artur Tagisow
@3nuc
dzięki :)
lexasa
@lexasa

I'm doing a production build of a project that uses type-graphql. The project runs fine in dev environment but in compiled JS it produces strange error.

Error: You need to provide explicit type for AdResolver#ads parameter #2 !
    at Object.findType (/Users/lexa/Projects/backend/node_modules/type-graphql/dist/helpers/findType.js:17:15)
    at Object.getParamInfo (/Users/lexa/Projects/backend/node_modules/type-graphql/dist/helpers/params.js:9:49)
    at Args (/Users/lexa/Projects/backend/node_modules/type-graphql/dist/decorators/Args.js:9:120)
    at /Users/lexa/Projects/backend/build/modules/ad/Ads.js:12:37
    at DecorateProperty (/Users/lexa/Projects/backend/node_modules/reflect-metadata/Reflect.js:553:33)
    at Object.decorate (/Users/lexa/Projects/backend/node_modules/reflect-metadata/Reflect.js:123:24)
    at __decorate (/Users/lexa/Projects/backend/build/modules/ad/Ads.js:4:92)
    at Object.<anonymous> (/Users/lexa/Projects/backend/build/modules/ad/Ads.js:165:1)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
error: Failed generate GraphQL schema {"timestamp":"2019-08-13T20:30:12.497Z"}

here is TS source code

@ObjectType()
class AdResult extends Result(Ad) {}

@InputType()
class AdInput extends ListInput(AdFilter, AdSort, AdSort.ID_DESC) {}

@Resolver()
export class AdResolver {
  @Authorized()
  @Query(() => AdResult, {nullable: true})
  async ads(
    @Ctx() ctx: AppContext,
    @Arg('input', type => AdInput) input: AdInput,
  ): Promise<AdResult> {
lexasa
@lexasa

I'm doing a production build of a project that uses type-graphql. The project runs fine in dev environment but in compiled JS it produces strange error.

Error: You need to provide explicit type for AdResolver#ads parameter #2 !
    at Object.findType (/Users/lexa/Projects/backend/node_modules/type-graphql/dist/helpers/findType.js:17:15)
    at Object.getParamInfo (/Users/lexa/Projects/backend/node_modules/type-graphql/dist/helpers/params.js:9:49)
    at Args (/Users/lexa/Projects/backend/node_modules/type-graphql/dist/decorators/Args.js:9:120)
    at /Users/lexa/Projects/backend/build/modules/ad/Ads.js:12:37
    at DecorateProperty (/Users/lexa/Projects/backend/node_modules/reflect-metadata/Reflect.js:553:33)
    at Object.decorate (/Users/lexa/Projects/backend/node_modules/reflect-metadata/Reflect.js:123:24)
    at __decorate (/Users/lexa/Projects/backend/build/modules/ad/Ads.js:4:92)
    at Object.<anonymous> (/Users/lexa/Projects/backend/build/modules/ad/Ads.js:165:1)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
error: Failed generate GraphQL schema {"timestamp":"2019-08-13T20:30:12.497Z"}

here is TS source code

@ObjectType()
class AdResult extends Result(Ad) {}

@InputType()
class AdInput extends ListInput(AdFilter, AdSort, AdSort.ID_DESC) {}

@Resolver()
export class AdResolver {
  @Authorized()
  @Query(() => AdResult, {nullable: true})
  async ads(
    @Ctx() ctx: AppContext,
    @Arg('input', type => AdInput) input: AdInput,
  ): Promise<AdResult> {

My bad. build dir had some leftovers from the previous version.

Nick Bolles
@NickBolles

I'm running across an issue (I'm still not sure why it showed up all the sudden, but I think it's still something to look at), where type-graphql is getting a Proxy as the return type for a resolver, but the decorator target was the raw class, not a Proxy, so getGraphQLOutputType doesn't match the type and throws an error

I'm using nestjs-graphql btw.

UnhandledPromiseRejection:  Error: Cannot determine GraphQL output type for getUser
    at Function.getGraphQLOutputType (/home/coder/project/packages/server/node_modules/type-graphql/dist/schema/schema-generator.js:396:19)
    at handlers.reduce (/home/coder/project/packages/server/node_modules/type-graphql/dist/schema/schema-generator.js:284:28)
    at Array.reduce (<anonymous>)
    at Function.generateHandlerFields (/home/coder/project/packages/server/node_modules/type-graphql/dist/schema/schema-generator.js:278:25)
    at Function.buildRootQueryType (/home/coder/project/packages/server/node_modules/type-graphql/dist/schema/schema-generator.js:247:26)
    at Function.generateFromMetadataSync (/home/coder/project/packages/server/node_modules/type-graphql/dist/schema/schema-generator.js:32:25)
    at Function.<anonymous> (/home/coder/project/packages/server/node_modules/type-graphql/dist/schema/schema-generator.js:16:33)
    at Generator.next (<anonymous>)
    at /home/coder/project/packages/server/node_modules/tslib/tslib.js:110:75
    at new Promise (<anonymous>)

schema-generator.js

static mapArgFields(argumentType, args = {}) {
        const argumentInstance = new argumentType.target();
        argumentType.fields.forEach(field => {
            field.typeOptions.defaultValue = this.getDefaultValue(argumentInstance, field.typeOptions, field.name, argumentType.name);
            args[field.schemaName] = {
                description: field.description,
                type: this.getGraphQLInputType(field.name, field.getType(), field.typeOptions),
                defaultValue: field.typeOptions.defaultValue,
            };
        });
    }
    static getGraphQLOutputType(typeOwnerName, type, typeOptions = {}) {
        let gqlType;
        gqlType = types_1.convertTypeIfScalar(type);
        console.log("GetGraphQLOutputType ", type, typeOptions)
        debugger;
        if (!gqlType) {
            const objectType = this.objectTypesInfo.find(it => it.target === type);
            if (objectType) {
                gqlType = objectType.type;
            }
        }
        if (!gqlType) {
            const interfaceType = this.interfaceTypesInfo.find(it => it.target === type);
            if (interfaceType) {
                gqlType = interfaceType.type;
            }
        }
        if (!gqlType) {
            const enumType = this.enumTypesInfo.find(it => it.enumObj === type);
            if (enumType) {
                gqlType = enumType.type;
            }
        }
        if (!gqlType) {
            const unionType = this.unionTypesInfo.find(it => it.unionSymbol === type);
            if (unionType) {
                gqlType = unionType.type;
            }
        }
        if (!gqlType) {
            throw new Error(`Cannot determine GraphQL output type for ${typeOwnerName}`); // It's throwing here
        }
        const { nullableByDefault } = build_context_1.BuildContext;
        return types_1.wrapWithTypeOptions(typeOwnerName, gqlType, typeOptions, nullableByDefault);
    }

Here's a watch statement on type and the User target in the objectTypesInfo object

image.png
Nick Bolles
@NickBolles
one alternative is to try to instantiate the type and compare the constructor, to get rid of the Proxy in the middle
image.png
Michał Lytek
@19majkel94

where type-graphql is getting a Proxy as the return type for a resolver, but the decorator target was the raw class, not a Proxy

such use case is not supported, as well as other mutation of decorated class

Nick Bolles
@NickBolles
@19majkel94 What do you think about instantiating the type and comparing it's constructor? I think that's all that would be required. Maybe I'm missing something through. Again, I'm not sure where the object is being wrapped by a proxy. I've tried stripping it down to the minimum and it's still happening :/
Nick Bolles
@NickBolles
...Somehow it looks like this was caused by nestjs-config. I don't understand why but it's working when I revert my changes to it...