GraphQL Codegen duplicates RegisterDocument with typescript-urql - graphql-codegen

The codegen.ts config below results in duplicating the RegisterDocument entries.
codegen.ts:
const config: CodegenConfig = {
overwrite: true,
schema: "http://localhost:4000/graphql",
documents: "src/graphql/**/*.graphql",
generates: {
"src/generated/graphql": {
preset: "client",
plugins: [
"typescript-urql"
],
config: {
documentVariableSuffix: 'test2'
}
}
}
};
the output:
export const RegisterDocument = {"kind":"Document", ...}
export const RegisterDocument = gql`
mutation Register($username: String!, $password: String!) {
register(options: {username: $username, password: $password}) {
errors {
field
message
}
user {
id
username
createdAt
}
}
}
`;
export function useRegisterMutation() {
return Urql.useMutation<RegisterMutation, RegisterMutationVariables>(RegisterDocument);
};
Seemingly either the documentVariableSuffix param didn't affect the output const naming or it was a wrong param. The use of the typescript-operations or/and typescript packages only led to more duplicates.
What is the way to have typescript-urql register the mutation differently?
UP. The register mutation I need typings for:
const registerMutationDocument = graphql(`
mutation Register($username: String!, $password: String!) {
register(options: { username: $username, password: $password }) {
errors {
field
message
}
user {
id
username
createdAt
}
}
}
`)

I'm Charly, from The Guild, working on GraphQL Code Generator.
The preset: "client" is not meant to be used in combination with other plugins.
You must use either the client-preset or typescript-urql-plugin which provides 2 different ways to get typed GraphQL Operations.
The typescript-urql-plugin generates React hooks while, the client-preset generated typed GraphQL documents that can be used with URQL's native useQuery() and useMutation().
We now recommend using the client-preset that provides a better developer experience and smaller generated code for the same result.
You will find a guide to setup the client-preset with URQL here: https://the-guild.dev/graphql/codegen/docs/guides/react-vue

After a few attempts with various code pieces it seems I've got it to work. Thank you mr. Poly for the hints.
Here's the take.
First, the present iteration of graphql-codegen watches for .ts/.tsx documents not *.graphql ones. Second the only plugins needed are the ones listed in the docs.
const config: CodegenConfig = {
overwrite: true,
schema: "http://localhost:4000/graphql",
documents: "src/graphql/mutations/*.ts",
generates: {
"src/generated/graphql/": {
preset: "client",
plugins: []
}
}
};
Third the way to put the mutations etc. to a dedicated folder that I used was to create one at src/graphql/mutations. The register.ts holding the mutation had the following code:
import { graphql } from '../../generated/graphql';
export const registerMutationDocument = graphql(`
mutation Register($username: String!, $password: String!) {
register(options: { username: $username, password: $password }) {
errors {
field
message
}
user {
id
username
createdAt
}
}
}
`);
The only problem for me was if I tried to add a field to it that didn't exist on the model the editor froze.
The usage within the component:
import { registerMutationDocument } from '../graphql/mutations/register';
...
const [, register] = useMutation(registerMutationDocument);

Related

Getting an error when having heuristic fragment matching with Apollo & Nuxt.js

I am trying to connect the below graphql query with nuxtjs.
query getContent($slug: String!) {
contents (filters: { slug: { eq: $slug } }) {
data {
id
attributes {
title
content {
__typename
... on ComponentContentParagraph {
content
}
}
}
}
}
}
I am getting the following error and not getting the result from the query.
You are using the simple (heuristic) fragment matcher, but your queries contain union or interface types. Apollo Client will not be able to accurately map fragments. To make this error go away, use the `IntrospectionFragmentMatcher` as described in the docs: https://www.apollographql.com/docs/react/advanced/fragments.html#fragment-matcher
I have checked the questions and answers available here.
Apollo+GraphQL - Heuristic Fragment Manual Matching
I have followed the docs from apollo as well.
https://www.apollographql.com/docs/react/data/fragments/#fragment-matcher
I managed to generate possibleTypes as mentioned in the docs.
Here is my next config.
apollo: {
includeNodeModules: true,
clientConfigs: {
default: "~/graphql/default.js",
},
},
This is the default.js
import { InMemoryCache } from "apollo-cache-inmemory";
import possibleTypes from "./possibleTypes.json";
export default () => {
return {
httpEndpoint: process.env.BACKEND_URL || "http://localhost:1337/graphql",
cache: new InMemoryCache({ possibleTypes }),
};
};
I am using strapi for the backend and this query works fine when running from graphql interface.

Is it possible to `readFragment` from apollo cache if it has no id?

I have a nested component in my app.
At the top of the page, I have a query like
const REPOSITORY_PAGE_QUERY = gql`
query RepositoryPageQuery($name: String!, $owner: String!) {
repository(name: $name, owner: $owner) {
...RepositoryDetailsFragment
}
}
${REPOSITORY_DETAILS_FRAGMENT}
`;
RepositoryDetailsFragment then includes
// list of branches
refs(first: 2, refPrefix: "refs/heads/") {
...BranchesFragment
}
and finally
fragment BranchesFragment on RefConnection {
totalCount
pageInfo {
...PageInfoFragment
}
edges {
node {
id
name
}
}
}
${PAGE_INFO_FRAGMENT}
Obviously, I am not happy, because I need to pass BranchesFragment info around 3 levels deep.
Instead, it would be great if I could read it from the cache directly in my BranchesList component.
I tried to use
client.cache.readFragment({
fragment: BRANCHES_FRAGMENT,
fragmentName: "BranchesFragment"
});
But the problem is that this fragment does not have any id. Is there any way to deal with it and get the fragment info?
Alright, I suddenly came to the solution. Maybe it could be useful for others.
Imagine we have a hierarchy of query -> fragments and components -> subcomponents like this:
RootPageComponent
query
query RepositoryPageQuery(
$name: String!
$owner: String!
$count: Int!
$branchSearchStr: String!
) {
repository(name: $name, owner: $owner) {
...RepositoryDetailsFragment
}
}
${REPOSITORY_DETAILS_FRAGMENT}
component returns the following
<RepositoryDetails repository={data.repository} />
RepositoryDetails
Has a fragment
fragment RepositoryDetailsFragment on Repository {
name
descriptionHTML
defaultBranchRef {
id
name
}
# the branches repository has
refs(first: $count, refPrefix: "refs/heads/", query: $branchSearchStr) {
...BranchesFragment
}
}
${BRANCHES_FRAGMENT}
and returns <BranchesList /> component.
So, instead of passing branch.info from RootPage to RepositoryDetails and then to BranchesList;
You can do the following in BranchesList
const client = useApolloClient();
client.cache.readFragment({
fragment: BRANCHES_FRAGMENT,
fragmentName: "BranchesFragment",
id: "RefConnection:{}" // note this {} - apollow cache adds it when no id is present for the object
})
IMPORTANT!
Make sure to also update type policy for the field and set keyArgs to []
So in this particular case:
RefConnection: {
keyFields: []
...
}
This will give the same result, but you won't have to pass props to nested components and instead can read from cache directly (just like one would do using redux)

Unable to use Fragments on GraphQL-yoga with Primsa

I am using graphql-yoga with Prisma and Prisma-Bindings
I'm trying to add a fragment to my resolver so that a specific field (id in this situation) is always fetched when the user asks for a custom field, costsToDate.
This is so i can make some additional queries needed to build the result for that field, and i need the ID of the object for that.
Unfortunatley i can't seem to get it to work, and the documentations seems a little lacking on the specifics with graphql-yoga and Prisma.
Here is the definition of the type:
type Job {
id: ID!
projectNumber: String!
client: Client!
name: String!
description: String
quoteNumber: String
workshopDaysQuoted: String!
quoted: String!
targetSpend: String!
costs: [JobCost!]!
estimatedCompletion: DateTime
completed: Boolean!
costTotal: String
workshopDaysUsed: String
costsToDate: String
}
And here is the resolver for the query:
const jobs = {
fragment: `fragment description on Jobs { id }`,
resolve: jobsResolver
}
async function jobsResolver(root, args, context, info) {
await validatePermission(args,context,info,['admin','user','appAuth'])
const {showCompleted} = args
const completedFilter = (typeof showCompleted === 'boolean') ? { completed: showCompleted } : {}
const jobIDS = await context.db.query.jobs({ where: completedFilter }, `{ id }`)
//console.log(jobIDS);
let jobs = await context.db.query.jobs({
where: completedFilter
}, info)
return await getAllJobCostsToDateList(jobs)
}
I am applying the the fragmentReplacements as per below.
const fragmentReplacements = extractFragmentReplacements(resolvers)
console.log(fragmentReplacements)
const port = process.env.PORT || 3010
const graphQLServer = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers,
resolverValidationOptions: {
requireResolversForResolveType: false
},
context: req => ({
...req,
db: new Prisma({
typeDefs: `src/generated/prisma.graphql`,
fragmentReplacements,
endpoint: PRISMA_ENDPOINT,
secret: PRISMA_KEY,
debug: false
})
})
})
If i console.log the fragmentReplacements object i get the following, so it does seem to be picking up the fragments.
[ { field: 'job', fragment: 'fragment costsToDate on Job { id }' },
{ field: 'jobs',
fragment: 'fragment costsToDate on Jobs { id }' } ]
So my expectation here is that if i make a query against jobs or job that asks for the costsToDate field that it will also fetch the id for the job/each job.
However if i make the following query.
query{
jobs{
description
costsToDate
}
}
But i see no id fetched, and nothing in the root parameter on the resolver function.
Apologies as i am probably barking up completely the wrong tree here, seems like a somewhat simple requirement, but i can't quite work it out. Sure i'm missing something fundamental.
Thanks!
Gareth
A fragment is used to always retrieve given fields on a given type.
It follows the following format:
fragment NameOfYourFragment on YourType { ... }
You currently can't apply a given fragment conditionally as it is always applied.
Moreover, you specified a fragment on Jobs, but the type name used by Prisma is Job (even if you have the job and jobs resolvers)
You probably only need the following resolver:
const job = {
fragment: `fragment JobId on Job { id }`,
resolve: jobsResolver
}

Enumerating all fields from a GraphQL query

Given a GraphQL schema and resolvers for Apollo Server, and a GraphQL query, is there a way to create a collection of all requested fields (in an Object or a Map) in the resolver function?
For a simple query, it's easy to recreate this collection from the info argument of the resolver.
Given a schema:
type User {
id: Int!
username: String!
roles: [Role!]!
}
type Role {
id: Int!
name: String!
description: String
}
schema {
query: Query
}
type Query {
getUser(id: Int!): User!
}
and a resolver:
Query: {
getUser: (root, args, context, info) => {
console.log(infoParser(info))
return db.Users.findOne({ id: args.id })
}
}
with a simple recursive infoParser function like this:
function infoParser (info) {
const fields = {}
info.fieldNodes.forEach(node => {
parseSelectionSet(node.selectionSet.selections, fields)
})
return fields
}
function parseSelectionSet (selections, fields) {
selections.forEach(selection => {
const name = selection.name.value
fields[name] = selection.selectionSet
? parseSelectionSet(selection.selectionSet.selections, {})
: true
})
return fields
}
The following query results in this log:
{
getUser(id: 1) {
id
username
roles {
name
}
}
}
=> { id: true, username: true, roles: { name: true } }
Things get pretty ugly pretty soon, for example when you use fragments in the query:
fragment UserInfo on User {
id
username
roles {
name
}
}
{
getUser(id: 1) {
...UserInfo
username
roles {
description
}
}
}
GraphQL engine correctly ignores duplicates, (deeply) merges etc. queried fields on execution, but it is not reflected in the info argument. When you add unions and inline fragments it just gets hairier.
Is there a way to construct a collection of all fields requested in a query, taking in account advanced querying capabilities of GraphQL?
Info about the info argument can be found on the Apollo docs site and in the graphql-js Github repo.
I know it has been a while but in case anyone ends up here, there is an npm package called graphql-list-fields by Jake Pusareti that does this. It handles fragments and skip and include directives.
you can also check the code here.

Can an Apollo Mutation return a custom result object?

The result returned from an Apollo mutation is typically a type or a subset of fields from a type, and this is usually great. So my mutation:
const addUserMutation = gql`
mutation createUser($email: String!, $permissions: [CreatePermissionInput]) {
createUser(input: {email: $email, permissions: $permissions}) {
id
created
lastUpdated
uid
email
permissions {
who
...
}
}
}
`
Which is calling:
extend type Mutation {
createUser(input: CreateUserInput!): User
}
Will return the user with the fields listed.
Problem is, I want to know if the user that we just tried to create already existed or not. So how can I edit the response to include this flag? Can you have a mutation return, say, an object like:
{
exists: true,
user: { ... }
}
So I can do this:
this.props.submit({
variables: {
email,
permissions,
},
}).then(({ result }) => {
console.log(result)
// > { exists: true, user: [USER OBJECT] }
})
I get that this will break the auto cache update but sometimes you need the response from an update to tell you more.
Create an additional type for the return result of mutation
type UserPayLoad {
exists:Boolean
user:User
}
extend type Mutation {
createUser(input: CreateUserInput!): UserPayLoad
}
Just try this. This may help you

Resources