If you have ruby 3.2.2 set up on your machine feel free to clone my repo here and run rails server -b <YOUR LOCAL IP>
then rails db:seed
to get you started.
You'll need to run the following to install everything in the project you need for graphql
yarn add @apollo/client graphql
yarn add -D @graphql-codegen/cli @graphql-codegen/client-preset @parcel/watcher
Create this codegen.ts
file and populate it with the following
import { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: "http://<YOUR_LOCAL_IP>:3000/graphql",
documents: ["src/**/*.{ts,tsx,graphql}"],
generates: {
"./src/__generated__/": {
preset: "client",
plugins: [],
presetConfig: {
gqlTagName: "gql",
fragmentMasking: false,
},
},
},
ignoreNoDocuments: true,
};
export default config;
After this you can add the following scripts to your package.json
{
...
"compile": "graphql-codegen",
"watch": "graphql-codegen -w"
}
Go back into your App.tsx and wrap the Home component with the Apollo Provider
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
import { Home } from "./src/Home";
const client = new ApolloClient({
uri: "http://192.168.2.28:3000/graphql",
cache: new InMemoryCache(),
});
export default function App() {
return (
<ApolloProvider client={client}>
<Home />
</ApolloProvider>
);
}
At this point you should be able to add a graphql query to any file in your app and run yarn run compile
or watch
to be able to import the typed GraphQL files. Make sure to import gql
from the __generated__
in order to have actual type safety
Back in the src
directory let make a query file graphql/author.ts
When you use your GraphQL queries its usually a good idea to wrap them with a hook. You could, of course, just invoke useMutation
and useQuery
and hope for the best; but with all things in software, its usually best to keep things organized.
Here we start by creating a folder to hold our hooks called hooks
. Inside this folder, create a new file called useTodosByAuthorId.ts
. You'll want to create this file:
import { useQuery } from "@apollo/client";
import { gql } from "../__generated__";
export const GET_TODOS_FOR_AUTHOR = gql(`
query GetTodosForAuthor($id:ID!) {
getTodosForAuthor(id:$id) {
id
authorId
title
message
completed
}
}
`);
export const useTodosByAuthorId = (id: string) => {
const result = useQuery(GET_TODOS_FOR_AUTHOR, {
variables: {
id,
},
});
return result;
};
We then go back to home and hard code the author id to 1
export function Home() {
const [visible, setVisible] = useState(false);
const { data, loading, error, refetch } = useTodosByAuthorId("1");
We can then just plug this into the FlatList
<FlatList
data={data?.getTodosForAuthor}
renderItem={(data) => {
return <ListItem item={data.item} onPress={() => {}} />;
}}
/>
Restarting the app should grab all the todos for that author
We are going to start by creating a fragment so we can share code. This can be done in three steps
- Create a
src/hooks/Fragments.ts
file that will house our fragments - Add a fragment for the Essential items of a Todo to the file
import { gql } from "../__generated__";
export const ESSENTIALS_FRAGMENT = gql(`
fragment Essentials on Todo {
authorId
id
title
message
completed
}
`);
- Register the Fragment.
import { createFragmentRegistry } from "@apollo/client/cache";
const client = new ApolloClient({
uri: "http://192.168.2.28:3000/graphql",
cache: new InMemoryCache({
fragments: createFragmentRegistry(ESSENTIALS_FRAGMENT),
}),
});
Next lets refactor the useTodosByAuthorId
hook to use the fragment:
export const GET_TODOS_FOR_AUTHOR = gql(`
query GetTodosForAuthor($id:ID!) {
getTodosForAuthor(id:$id) {
...Essentials
}
}
`);
Finally lets make the Create Todo Hook src/hooks/useCreateTodo.ts
:
import { useMutation } from "@apollo/client";
import { gql } from "../__generated__";
export const CREATE_TODO_FOR_AUTHOR = gql(`
mutation CreateTodoForAuthor($title:String!, $message:String!, $authorId:ID!) {
createTodo(input: { title:$title, message:$message, authorId:$authorId }) {
...Essentials
}
}
`);
export const useCreateTodo = (authorId: string) => {
const [createTodo, { loading, error, data }] = useMutation(
CREATE_TODO_FOR_AUTHOR
);
const createNewTodo = async (title: string, message: string) => {
await createTodo({
variables: {
title,
message,
authorId,
},
});
};
return {
createNewTodo,
loading,
error,
data,
};
};
We can then update Home to have the same properties
const { data, loading, error, refetch } = useTodosByAuthorId("1");
const { createNewTodo } = useCreateTodo("1");
...
const createTodo = async (title: string, message: string) => {
await createNewTodo(title, message);
setVisible(false);
refetch();
};
Create a new file useCompleteTodo.ts
Enter the following Code:
import { gql, useMutation } from "@apollo/client";
import { useCallback } from "react";
export const COMPLETE_TODO = gql(`
mutation CompleteTodo($authorId:ID!, $todoId:ID!, $completed:Boolean!) {
completeTodo(input:{
authorId:$authorId,
todoId:$todoId,
completed:$completed
}) {
...Essentials
}
}
`);
export const useCompleteTodo = (authorId: string) => {
const [completeTodo, { error, loading, data }] = useMutation(COMPLETE_TODO);
const markTodoAsComplete = useCallback((todoId: string, completed = true) => {
completeTodo({
variables: {
authorId,
todoId,
completed,
},
});
}, []);
return {
markTodoAsComplete,
error,
loading,
data,
};
};
Finally update the home component one last time
const { markTodoAsComplete } = useCompleteTodo("1");
<FlatList
data={data.getTodosForAuthor}
renderItem={(data) => {
const onPress = () => {
markTodoAsComplete(data.item.id, !data.item.completed);
};
return <ListItem item={data.item} onPress={onPress} />;
}}
/>;