Graphql-codegen nested fragment use inside a React component - graphql-codegen

The nested fragment regularUserResponseFragmentDocument
import { graphql } from '../../generated/graphql';
export const regularErrorFragmentDocument = graphql(`
fragment regularError on FieldError {
field
message
}
`);
export const regularUserFragmentDocument = graphql(`
fragment regularUser on User {
id
username
}
`);
export const regularUserResponseFragmentDocument = graphql(`
fragment regularUserResponse on UserResponse {
errors {
...regularError
}
user {
...regularUser
}
}
`);
is used in a component like so:
if (response.data?.login) {
const data = useFragment(regularUserResponseFragmentDocument, response.data.login);
const errorsData = useFragment(regularErrorFragmentDocument, data.errors);
if (errorsData) {
setErrors(toErrorMap([...errorsData]));
}
const userData = useFragment(regularUserFragmentDocument, data.user);
if (userData) {
await router.push('/');
}
}
Is there a way to avoid use of useFragment on the nested fragments?

No there is not; this is the proper way to get nested masked fragments.
You will find a similar example here where useFragment() was renamed to getFragmentData() (from codegen config) to avoid React hooks rules issues: https://github.com/charlypoly/codegen-repros/blob/master/client-preset-nested-fragments-interface/src/App.tsx

Related

Apollo useQuery hook on component mount

I'm new to hooks and trying to use them more
How can I get data (with Apollo) when a component mount ?
I'm trying to use useQuery inside a useEffect, my code so far looks like this
const MyComponent = () => {
const getME = () => {
const { loading, error, data } = useQuery(ME);
setMe(data.me) // useState hook
console.log('query me: ', me);
};
useEffect(getME);
return (<>
...
</>)
}
but this gives me an error
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
edit: this is the query
import { gql } from '#apollo/client';
export const ME = gql`
query me {
profile {
firstName
lastName
}
}
`;
Here is an example on how you should use the useQuery hook and then stock the data in the state
const { loading, data, error } = useQuery(SOME_QUERY)
const [state, setState] = React.useState([])
useEffect(() => {
// do some checking here to ensure data exist
if (data) {
// mutate data if you need to
setState(data)
}
}, [data])`enter code here`
from https://github.com/trojanowski/react-apollo-hooks/issues/158

Apollo client fragments not embedding data

This is the first time I've ventured into fragments and I can't see where I'm screwing up, but it definitely isn't working! In GraphiQL it's working fine:
query Tasks($taskIds: [String]!) {
tasks(taskIds: $taskIds) {
...taskDisplay
}
}
fragment taskDisplay on Task {
_id
name
description
status
children {
_id
}
}
Here's what's in my client app:
import { gql } from "#apollo/client";
export const TASK_FRAGMENT = gql`
fragment taskDisplay on Task {
_id
name
description
status
children {
_id
}
}
`;
export const TASKS = gql`
query Tasks($taskIds: [String]!) {
tasks(taskIds: $taskIds) {
...taskDisplay
}
}
${TASK_FRAGMENT}
`;
So, the server returns the data correct as I can see in the Network tab of Chrome, but the data received by the useQuery result is an empty object. What gives?
Using #apollo/client#3.2.0-beta.2 (I have downgraded to 3.1.0 with same results)
EDIT:
Adding more info. My code is about as simple as it could be using a hook. Here's what's happening:
import { useQuery, gql } from "#apollo/client";
import { TASK_FRAGMENT } from "../pages/task/queries";
const ROOT_TASK_QUERY = gql`
query Project($projectId: String!) {
rootTask(projectId: $projectId) {
...taskDisplay
}
}
${TASK_FRAGMENT}
`;
const useProject = ({ variables }) => {
return useQuery(ROOT_TASK_QUERY, {
variables,
});
};
export default useProject;
And this is just logging the query itself:
Your returned data is missing the __typename field

Graphql Api Chuck Norris

I need help building a GraphQL Api that wraps the ChuckNorris.io API
The API sholud have aQuery type that resolves all Categories
(https://api.chuckmorris.io/jokes/categories)
The Api should have Query type that resolves a random joke given as an argument (https://api.chucknorris.io/jokes/random?category={category})
const express=require('express');
const {ApolloServer,gql}=require('apollo-server-express');
const fetch=require('node-fetch');
const typeDefs=gql`
type Joke{
icon_url:String,
id:String,
url:String
value: String
}
type Category{
animal:String
career:String
celebrity:String
dev:String
explicit:String
fashion:String
food:String
history:String
money:String
movie:String
music:String
political:Strig
religion:String
science:String
sport:String
travel:String
}
type Query{
getCategory(category:String!):Joke
category:Category
}
`
const resolvers={
Query:{
getCategory: async(_,{category})=>{
const response=await fetch(`https://api.chucknorris.io/jokes/random?category=${category}`)
return response.json();
},
category: async(_,{})=>{
const response=await fetch('https://api.chucknorris.io/jokes/categories')
return response.json();
}
}
}
const server= new ApolloServer({typeDefs,resolvers});
const app=express();
server.applyMiddleware({app});
app.listen({port:4000},()=>
console.log('Now browse to http://localhost:4000' + server.graphqlPath)
)
your query for type category should return a list of strings (array)
so
export const typeDefs = gql`
type Joke {
value: String!
id:ID!
icon_url:String!
}
type Query {
getAllCategories:[String!]!
randomJoke(category: String!):Joke
}
`;
for your resolver, you don't need fetch. apollo provides datasources to connect to external REST APIs like the one you have.
so install the npm package "apollo-datasource-rest" and add it to your instance of apollo server like so
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: ()=>({
jokeinstance : new Jokenorris
})
})
then create the datasource class for Jokenorris and import appropriately or do everything in one src file as you did.
import pkg from "apollo-datasource-rest";
const { RESTDataSource } = pkg;
export class Jokenorris extends RESTDataSource {
constructor() {
super();
this.baseURL = "https://api.chucknorris.io/jokes";
}
async getAllCategories() {
const res = await this.get("categories");
return res;
}
async getRandomJoke({ category }) {
const response = await this.get("random", { category: category });
return response;
}
}
then your resolveer can look like so, you can ignore the exports and imports if you chunked everything in one file
export const resolvers = {
Query: {
allJokeCategories: (_, __, { dataSources }) =>
dataSources.jokeinstance.getAllCategories(),
randomJoke: (_, {category}, {dataSources})=>
dataSources.jokeinstance.getRandomJoke({category:category})
},
};

Why apollo server custom directive not working?

I am trying to implement a custom directive with apollo server. I took the example from the official site.
My query is like below:
directive #upper on FIELD_DEFINITION
type Query {
hello: String #upper
}
My resolver is like below:
Query:{
async hello(){
return "hello world";
}
}
Here is my apollo server config for custom directive:
const { ApolloServer, SchemaDirectiveVisitor } = require('apollo-server-express');
const { defaultFieldResolver } = require("graphql");
class UpperCaseDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field;
field.resolve = async function (...args) {
const result = await resolve.apply(this, args);
if (typeof result === "string") {
return result.toUpperCase();
}
return result;
};
}
}
const server = new ApolloServer({
schema,
schemaDirectives: {
upper: UpperCaseDirective
},
introspection: true,
playground: true,
cors: cors()
});
The output I always get :
{
"data": {
"hello": "hello world"
}
}
Why the custom directive is not activated? Why the output is not in uppercase?
You would pass schemaDirectives to ApolloServer's constructor if ApolloServer was building your schema for you -- that is, if you were also passing in resolvers and typeDefs. If you're passing in an existing schema, it's already built and ApolloServer won't apply the directives. If you're using makeExecutableSchema, you can pass your schemaDirectives to that. It's also possible to manually visit all directives like this:
SchemaDirectiveVisitor.visitSchemaDirectives(schema, schemaDirectives)
This is the only way to get directives to work with certain libraries, like graphql-modules.

Why does GraphQL require me to have a resolver that returns an empty object?

So, when I have a schema:
type Query { me: Me }
type Me { hello: String }
I expect a satisfying resolver to be:
const resolvers = {
Me: {
hello() { return "Hi, me!"; }
}
}
Alas, this isn't the case, I have to add a dummy me-resolver (see below).
Why is this the case?
I'd say it should traverse the query, and if it can't find a satisfying field-resolver, it should look for a corresponding type-resolver.
const { graphql } = require("graphql");
const { makeExecutableSchema } = require("graphql-tools");
const compose = require("compose-function");
const typeDefs = `
type Query {
me: Me
}
type Me {
hello: String!
}
`;
const resolvers = {
Query: {
// ===========================>
me() {
return {};
}
// <===========================
},
Me: {
hello() {
return "Hi, me!";
}
}
};
const schema = makeExecutableSchema({
typeDefs,
resolvers
});
graphql(schema, "{ me { hello } }").then(
compose(
console.log,
JSON.stringify
)
); // {"data":{"me":{"hello":"Hi, me!"}}}
Alright, I've figured it out. It actually makes a lot of sense.
null is a valid response. Only if there is an object with properties that the query wants, it should look for the next resolver to satisfy the query.
It's also written in the spec under Errors and Non‐Null Fields https://graphql.github.io/graphql-spec/June2018/#sec-Executing-Selection-Sets

Resources