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)
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).
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.
On the dashboard page you will find your Account Details that you will need to save for later:
Make sure to keep these details secret and do not share them with anyone.
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:
1 yarn create strapi-app strapi-cloudinary --quickstart
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:
After you have added every field, hit Save. This will restart the server.
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.
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:
1 module.exports = ({ env }) => ({
2 // ...
3 upload: {
4 config: {
5 provider: 'cloudinary',
6 providerOptions: {
7 cloud_name: env('CLOUDINARY_NAME'),
8 api_key: env('CLOUDINARY_KEY'),
9 api_secret: env('CLOUDINARY_SECRET'),
10 },
11 actionOptions: {
12 upload: {},
13 delete: {},
14 },
15 },
16 },
17 // ...
18 });
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:
1CLOUDINARY_NAME = cloudinary-name
2CLOUDINARY_KEY = cloudinary-key
3CLOUDINARY_SECRET = cloudinary-secret
Before we move ahead, we need to add some content. Go to Content Manager, and add some entires. Make sure you Save and Publish.
I will add three entries of post.
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
.
1 {
2 name: 'strapi::security',
3 config: {
4 contentSecurityPolicy: {
5 useDefaults: true,
6 directives: {
7 'connect-src': ["'self'", 'https:'],
8 'img-src': ["'self'", 'data:', 'blob:', 'res.cloudinary.com'],
9 'media-src': ["'self'", 'data:', 'blob:', 'res.cloudinary.com'],
10 upgradeInsecureRequests: null,
11 },
12 },
13 },
14 },
It should look like so:
Go to this URL on your browser: http://localhost:1337/api/posts?populate=*
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:
1 query Post {
2 posts {
3 data {
4 id
5 attributes {
6 title
7 content
8 published_date
9 cover {
10 data {
11 id
12 attributes {
13 url
14 }
15 }
16 }
17 }
18 }
19 }
20 }
Response should look like so:
1 {
2 "data": {
3 "posts": {
4 "data": [
5 {
6 "id": "4",
7 "attributes": {
8 "title": "J cole",
9 "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.",
10 "published_date": "2021-12-05T23:00:00.000Z",
11 "cover": {
12 "data": {
13 "id": "4",
14 "attributes": {
15 "url": "https://res.cloudinary.com/localhost101/image/upload/v1638876030/J_Cole_3842b0a6a6.jpg"
16 }
17 }
18 }
19 }
20 },
21 {
22 "id": "5",
23 "attributes": {
24 "title": "Kendrick Lamar",
25 "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.",
26 "published_date": "2021-12-14T23:00:00.000Z",
27 "cover": {
28 "data": {
29 "id": "5",
30 "attributes": {
31 "url": "https://res.cloudinary.com/localhost101/image/upload/v1638881415/kendr_3883e77ac9.jpg"
32 }
33 }
34 }
35 }
36 },
37 {
38 "id": "6",
39 "attributes": {
40 "title": "Davido",
41 "content": "Davido is the baddest. Davido is the baddest. Davido is the baddest. Davido is the baddest.Davido is the baddest.",
42 "published_date": "2021-12-13T23:00:00.000Z",
43 "cover": {
44 "data": {
45 "id": "6",
46 "attributes": {
47 "url": "https://res.cloudinary.com/localhost101/image/upload/v1638881498/davido_d54888ae15.webp"
48 }
49 }
50 }
51 }
52 }
53 ]
54 }
55 }
56 }
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:
1 import { ApolloClient } from "apollo-client";
2 import { InMemoryCache } from "apollo-cache-inmemory";
3 import withApollo from "next-with-apollo";
4 import { createHttpLink } from "apollo-link-http";
5 import fetch from "isomorphic-unfetch";
6
7 const link = createHttpLink({
8 fetch,
9 uri: "http://localhost:1337/graphql"
10 });
11
12 export default withApollo(
13 ({ initialState }) => new ApolloClient({
14 link: link,
15 cache: new InMemoryCache().restore(initialState || {})
16 })
17 );
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:
1 import '../styles/globals.css'
2 import React from "react";
3 import { ApolloProvider } from "@apollo/react-hooks";
4 import withData from "../utils/apollo";
5
6 function MyApp({ Component, pageProps, apollo }) {
7 return (
8 <ApolloProvider client={apollo}>
9 <Component {...pageProps} />
10 </ApolloProvider>
11 )
12 }
13
14 export default withData(MyApp)
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:
1 import React from "react";
2 import { useQuery } from "@apollo/client";
3 const Query = ({ children, query }) => {
4 const { data, loading, error } = useQuery(query);
5 if (loading) return <p>Loading...</p>;
6 if (error) return <p>Error: {JSON.stringify(error)}</p>;
7 return children({data} );
8 };
9 export default Query;
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:
1 import gql from "graphql-tag";
2
3 const POSTS_QUERY = gql`
4 query Posts {
5 posts {
6 id
7 title
8 content
9 published_at
10 cover {
11 url
12 }
13 }
14 }
15 `;
16
17 export default POSTS_QUERY;
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:
1 module.exports = {
2 images: {
3 domains: ["res.cloudinary.com"],
4 },
5 }
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:
1 import Head from 'next/head'
2 import styles from '../styles/Home.module.css'
3 import Query from "../components/query";
4 import POSTS_QUERY from "../apollo/queries/posts/posts";
5 import Image from 'next/image'
6
7 export default function Home() {
8 return (
9 <div className={styles.container}>
10 <Query query = {POSTS_QUERY}>
11 {({data:posts}) => {
12 {const coder = posts.posts.data}
13 {console.log(coder)}
14
15 return(
16 <div>
17 <Head>
18 <title>Create Next App</title>
19 <link rel="icon" href="/favicon.ico" />
20 </Head>
21
22
23 <main className={styles.main}>
24 <h1 className={styles.title}>
25 Welcome to <a href="https://nextjs.org">Next.js!</a>
26 </h1>
27 <p className={styles.description}>
28 Get started by editing{' '}
29 <code className={styles.code}>pages/index.js</code>
30 </p>
31 <div className = {styles.grid}>
32
33 {coder.map((post) => (
34
35 <div className = {styles.card}>
36 <div className = {styles.image}>
37 <Image src = {post.attributes.cover.data.attributes.url} width={150} height={150}></Image>
38 </div>
39 <h1>{post.attributes.title}</h1>
40
41 </div>
42
43 ))}
44 </div>
45 </main>
46 <footer className={styles.footer}>
47 <a
48 href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
49 target="_blank"
50 rel="noopener noreferrer"
51 >
52 Powered by{' '}
53 <img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
54 </a>
55 </footer>
56 </div>
57 )
58 }
59
60 }
61
62 </Query>
63 </div>
64
65 )};
66```js
67
68Now if you start your application with yarn develop your images should be nicely displayed on the home page.
69
70
71![](https://paper-attachments.dropbox.com/s_00BFC2D3C57BAD5A1263654F5D21D0D01B780FCF92FEBB22F8E99C3AB0BAA17B_1641486120360_full+fledge.png)
72
73
74[T](https://github.com/bigpreshy/frontend-cloudinary)[his is the Github link for the frontend code](https://github.com/bigpreshy/frontend-cloudinary)[.](https://github.com/bigpreshy/frontend-cloudinary)
75
76## Conclusion
77
78Congratulations! 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.
79We also went ahead and built a frontend that is displaying the uploaded images.
80At 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](https://docs.strapi.io/developer-docs/latest/plugins/upload.html#using-a-provider) [here](https://docs.strapi.io/developer-docs/latest/plugins/upload.html#using-a-provider) or [on NPM](https://www.npmjs.com/search?q=strapi-provider-upload-&ranking=popularity). You can even create one of your own.
81
82
83
84## Host on Strapi Cloud
85
86The Strapi experience has always been about flexibility, customization, and open-source innovation. But we understand that managing infrastructure and scaling your application can sometimes be a daunting task, diverting your focus from what you do best: developing web experiences.
87
88That's why we're excited to introduce **[Strapi Cloud](https://strapi.io/cloud)**, so you can leverage the same robust Strapi capabilities you love, but without the hassle of managing servers, worrying about uptime, or scaling infrastructure as your project grows. It will allow you to future-proof your apps with a CMS that meets the content management needs of all your stakeholders, no matter your use case, services or devices.
89
90Strapi remains committed to open-source values, and self-hosting will always be a viable option. We believe in offering choices that align with your project's unique needs. Strapi Cloud is an additional resource for those who want to focus solely on development, leaving the infrastructure to us.
91
92Aw are here to support you every step of the way, no matter which hosting option you choose. You can reach out to us and the community on our [Discord](https://discord.strapi.io/) if you have any questions!
Precious Luke likes to explore new technologies. When he's not doing technical writing, you can find him doing Youtube videos or learning more about backend infrastructures with a keen interest in NodeJs.