-
-
Notifications
You must be signed in to change notification settings - Fork 97
-
This is explore possible ergonomics improvements to GraphQL support. For context, the initial library was built to support 100% of the powers of Rest Hooks high performance high data integrity with helpers built on the low level API. To expand upon GraphQL support more specializations can be created on top of. These of course will always be completely optional.
https://ntucker.notion.site/GQLResource-3879592f59a044f5a461f91ed0cbfb9e
Other previous discussions: #361
Before
export const userDetail = gql.query( (v: { name: string }) => `query UserDetail($name: String!) { user(name: $name) { id name email } }`, { user: User }, );
After
export const userDetail = gql.query( 'user ', { name: 'String' }, User, );
Before
const createReview = gql.mutation( (v: { ep: string; review: { stars: number; commentary: string }; }) => `mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { createReview(episode: $ep, review: $review) { stars commentary } }`, { createReview: Review }, );
After
const createReview = gql.mutation( 'createReview', { episode; 'Episode!', review: 'ReviewInput!' }, Review, );
Types can be registered in the construction of base endpoint
const gql = new GQLEndpoint( 'https://swapi-graphql.netlify.app/.netlify/functions/index', { schema: { 'ReviewInput': { stars: 'Number', commentary: 'String' } }}, );
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 1 comment
-
Nested arguments are supplemented with gql-custom schemas that inherit from base.
We might need to specify ‘... on Repository’ - not sure if this is not always the case
Before
gql.query( (v: { login: string }) => `query getByPinned($login: String!) { user(login: $login) { pinnedItems(first: 6, types: REPOSITORY) { nodes { ... on Repository { name owner { login } id description createdAt stargazerCount isFork forkCount isPrivate } } } } }`, { user: { pinnedItems: { nodes: [GqlRepository] }, } }, )
After
gql.query( `getByPinned($login: String!)`, { user: { pinnedItems: new schema.Object( { nodes: [GqlRepository.join('owner')] }, `pinnedItems(first: 6, types: REPOSITORY)` ) } }, );
Before
` query { getItems { id name price currency } } `
After
gql.query( `getItems`, { getItems: new schema.Collection([Item]) }, );
TODO: Check that mutation with user(user: $user)
rather than updateUser(user: $user)
is valid and works
Before
export const UserResource = { get: gql.query( `query GetUser($id: ID!) { user(id: $id) { id name username email phone website } } `, { user: User }, ), update: gql.mutation( `mutation updateUser($user: User!) { user(user: $user) { id name username email phone website } }`, { updateUser: User }, ), };
After
export const UserResource = { get: gql.query(`getUser($id: ID!)`, { user: User }), update: gql.mutation(`updateUser($user: User!)`, { user: user }), }; UserResource.update({ id: 'myid', username: 'bob' });
const UserResource = createResource({ schema: Todo, methods: ['get', 'update'], });
Schema Include/Exclude
Base definition should include all related fields in schema definition.
However, by not including a default value you can (by default) exclude requesting them and joining on them during denormalization.
TODO: Make sure denormalization doesn’t require any members that do not have a default value.
// defaults to not requesting author class Post extends GQLEntity { title = ''; content = ''; static schema = { author: User, related: [Post], } } const getPost = gql.query(`getPost($id: ID!)`, { post: Post.join('author', 'related') }); const getPosts = gql.query( `getPosts($first: Number)`, { getPosts: new schema.Collection([Post.join('author')]) } ); getPost('myid'); getPosts(6);
Entity.join
- GQLEntity validation is based on default members
- join creates an extended class that includes defaults based on the schema definition
- this makes that join item required
- this also has the affect of changing the auto-generated gql string
- this also ensures the resulting type includes the extra fields
- join must be memoized since schemas are used as MemoCache keys
Design Decisions
- graphql’s include/exclude fields are mostly useful for nested items. in fact, it’s usually a bad idea to selectively exclude simple scalar fields.
- Therefore we make the default case to automatically include all non-nested fields; and make it easy to specify which nested items should by default be included or not.
- Then we make it easy to selectively include any related item
- We still want schema definitions as a central source of truth - so our base Entity should know about all potential relationships (i.e., everything in the gql schema)
- Various nesting pieces might include additional parameters
- Therefore, we add additional arguments to support those in our gql - specific schemas.
- Schemas definitions have the structure of the gql query, therefore should be used to generate the whole query
- Top level of query or mutation is a function name and signature - we can use typescript magic to extract full definition from just the signature string
Beta Was this translation helpful? Give feedback.