Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL Custom Schema #5821

Merged
merged 11 commits into from
Jul 18, 2019

Conversation

davimacedo
Copy link
Member

This PR empowers the Parse GraphQL API with custom user-defined schema. The developers can now write their own types, queries, and mutations, which will merged with the ones that are automatically generated. The new types are resolved by the application's cloud code functions.

Therefore, regarding #5777, this PR closes the cloud functions needs and also addresses the graphql customization topic. In my view, I think that this PR, together with #5782 and #5818, when merged, closes the issue.

How it works:

  1. When initializing ParseGraphQLServer, now the developer can pass a custom schema that will be merged to the auto-generated one:
      parseGraphQLServer = new ParseGraphQLServer(parseServer, {
        graphQLPath: '/graphql',
        graphQLCustomTypeDefs: gql`
          extend type Query {
            custom: Custom @namespace
          }
           type Custom {
            hello: String @resolve
            hello2: String @resolve(to: "hello")
            userEcho(user: _UserFields!): _UserClass! @resolve
          }
        `,
      });

Note:

  • This PR includes a @namespace directive that can be used to the top level field of the nested queries and mutations (it basically just returns an empty object);
  • This PR includes a @resolve directive that can be used to notify the Parse GraphQL Server to resolve that field using a cloud code function. The to argument specifies the function name. If the to argument is not passed, the Parse GraphQL Server will look for a function with the same name of the field;
  • This PR allows creating custom types using the auto-generated ones as in userEcho(user: _UserFields!): _UserClass! @resolve;
  • This PR allows to extend the auto-generated types, as in extend type Query { ... }.
  1. Once the schema was set, you just need to write regular cloud code functions:
      Parse.Cloud.define('hello', async () => {
        return 'Hello world!';
      });

      Parse.Cloud.define('userEcho', async req => {
        return req.params.user;
      });
  1. Now you are ready to play with your new custom api:
query {
  custom {
    hello
    hello2
    userEcho(user: { username: "somefolk" }) {
      username
    }
  }
}

should return

{
  "data": {
    "custom": {
      "hello": "Hello world!",
      "hello2": "Hello world!",
      "userEcho": {
        "username": "somefolk"
      }
    }
  }
}

@davimacedo
Copy link
Member Author

@omairvaiyani let me know your feedback.

@davimacedo davimacedo mentioned this pull request Jul 18, 2019
9 tasks
@codecov
Copy link

codecov bot commented Jul 18, 2019

Codecov Report

Merging #5821 into master will increase coverage by 0.01%.
The diff coverage is 97.72%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5821      +/-   ##
==========================================
+ Coverage   93.66%   93.68%   +0.01%     
==========================================
  Files         146      147       +1     
  Lines       10236    10279      +43     
==========================================
+ Hits         9588     9630      +42     
- Misses        648      649       +1
Impacted Files Coverage Δ
src/GraphQL/ParseGraphQLServer.js 97.22% <ø> (ø) ⬆️
src/GraphQL/ParseGraphQLSchema.js 96.38% <100%> (+1.64%) ⬆️
src/GraphQL/loaders/schemaDirectives.js 94.11% <94.11%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9eec0f1...7ae2529. Read the comment docs.

3 similar comments
@codecov
Copy link

codecov bot commented Jul 18, 2019

Codecov Report

Merging #5821 into master will increase coverage by 0.01%.
The diff coverage is 97.72%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5821      +/-   ##
==========================================
+ Coverage   93.66%   93.68%   +0.01%     
==========================================
  Files         146      147       +1     
  Lines       10236    10279      +43     
==========================================
+ Hits         9588     9630      +42     
- Misses        648      649       +1
Impacted Files Coverage Δ
src/GraphQL/ParseGraphQLServer.js 97.22% <ø> (ø) ⬆️
src/GraphQL/ParseGraphQLSchema.js 96.38% <100%> (+1.64%) ⬆️
src/GraphQL/loaders/schemaDirectives.js 94.11% <94.11%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9eec0f1...7ae2529. Read the comment docs.

@codecov
Copy link

codecov bot commented Jul 18, 2019

Codecov Report

Merging #5821 into master will increase coverage by 0.01%.
The diff coverage is 97.72%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5821      +/-   ##
==========================================
+ Coverage   93.66%   93.68%   +0.01%     
==========================================
  Files         146      147       +1     
  Lines       10236    10279      +43     
==========================================
+ Hits         9588     9630      +42     
- Misses        648      649       +1
Impacted Files Coverage Δ
src/GraphQL/ParseGraphQLServer.js 97.22% <ø> (ø) ⬆️
src/GraphQL/ParseGraphQLSchema.js 96.38% <100%> (+1.64%) ⬆️
src/GraphQL/loaders/schemaDirectives.js 94.11% <94.11%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9eec0f1...7ae2529. Read the comment docs.

@codecov
Copy link

codecov bot commented Jul 18, 2019

Codecov Report

Merging #5821 into master will increase coverage by 0.01%.
The diff coverage is 97.72%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5821      +/-   ##
==========================================
+ Coverage   93.66%   93.68%   +0.01%     
==========================================
  Files         146      147       +1     
  Lines       10236    10279      +43     
==========================================
+ Hits         9588     9630      +42     
- Misses        648      649       +1
Impacted Files Coverage Δ
src/GraphQL/ParseGraphQLServer.js 97.22% <ø> (ø) ⬆️
src/GraphQL/ParseGraphQLSchema.js 96.38% <100%> (+1.64%) ⬆️
src/GraphQL/loaders/schemaDirectives.js 94.11% <94.11%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9eec0f1...7ae2529. Read the comment docs.

const load = parseGraphQLSchema => {
parseGraphQLSchema.graphQLSchemaDirectivesDefinitions = definitions;

class NamespaceDirectiveVistor extends SchemaDirectiveVisitor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo NamespaceDirectiveVisitor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@codecov
Copy link

codecov bot commented Jul 18, 2019

Codecov Report

Merging #5821 into master will increase coverage by 0.01%.
The diff coverage is 97.72%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5821      +/-   ##
==========================================
+ Coverage   93.66%   93.68%   +0.01%     
==========================================
  Files         146      147       +1     
  Lines       10236    10279      +43     
==========================================
+ Hits         9588     9630      +42     
- Misses        648      649       +1
Impacted Files Coverage Δ
src/GraphQL/ParseGraphQLServer.js 97.22% <ø> (ø) ⬆️
src/GraphQL/ParseGraphQLSchema.js 96.38% <100%> (+1.64%) ⬆️
src/GraphQL/loaders/schemaDirectives.js 94.11% <94.11%> (ø)
...dapters/Storage/Postgres/PostgresStorageAdapter.js 96.82% <0%> (-0.09%) ⬇️
src/RestWrite.js 93.51% <0%> (+0.17%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9eec0f1...e1d6200. Read the comment docs.

@douglasmuraoka douglasmuraoka merged commit 71d92ae into parse-community:master Jul 18, 2019
@omairvaiyani
Copy link
Contributor

Very happy to see this! I can't see any issues from here, though I suspect I'll be getting familiar with these commits as I merge them with #5782

@ciekawy
Copy link

ciekawy commented Aug 2, 2019

This is so great to saw custom schema support already there. I am wondering now... I have my own integration in place for about a year using just apollo server mounted in express along with parse. Thus I have regular resolvers. Moving stuff into nested namespace does not seem to be a problem. Just wondering if it is now possible or maybe possible to add support for regular resolvers?

@davimacedo
Copy link
Member Author

@ciekawy the currently approach is using cloud functions as resolvers. When you say regular resolvers, what are you more concerned about in comparison to using cloud functions?

@ciekawy
Copy link

ciekawy commented Aug 2, 2019

as for regular resolvers my main point is that as I am already using regular ApolloServer with schema and resolvers. In addition as I am using TypeScript, resolvers are of type IResolvers from graphql-tools and also I can easily return a reference to another resolver when it complies with the schema as well as have nested resolvers and return data depending on parent object (basically relying on IFieldResolver of (parent, args, context, info) => any).

Currently have about 15 top level queries + 5 nested ones, and about 15 mutations (in the future certainly some of those could be migrated to use directly native parse graphql schema - I am just extremely time limited yet jumping into native parse graphql support would allow me to implement very nice dockerized integration tests).

Ideally I'd like to use Apollo compliant resolvers without (or with minimal) changes. One option I was thinking about could be to do schema stiching and delegate internally custom: Custom @namespace to another instance of parse server (but then the drawback most probably would be that those resolvers will be executed out of parse context unless the context could be passed somehow as a context param... ;)

@ciekawy
Copy link

ciekawy commented Aug 2, 2019

so to recap possible concerns are

  • preserve IResolvers as much as possible
  • at least preserve IFieldResolver methods (maybe it would be possible to wrap/map them to/from the cloud functions)
  • keep the flexibility of GraphQL resolvers and be able to define nested resolvers (having access to parent object) and being able to delegate to other resolvers for nested data.

Definitely I need to find time to play a bit with latest parse and at least try to understand better how the integrations works.

I see mapping custom schema to cloud function really great idea especially for those who were using cloud functions in the past and are willing to jump into the GraphQL. OTOH I think ideally there should be more natural GraphQL integration for those working already more with GraphQL then parse.

@davimacedo
Copy link
Member Author

I think that we can think in some way to allow the developer to pass an executable schema (created with graphql-tools) to the Parse GraphQL Server that will be merged with the automatic generated one.

We can also pass to the cloud code functions all (parent, args, context, info) args for it to resolve the field.

I think both ideas would help you, right?

@ciekawy
Copy link

ciekawy commented Aug 2, 2019

yep, first one would be awesome. Second would be great anyway - even for people using just the cloud functions approach as they would use the GraphQL approach from the get-go (main danger would be naming conflict yet renaming on my side is acceptable as I am now the only consumer of my schema).

I'd be very happy to contribute in this field, the only thing is thats part of my full-stack/mobile side-project when I have a stretch goal to make first MVP release :) I feel like my back-end part need some love and migrating to native parse-graphql support is the way it should be done. But now I could imagine that only by minimum effort while switching to parse 3.7.x+

@ciekawy
Copy link

ciekawy commented Aug 2, 2019

looks like federation is the current way of merging schemas
https://www.apollographql.com/docs/apollo-server/federation/migrating-from-stitching/

and we're not even talking about migration as there was no ay schema stitching yet. Seems it may require separate ApolloServer instance and a gateway which sounds like an overkill... Just asked a question regarding this
https://spectrum.chat/apollo/apollo-federation/qq-it-is-possible-doest-it-make-sense-to-have-federation-without-gateway~36e3d55b-1410-4a00-9adf-921a8fc187c9

@davimacedo
Copy link
Member Author

Nice @ciekawy ! Thanks for sharing your thoughts. We have a todo list of what we want to do in GraphQL here and I've just added these two issues. You are more than welcome to contribute on this! Just pick up one of the tasks, and send as a PR. I will be happy to review.

UnderratedDev pushed a commit to UnderratedDev/parse-server that referenced this pull request Mar 21, 2020
This PR empowers the Parse GraphQL API with custom user-defined schema. The developers can now write their own types, queries, and mutations, which will merged with the ones that are automatically generated. The new types are resolved by the application's cloud code functions.

Therefore, regarding parse-community#5777, this PR closes the cloud functions needs and also addresses the graphql customization topic. In my view, I think that this PR, together with parse-community#5782 and parse-community#5818, when merged, closes the issue.

How it works:

1. When initializing ParseGraphQLServer, now the developer can pass a custom schema that will be merged to the auto-generated one:
```
      parseGraphQLServer = new ParseGraphQLServer(parseServer, {
        graphQLPath: '/graphql',
        graphQLCustomTypeDefs: gql`
          extend type Query {
            custom: Custom @namespace
          }
           type Custom {
            hello: String @resolve
            hello2: String @resolve(to: "hello")
            userEcho(user: _UserFields!): _UserClass! @resolve
          }
        `,
      });
```

Note:
- This PR includes a @namespace directive that can be used to the top level field of the nested queries and mutations (it basically just returns an empty object);
- This PR includes a @resolve directive that can be used to notify the Parse GraphQL Server to resolve that field using a cloud code function. The `to` argument specifies the function name. If the `to` argument is not passed, the Parse GraphQL Server will look for a function with the same name of the field;
- This PR allows creating custom types using the auto-generated ones as in `userEcho(user: _UserFields!): _UserClass! @resolve`;
- This PR allows to extend the auto-generated types, as in `extend type Query { ... }`.

2. Once the schema was set, you just need to write regular cloud code functions:
```
      Parse.Cloud.define('hello', async () => {
        return 'Hello world!';
      });

      Parse.Cloud.define('userEcho', async req => {
        return req.params.user;
      });
```

3. Now you are ready to play with your new custom api:
```
query {
  custom {
    hello
    hello2
    userEcho(user: { username: "somefolk" }) {
      username
    }
  }
}
```
should return
```
{
  "data": {
    "custom": {
      "hello": "Hello world!",
      "hello2": "Hello world!",
      "userEcho": {
        "username": "somefolk"
      }
    }
  }
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants