DEV Community

Cover image for Add Cloudinary Support to Your Strapi Application
Strapi for Strapi

Posted on • Updated on

Add Cloudinary Support to Your Strapi Application

Author: Precious Luke

Description

In today's article, I will show you how to simply change the default upload provider of your Strapi application to Cloudinary. It is really easy and very straightforward. I will be showing you how to connect your Strapi application to use Cloudinary as a storage place for assets(photos and videos)

Prerequisites

To follow along with this tutorial you will need to have the following installed on your machine:

As stated on their FAQs page, Cloudinary is a very useful image and video management solution for websites and mobile apps. It covers everything from image and video uploads, storage, manipulations, optimizations to delivery. Cloudinary can deliver up to terabytes of data through a fast Content Delivery Network (CDN).

Step 1 - Set up Cloudinary

One of the first things you will need to do is to create a free account on Cloudinary. Once you managed to create your free account, you will be asked to confirm your email address. After confirmation, subsequently, whenever you log in, you will be redirected to the management dashboard of your account.

Cloudinary-dashboard

On the dashboard page you will find your Account Details that you will need to save for later:

  • Cloud Name
  • API Key
  • API Secret

Make sure to keep these details secret and do not share them with anyone.

Step 2 - Back-end Set up

Open up a terminal of your choice and navigate to the folder you want your project stored. I will create mine under C:\Projects, but you can store it wherever you want.
To follow along with this tutorial you will need an already up and running Strapi project or simply create one with the following command in your terminal:

yarn create strapi-app strapi-cloudinary --quickstart
Enter fullscreen mode Exit fullscreen mode

Once the creation of your project is completed you will be prompted to <http://localhost:1337/admin> to create the first admin user for your project, after the creation, you will be logged in to the administration panel.

After you managed to get into your administration panel, head to Content-Types Builder and create a collection-type called Posts.
Create required fields in order to store post related data for our blog, in this case, we are having these fields:

  • title: text (short text)
  • published_date: datetime
  • cover: media (single media)
  • content: rich text Adding collection types

Adding fields in Strapi

After you have added every field, hit Save. This will restart the server.

Save fields

Now make sure to add permissions to your Posts collection-type so it will be available to unauthenticated users on your site. Go under Settings → Roles ( Users & Permissions plugin ) → Public and simply check the boxes next to find and findOne actions for the posts endpoint*.* Click Save when done.

Users & Permission settings

Adding permissions for the Post content type

Now that we have our project set up, open the project's folder with your favorite IDE and install strapi-provider-upload-cloudinary package to your project.
Run this in your terminal at your project's location and you should be good to go:

yarn add @strapi-provider-upload-cloudinary

Make sure your Strapi server is not running at the moment of installation, you can restart it right after by running npm run develop.
After completion, create the following file ./config/plugins.js and add the below lines of code to it:

module.exports = ({ env }) => ({
  // ...
  upload: {
    config: {
      provider: 'cloudinary',
      providerOptions: {
        cloud_name: env('CLOUDINARY_NAME'),
        api_key: env('CLOUDINARY_KEY'),
        api_secret: env('CLOUDINARY_SECRET'),
      },
      actionOptions: {
        upload: {},
        delete: {},
      },
    },
  },
  // ...
});
Enter fullscreen mode Exit fullscreen mode

Under the root directory of your project create a .env file and add the following variables and with their respective values. These can be found in your Cloudinary dashboard under Account Details:

CLOUDINARY_NAME = cloudinary-name
CLOUDINARY_KEY = cloudinary-key
CLOUDINARY_SECRET = cloudinary-secret
Enter fullscreen mode Exit fullscreen mode

Step 3- Adding Some Post

Before we move ahead, we need add some contents. Go to Content Manager, and add some entires. Make sure you Save and Publish.

I will add three entries of post.

Step 4 - Fixing Preview Issue on Strapi Admin

After uploading a photo, it will upload to Cloudinary, but on your Strapi admin, you wont be able to preview the photo. You can fix this by replacing strapi::security string with the object below in ./config/middlewares.js.

{
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': ["'self'", 'data:', 'blob:', 'res.cloudinary.com'],
          'media-src': ["'self'", 'data:', 'blob:', 'res.cloudinary.com'],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
Enter fullscreen mode Exit fullscreen mode

It should look like so:

Go to this URL on your browser: http://localhost:1337/api/posts?populate=*

Step 5 - Fetching Data with GraphQL

We can also use GraphQL an alternative to REST to fetch data. To do that, first, install Graphql with this npm run strapi install graphql.
After that is done, go to http://localhost:1337/graphql.

You can start querying the data, to do so, input this in the GraphQL player environment and hit the Play button:

  query Post {
    posts {
      data {
        id
        attributes {
          title
          content
          published_date
          cover {
            data {
              id
              attributes {
                url
              }
            }
          }
        }
      }
    }
  }
Enter fullscreen mode Exit fullscreen mode

Response should look like so:

{
  "data": {
    "posts": {
      "data": [
        {
          "id": "4",
          "attributes": {
            "title": "J cole",
            "content": "This is a realcoleworld. This is a realcoleworld. This is a realcoleworld. This is a realcoleworld. This is a realcoleworld. This is a realcoleworld.",
            "published_date": "2021-12-05T23:00:00.000Z",
            "cover": {
              "data": {
                "id": "4",
                "attributes": {
                  "url": "https://res.cloudinary.com/localhost101/image/upload/v1638876030/J_Cole_3842b0a6a6.jpg"
                }
              }
            }
          }
        },
        {
          "id": "5",
          "attributes": {
            "title": "Kendrick Lamar",
            "content": "Just let me be me. Just let me be me. Just let me be me. Just let me be me. Just let me be me.",
            "published_date": "2021-12-14T23:00:00.000Z",
            "cover": {
              "data": {
                "id": "5",
                "attributes": {
                  "url": "https://res.cloudinary.com/localhost101/image/upload/v1638881415/kendr_3883e77ac9.jpg"
                }
              }
            }
          }
        },
        {
          "id": "6",
          "attributes": {
            "title": "Davido",
            "content": "Davido is the baddest. Davido is the baddest. Davido is the baddest. Davido is the baddest.Davido is the baddest.",
            "published_date": "2021-12-13T23:00:00.000Z",
            "cover": {
              "data": {
                "id": "6",
                "attributes": {
                  "url": "https://res.cloudinary.com/localhost101/image/upload/v1638881498/davido_d54888ae15.webp"
                }
              }
            }
          }
        }
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode


Step 6 - Adding Frontend

Great, now let's begin by creating our front-end application so we can display our images. I will be using Next.js for this tutorial but you can use any front-end framework you are comfortable with.
Inside a terminal of your choice create a folder where you would like your project to be stored and navigate to it.
One of the first things you will need to do is to install the create-next-app package globally by running one of the following commands:

yarn global add @create-next-app/core

or

npm install --global @create-next-app/core

Once everything is installed successfully you can start creating your NextJS application. The following line will save your time and will set up a quickstart template.

yarn create next-app frontend-cloudinary

To make sure everything installed correctly simply start your app with:

yarn dev
Before continuing the development let's install a few more dependencies that will be used throughout the application.
yarn add @apollo/react-hooks apollo-cache-inmemory apollo-client apollo-link-http graphql graphql-tag isomorphic-unfetch next-with-apollo
I will be using Apollo in order to interact with the GraphQL endpoint from the Strapi server.

@apollo/react-hooks is needed because we will be using React’s useQuery.
[apollo-cache-inmemory](https://www.npmjs.com/package/apollo-cache-inmemory) is used because we want to make use of caching. [apollo-client](https://www.apollographql.com/docs/react/) is a GraphQL state management library for managing both local and remote data. [apollo-link-http](https://www.npmjs.com/package/apollo-link-http) is the most common Apollo Link, a system of modular components for GraphQL networking. [graphql-tag](https://www.npmjs.com/package/graphql-tag) is JavaScript template literal tag that parses GraphQL query strings into the standard GraphQL AST. This requires us to install graphql because it uses it under the hood. [isomorphic-unfetch](https://www.npmjs.com/package/isomorphic-unfetch) is needed because it helps to switches between unfetch & node-fetch for client & server. [next-with-apollo](https://github.com/lfades/next-with-apollo) is for Apollo higher order component.

The following dependencies are important. Under the root directory of your front-end app create the following file ./utils/apollo.js and paste the following code inside:

import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import withApollo from "next-with-apollo";
import { createHttpLink } from "apollo-link-http";
import fetch from "isomorphic-unfetch";

const link = createHttpLink({
  fetch, 
  uri: "http://localhost:1337/graphql"
});

export default withApollo(
  ({ initialState }) => new ApolloClient({
      link: link,
      cache: new InMemoryCache().restore(initialState || {})
    })
);
Enter fullscreen mode Exit fullscreen mode

From line 1 to 5, we imported the from the dependencies we had just installed. From line 7 to 10, we created a constant variable link which uses a method createHttpLink from apollo-link-http. What this does is it fetches the data from the url: http://localhost:1337/graphql.

From line 12 to 17, withApollo is exported.

Under the root directory of your front-end app create the following file ./utils/apollo.js and paste the following code in this file:

import '../styles/globals.css'
import React from "react";
import { ApolloProvider } from "@apollo/react-hooks";
import withData from "../utils/apollo";

function MyApp({ Component, pageProps, apollo }) {
  return (
    <ApolloProvider client={apollo}>
      <Component {...pageProps} />
    </ApolloProvider>
  )
}

export default withData(MyApp)
Enter fullscreen mode Exit fullscreen mode

The Component prop is the active page, so whenever you navigate between routes, Component will change to the new page. Therefore, any props you send to Component will be received by the page.
For more details about the [_app.js](https://nextjs.org/docs/advanced-features/custom-app) file read here.

One of the next steps is to create a reusable Query component so that we can use it anywhere in our app. It will wrap your other components so that each time you will need to fetch data from your GraphQL endpoint, it will pass down the data as props to the children components.

Under the root directory of your front-end app create the following file ./components/query.js and paste the following code inside:

import React from "react";
import { useQuery } from "@apollo/client";
const Query = ({ children, query }) => {
  const { data, loading, error } = useQuery(query);
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {JSON.stringify(error)}</p>;
  return children({data} );
};
export default Query;
Enter fullscreen mode Exit fullscreen mode

One last thing to do before putting everything together is to create the actual GraphQL query. Create the following file ./apollo/queries/posts/posts.js and add the below code inside it:

import gql from "graphql-tag";

const POSTS_QUERY = gql`
query Posts {
    posts {
      id
      title
      content
      published_at
      cover {
        url
      }
    }
  }
`;

export default POSTS_QUERY;
Enter fullscreen mode Exit fullscreen mode

This is simply targeting the needed response based on the data structure we have on our Strapi collection. We are importing gql from graphql-tag for this. Then we went ahead to query the posts collections and its fields.

Now that we have everything ready for our app, let's start by setting it up together.
You will use the Next's Image component to render the images hosted on Cloudinary.
The Image component is provided by Next.js as of version 10.0.0. It allows for Automatic Image Optimization that will work with any image source you like. You will have optimized images even if you host them on an external data source, like in our case.

In order to have your images from Cloudinary optimized you will have to make the following changes to the next.config.js file:

module.exports = {
    images: {
      domains: ["res.cloudinary.com"],
    },
  }
Enter fullscreen mode Exit fullscreen mode

On the other hand, if you don't want your image to be optimized by the component you could set the in-optimized prop to true. Like so, the configuration above is not mandatory.
The code in your index.js file can simply be overwritten by the following:

import Head from 'next/head'
import styles from '../styles/Home.module.css'
import Query from "../components/query";
import POSTS_QUERY from "../apollo/queries/posts/posts";
import Image from 'next/image'

export default function Home() {
  return (
    <div className={styles.container}>
      <Query query = {POSTS_QUERY}>
        {({data:posts}) => {
          {const coder = posts.posts.data}
          {console.log(coder)}

            return(
            <div>
              <Head>
                <title>Create Next App</title>
                <link rel="icon" href="/favicon.ico" />
              </Head>


              <main className={styles.main}>
                <h1 className={styles.title}>
                  Welcome to <a href="https://nextjs.org">Next.js!</a>
                </h1>
                <p className={styles.description}>
                  Get started by editing{' '}
                  <code className={styles.code}>pages/index.js</code>
                </p>
                <div className = {styles.grid}> 

                  {coder.map((post) => (

                      <div className = {styles.card}>
                        <div className = {styles.image}>
                          <Image src = {post.attributes.cover.data.attributes.url} width={150} height={150}></Image>
                        </div>
                        <h1>{post.attributes.title}</h1>

                      </div>

                 ))}
                </div>
              </main>
              <footer className={styles.footer}>
                <a
                  href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  Powered by{' '}
                  <img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
                </a>
              </footer>
            </div>
            )
          }

        }

      </Query>
    </div>

  )};
Enter fullscreen mode Exit fullscreen mode

Now if you start your application with yarn develop your images should be nicely displayed on the home page.

This is the Github link for the frontend code.

Conclusion

Congratulations! Here are some of the things achieved in this tutorial: we discussed how to set up a Strapi instance(locally) and we also saw how to create a Cloudinary account and get credentials from it before putting them in .env file of our Strapi application. Again, we added post collection types structure and added some data to it to show you how it can be consumed.
We also went ahead and built a frontend that is displaying the uploaded images.
At the end of this article, you should easily change the default Strapi provider to Cloudinary. You can check more about other upload providers on the Strapi documentation here or on NPM. You can even create one of your own.

Discussion (0)