Quick Introduction to Strapi Headless CMS for Ionic ReactJS Mobile App w/GraphQL

Update to previous post where we used REST API to access the content

Image for post
Image for post

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

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 ApolloClient(), 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. [ See Previous Post ]

DC based software agency utilizing #Javascript, #Typescript, #HTML5, #Ionicframework, #NodeJS, #Firebase to build web & mobile solutions. https://buff.ly/300Zru

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store