Overview
I started a series on using strapi.io as a headless CMS for Ionic Framework application written in ReactJS. In all of the earlier videos, I was using the REST API to access the content in the CMS and I wanted to try using the GraphQL API that is provided.
Strapi is the leading open-source headless CMS. It’s 100% Javascript, fully customizable and developer-first.
This post goes along with the video I created showing how to refactor the code from the REST API to start using the GraphQL API.
This post should be seen as a companion document to the video and source code
- Make Sure to Watch Video To Setup Server to Support GraphQL
- REST API Implementation Video
Lets Go
Install libraries we need to get graphql integrated with strapi.
npm install apollo-upload-client
npm i --save-dev @types/apollo-upload-client
npm install graphql @apollo/client
Now that we have the libraries, let's set up the client in index.tsx
First we import the necessary libraries
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";
import { createUploadLink } from "apollo-upload-client";
The create the client from new AplolloClient()
, since we are uploading files we are using the createUploadLink
function to create the link associated with the strapi server; We will also be using the in memory cache
const client = new ApolloClient({
link: createUploadLink({
uri: "http://localhost:1337/graphql",
}),
cache: new InMemoryCache(),
});
Finally, wrap the whole app with the ApolloProvider
which will give us access to the client in the application.
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById("root")
);
Loading All The ImagePosts
We are going to load all of the posts because they are needed for the first page of the app Home.tsx
We need to import the libraries, we're going to use to support the useQuery
hook
import { gql, useQuery } from "@apollo/client";
Let's set up the query which was tested in the playground; we use this to get all of the ImagePosts and the properties from the object we need to render in the user interface.
const IMAGE_POST_QUERY = gql`
query getAll {
imagePosts {
id
title
body
image {
name
id
url
}
}
}
`;
Now we can utilize the useQuery hook to get the data, provide us with some loading information and an error object if necessary.
const {
loading,
error,
data,
} = useQuery(IMAGE_POST_QUERY);
Now let's move on to the template and start with adding the IonLoading component which uses the loading
property from above.
<IonLoading isOpen={loading} message="Loading..."/>
The query returns the data with the property imagePosts
because that is what I specified in the query; we loop through that property to render the results.
<IonList>
{!loading && data?.imagePosts?.map((p: any) => {
return (
<IonItem key={p.id}>
<IonLabel>
<h1 className="ion-text-wrap">{p.title}</h1>
<h2 className="ion-text-wrap">{p.body}</h2>
<p className="ion-text-wrap">{p.image?.name}</p>
<div>
<IonImg
src={`http://localhost:1337${p.image?.url}`}
></IonImg>
</div>
</IonLabel>
</IonItem>
);
})}
</IonList>
Adding A New ImagePost
The same process as before when querying the data, we will use when mutating the data. First we define the mutation we will use with the useMutation
hook and pass it the appropriate parameters.
Like before this is a two-step process, upload the file and then add the post
We will upload the selected image using this upload mutation constant UPLOAD_MUTATION
const UPLOAD_MUTATION = gql`
mutation($file: Upload!) {
upload(file: $file) {
name
id
}
}
`;
Next we are setting the hook up with the name of the method we will use addImageGQL
. We will need the loading status for the component and then finally we pass in the query.
const [
addImageGQL,
{ loading: loadingUpload }
] = useMutation(UPLOAD_MUTATION);
In order to call the function and upload the file, we use the addImageGQL
method like this. The file parameter is from the local state variable we defined to hold the file object returned from the input form.
const {
data: imageData
} = await addImageGQL({ variables: { file } });
This will upload the file for us and provide us with the id of the uploaded file to associate with the ImagePost. We can access it like this.
imageData.upload.id
Now that we have the image in the CMS we can get an id to associate with the imagePost and save the whole document.
First we need the imagePost mutation; a constant UPLOAD_IMAGE_POST_MUTATION
notice that all of the parameters we need for the mutation are the fields we captured in the input form in AddItem.tsx
; We also can specify the fields we need to be returned from the mutation.
const UPLOAD_IMAGE_POST_MUTATION = gql`
mutation createImagePost($title: String, $body: String, $image: ID) {
createImagePost(
input: { data: { title: $title, body: $body, image: $image } }
) {
imagePost {
id
title
body
image {
id
url
name
}
created_at
}
}
}
`;
To upload the post we use the useMutation
hook and pass along the id of the image and the title
and body
from the input form.
const [
addImagePostGQL,
{ loading: loadingImagePost }
] = useMutation( UPLOAD_IMAGE_POST_MUTATION);
here is the use of the hook in action
const { data: postData } = await addImagePostGQL({
variables: {
title,
body,
image: imageData.upload.id,
},
});
At this point, you should be able to see that the document has been added to the strapi CMS.
to handle optimistic load of the imagePosts, meaning load the imagePost in the local cache; we can push the new record into the cache using the following code.
const [
addImagePostGQL,
{ loading: loadingImagePost }
] = useMutation(
UPLOAD_IMAGE_POST_MUTATION,
{
update: (cache, { data: { createImagePost } }) => {
const { imagePost } = createImagePost;
// get the posts from the cache...
const currentData: any = cache.readQuery({ query: IMAGE_POST_QUERY });
// add the new post to the cache & write results back to cache
cache.writeQuery({
query: IMAGE_POST_QUERY,
data: {
imagePosts: [...currentData?.imagePosts, imagePost],
},
});
},
}
);
Conclusion
As stated above, this is meant to accompany the video so please take a look at the videos in the series, review the document and if it is still not clear, leave a comment.