Integrating S3 Storage Raya and Strapi for Asset Storage Optimization – Part 4

How can we help?
< All Topics
Print

Integrating S3 Storage Raya and Strapi for Asset Storage Optimization – Part 4

In the previous series, we’ve understood the steps to integrate content from the Strapi backend into the React frontend effectively. Now, let’s enhance the scalability and efficiency of our website.

One of the solution is to integrate our Strapi backend with the S3 storage service from CloudRaya, namely Storage Raya. Through this integration, we can store website assets, such as images and videos, more effectively and without worrying about storage space limitations on VMs.

Moreover, by adopting this storage approach, we will gain several other benefits, such as more centralized asset management, the ability to manage and secure asset access more effectively, and efficiency in handling traffic spikes.

Installation and S3 Upload Provider Configuration

In this tutorial, we will use the S3 Upload Provider from the AWS S3 API. Later, we will customize some of its configurations to align with the usage of Storage Raya.

The first step is to stop your Strapi server by pressing Ctrl+C in the terminal if you are still running it. Next, navigate to the main project directory, and install the following packages:

				cd backend-strapi
npm install @strapi/provider-upload-aws-s3
			

Create file named plugins.js inside the folder config, and insert the configuration to the file:

				//plugins.js
module.exports = ({ env }) => ({
    // ...
    upload: {
      config: {
        provider: 'aws-s3',
        providerOptions: {
          baseUrl: env('STORAGERAYA_CDN_URL'), // <- Opsional. Comment line ini jika tidak ingin menggunakan CDN
          s3Options: {
            accessKeyId: env('STORAGERAYA_ACCESS_KEY_ID'),
            secretAccessKey: env('STORAGERAYA_ACCESS_SECRET'),
            endpoint: env('STORAGERAYA_ENDPOINT'),
            params: {
               Bucket: env('STORAGERAYA_BUCKET')
             },
          },
        },
      },
    },
    // ...`
});
			

Update Strapi S3 Configuration

▶ Update variabel Environment

Edit and update the .env file in the Strapi backend by adding the following configuration:

				...

# STORAGE RAYA CONF
STORAGERAYA_ACCESS_KEY_ID= ACCESS_KEY_STORAGERAYA_ANDA
STORAGERAYA_ACCESS_SECRET= SECRET_KEY_STORAGERAYA_ANDA
STORAGERAYA_ENDPOINT= https://s3-jak01.storageraya.com
STORAGERAYA_BUCKET= NAMA_BUCKET_STORAGERAYA_ANDA
STORAGERAYA_CDN= https://link-cdn-bucket-anda.z.cdn.cloudraya.com
			

To understand how environment variables are used in Strapi, please read more on the following page.

▶ Update Middleware

Edit and update middlewares.js file inside config, folder, and become like this:

				//middleware.js

module.exports = ({ env }) => [
  'strapi::errors',
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': [
            "'self'",
            'data:',
            'blob:',
            '*.storageraya.com',
            '*.cloudraya.com'
          ],
          'media-src': ["'self'", 'data:', 'blob:', '*.storageraya.com'],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
  'strapi::cors',
  'strapi::poweredBy',
  'strapi::logger',
  'strapi::query',
  'strapi::body',
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
];
			

Build and restart our Strapi’s backend server.

				npm run build
npm run develop
			

Assessment Step

▶ Upload an Image

Navigate to the Media Library page in the Strapi admin panel. Then click + Add new assets. After that, choose the image you want to upload.

In a moment, the image you just uploaded will appear in the Media Library.

And also in our Storage Raya S3 bucket..

▶ Delete Image

The image that we delete in the Media Library will also be automatically deleted in the Storage Raya S3 bucket.

Adjusting Asset URLs on the Frontend

Before that, let’s look at the API response related to asset URLs.

Now, let’s re-upload in the Media Library all the cover image assets from the articles that we already have. Until the results are successfully displayed on the S3 bucket like this:

It appears that there have been changes in the API response as well, different from before when we were not using S3.

Next, let’s modify the syntax in some files that use source URL links (src) that refer to images. In this example, we will edit them in Articles.jsx and ArticleContent.jsxLook at the syntax that I have highlighted.

				// Articles.jsx

import React from 'react'
import { Link } from 'react-router-dom'
import { ReactMarkdown } from 'react-markdown/lib/react-markdown'

const Articles = ({articles}) => {

  return (
    <div className='w-full bg-[#f9f9f9] py-[50px]'>
        <div className='max-w-[1240px] mx-auto'>
            <div className='grid lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-2 ss:grid-cols-1 gap-8 px-4 text-black'>
                {articles.data.map((articlesmap)=>
                    <div  className='bg-white rounded-xl overflow-hidden drop-shadow-md'>
                        <Link to={`/article/${articlesmap.id}`}>
                        <img
                            className='h-56 w-full object-cover'
                            src={articlesmap.attributes.coverImage.data.attributes.url}
                            alt={articlesmap.attributes.title} />
                        </Link>
                        <div className='p-8'>
                        <Link to={`/article/${articlesmap.id}`}>
                            <h3 className='font-bold text-2xl my-1'>{articlesmap.attributes.title}</h3>
                        </Link>
                        <div className='flex space-x-2 mt-2'>
                            {articlesmap.attributes.categories.data.map((categorymap) => (
                            <span
                                key={categorymap.id}
                                className='px-2 py-1 text-sm font-medium bg-[#1A9FDA] text-gray-800 rounded'
                            >
                                {categorymap.attributes.name}
                            </span>
                            ))}
                        </div>
                        <ReactMarkdown className='mt-2 text-gray-600 text-xl line-break'>{`${articlesmap.attributes.body.substring(0, 150)}...`}</ReactMarkdown>
                            <Link to={`/article/${articlesmap.id}`} className='text-blue-500 font-medium'>
                                Read more
                            </Link>
                        </div>
                    </div>
                )}
            </div>
        </div>
    </div>
  )
}

export default Articles
			
				// ArticleContent.jsx

import React from 'react'
import { useParams } from 'react-router-dom'
import { ReactMarkdown } from 'react-markdown/lib/react-markdown'

const ArticleContent = ({articles}) => {
const {idFromURL}=useParams()
 
  
let articlemap = {};
let array = articles.data.filter((articlemap) => articlemap.id == idFromURL);
if (array.length > 0) {
  articlemap = array[0];
} else {
  return (
    <div className="flex items-center justify-center bg-[#bfbfbf] h-screen">
      <div className="bg-white p-6 rounded-md shadow-md text-red-500">
        <p className="ml-2">⛔ Article not found. ⛔</p>
      </div>
    </div>
  );
}
    
return (

    <div className='w-full pb-10 bg-[#f9f9f9]'>
      <div className='max-w-7xl mx-auto px-4 py-12 sm:px-6 lg:px-8'>
        <div className='grid lg:grid-cols-3 gap-8'>
          <div className='col-span-2 bg-white shadow rounded-lg overflow-hidden'>
          <img className='h-[300px] w-full object-cover' src={articlemap.attributes.coverImage.data.attributes.url} />
            <div className='p-6'>
              <h1 className='text-3xl font-semibold'>{articlemap.attributes.title}</h1>
               <div className='flex space-x-2 mt-2'>
                {articlemap.attributes.categories.data.map((categorymap) => (
                  <span
                    key={categorymap.id}
                    className='px-2 py-1 text-sm font-medium bg-[#1A9FDA] text-gray-800 rounded'
                  >
                    {categorymap.attributes.name}
                  </span>
                ))}
              </div> 
              <ReactMarkdown className='mt-2 text-gray-500 text-justify' >{articlemap.attributes.body}</ReactMarkdown>
            </div>
          </div>
          <div className='items-center w-full bg-white rounded-xl drop-shadow-md py-5 max-h-[250px]'>
                { <div>
                    <img className='p-2 w-32 h-32 rounded-full mx-auto object-cover' src={articlemap.authorImg} />
                    <h1 className='font-bold text-2xl text-center text-gray-900 pt-3'>{articlemap.authorName}</h1>
                    <p className='text-center text-gray-900 font-medium'>{articlemap.authorDesc}</p>
                  </div>
                }
            </div>
        </div>
      </div>
    </div>
  )
}

export default ArticleContent
			

It appears that the images can still be loaded successfully.

Summary

In this tutorial, we have learned the steps together in integrating Storage Raya with Strapi to store assets. From uploading and deleting images through the Strapi Admin Media Library to displaying them in the React frontend.

Other tutorial series that can complement your understanding of development with Strapi can be found in the earlier guides, or you can explore various other interesting tutorials on the CloudRaya Knowledge Base (KB) page.

Table of Contents

Ready, Set, Cloud

Ready, Set, Cloud