youshido-php/GraphQLBundle

Read Schema from Doctrine Annotations

davidbarratt opened this issue · 12 comments

It seems like most of the necessary schema data is already present in Doctrine's Entity Annotations with perhaps some additional annotations for schema that cannot be inferred from the existing annotations. Is this something that can be done or am I missing something?

bjunc commented

This sounds like a pretty cool idea.

Just curious though, are you imagining a situation where the annotations for a schema are read on the fly (eg. serialization @Groups()), or are you thinking a static schema file would be generated from a command (eg. Doctrine's php bin/console doctrine:schema:update)?

I would think it would be read on the fly, no need to create a static schema file if annotations can be easily cached.

bjunc commented

I'm in the early stages of GraphQL, right now just experimenting with creating a schema that replicates our REST API. Having created a handful of inline fields/objects and AbstractObjectType, I can already see how annotations could be used to generate this on the fly.

I'd be happy to start working on this feature after I get past the initial experimentation / proof of concept stage.

Any thoughts on the annotation naming convention? If it's possible, it would be awesome to piggyback the @Security annotation for guarding field resolves.

I envisioned using Doctrine's existing annotations and just having extra annotations when necessary. This would be really convenient for people who use Doctrine already.

Then again, if someone isn't using Doctrine (or SensioFrameworkExtras bundle for @Security) then they wouldn't really be able to use the annotations.

So maybe it would be best to initially have custom annotations and then use other bundles (Doctrine, SensioFrameworkExtras, etc.) as a fallback if necessary?

As far as a naming convention, I would use Youshido\GraphQLBundle\Annotation and I would follow the types in here https://github.com/Youshido/GraphQL/tree/master/src/Type. Actually, it might be possible to just load these types as annotations themselves, the Annotation API is pretty broad.

There could be an argument for including the default annotations in Youshido/GraphQL and putting the fallbacks in this repo.

Yep, I do have the same feeling... But without talking about this project, I think the annotation should be the PHP language feature, but not as in a comment. There's a lot of such annotations now @ApiDoc @Route @Group @ParameterConvert ...

bjunc commented

@yarcowang that would kinda defeat the point, right?

I still haven't gotten to the familiarity point of GraphQL to know if annotations alone can generate a full schema, but my feeling is that much of a schema could be generated this way.

For instance, a GQL (GraphQL) field needs a type, which can be repurposed from the already existing @ORM annotation. The ORM association annotations (eg. @ORM\OneToMany) could create a ListType. The GQL resolve could repurpose the entity getter/setter; which is apparently what Facebook had in mind anyway. @ApiDoc could be used for the GQL field description, and possibly even the supported field arguments.

Securing a field seems more tricky (unless I'm missing something). Assuming the currently authenticated user is the "viewer", then it would seem like finding a way to tap into isGranted would be the way to go. I haven't looked into this, but apparently this bundle already has something addressing this, and notes the performance consideration.

I also don't have much experience with the annotation API, but the whole thing seems plausible.

@bjunc Yep. But the annotation can do only one thing. If the name is @ORM\OneToMany, then the whole functions are defined in ORM\OneToMany. If you are going to add GQL feature, then you need to modify the code from ORM\OneToMany... That would break whole ?? (don't know how to say it in English, one class/piece of code only does one thing)
The only way to make it clean is the language itself support extension on the class, like the swift did

Hey there! I'm currently working to add this feature to my project, the first issue im facing is that the only way I can find to access the entity repository is on the resolve method. I'm planning to add fields on the fly in the schema class, first thing is to get the entity annotations in the abstract field controller.
$ this-> getEntityManager () -> getClassMetadata ($ entity);
Any ideas?

I found a repo that extend this bundle: YoushidoGraphQLExtendedBundle.
It has @Security annotations and a command to generate schema from entities but is not on the fly.

bjunc commented

I ended up mothballing the idea of generating a schema from annotations, but if it were going to be done, I think it'd need to work similarly to how serialization/deserialization groups work. For instance, it seems to be a common design pattern to have distinct fields/types for different mutations, in addition to the root type. So for instance, lets say you had a Post entity, you might have a CreatePostInputType, UpdatePostInputType, DeletePostInputType, and a root PostType. This allows for certain fields to be required/optional in different situations. The GitHub GraphQL API is an example of this.

@bjunc In my option, it will be a lot of work if you do follow GraphQL. Maybe extending REST is better. A great feature I want in GraphQL is the fields. You can define the fields you want in the client end. So maybe REST API can also accept a special key __fields that make the backend return only the relevant fields.
I don't have any feelings on mutations, in my mind, CREATE/UPDATE/DELETE in REST is more simple. GraphQL is better in Query (GET/fetch data) like in its name.
Still in the wait and see status...

bjunc commented

For me, I have yet to find a consequential downside to GraphQL. I've now built GQL backends in a few different technologies (currently building one in Phoenix/Elixir). The mutations are really interesting, as it's essentially the return to verb based actions. Takes a little getting used to since REST is so fundamentally noun based. However, I find it much easier to build out sophisticated endpoints in GQL. You can better protect certain fields that way too.

All told though, I'm personally not pursuing the idea that the entity/model business layer both determines the database schema as well as the GQL schema. I know that's essentially what GraphCool has done with their service, so that might be something worth looking into for those who are interested in that.