BACKGROUND
Serverless frameworks enable frontent developers to build full stack applications without having to operate and manage the entire application infrastructure.
By abstracting unnecessary complexities from the development process, developers don't need to manually implement backend tasks like scaling server, provisioning capacity, and resources. Instead, the coding logic embedded in serverless frameworks such as Netlify Functions, automates much of the back-end legwork.
In this article, we'll explore how you can leverage Netlify's serverless framework to connect Cloudinary and Airtable in order to perform image manipulation, transformation, and storage.
The complete source code used in this tutorial is available on Codesandbox.
Prerequisites
While this is a beginner-friendly tutorial, you'll want to have some experience writing functional JavaScript, downloading libraries, etc.
You will need Node.js and Netlify CLI installed on your machine. They are the core dev dependencies of this project.
Cloudinary, Airtable and Netlify are the platforms we''ll be leveraging to deploy, store, and manipulate images.
Cloudinary setup
If you don't have a Cloudinary account yet, you can sign up for a free one.
After verifying your account, you can login to access the required credentials.
From the dashboard, write down your cloud_name, api_key & api_secret
. We'll be using them in the application.
The next step is to create a folder where all the application images will be stored. Click on the Media library tab, and create a folder. Name it aircloud
.
A great way to manipulate images as we upload them to Cloudinary is by using upload presets. This will enable one to pass one or more upload parameters defined in the cloudinary documentation. In this application, we will create an upload preset to resize images. To do this;
Click on the settings icon on the navigation bar of your Cloudinary dashboard,
Scroll down to the upload presets section, and click on the
Add upload preset
link.Give your preset a
name
, and make the signing modeSigned
, since we want the parameters declared with the requests we make to be considered first.On the
Folder
form, please input the name of the folder you would want your application images to be stored. In our case, we named the folderaircloud
.Click on the save button and the setup will be complete.
Airtable setup
Airtable is an easy-to-use online platform for creating and sharing relational databases. This is where we will store the deployed images, IDs, and Urls, in order to access them on our application. After creating an account on the platform, you’ll need to have a base (database), and a table/sheet set up before you can start to programmatically interact with the database.
You shall need the apiKey
, base id
and table name
in order to access the base (database) from your application.
Upon creating the table and getting the credentials your table structure should look like :
A detailed step by step introduction to Airtable can be found Here.
With all the platforms above setup we can now start to build the application.
REACT SETUP
To get started with Create React App:
Step 1: Create React App
Navigate to the project directory of your choice and run :
1npx create-react-app aircloud
After the installation is done, you will have a React application placed in the folder aircloud.
Step 2: Install project dependencies
Install the required dependencies the application will use by running the command below:
1npm i dotenv cloudinary-react cloudinary
When it comes to structuring the appearance of the application, we're going to be leveraging on bootstrap
a Css library.
Add the following CDN to your index.html
file found inside the public
directory :
1<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
Step 3: Configure environment variables
After you setup the different platforms, and acquire the credentials, create an .env file on the root of your folder, and add the respective values :
1CLOUDINARY_CLOUD_NAME = xxxxx;2CLOUDINARY_API_KEY = xxxxxxx;3CLOUDINARY_API_SECRET = xxxxxxx;4CLOUDINARY_UPLOAD_PRESET = xxxxxxx;5AIRTABLE_API_KEY = xxxxxxxx;6AIRTABLE_BASE_ID = xxxxxxxxx;7AIRTABLE_TABLE_NAME = xxxxxxxx;
Step 4: Set up Netlify functions
To use the Netlify functions to manage the application, create a netlify.toml
file on the root project folder, and paste the code below:
1[build]2 functions="functions"
This file will define how Netlify will build and deploy your site. All the serverless functions shall be stored in the functions folder.
Now, create a functions folder in the root project directory as defined in the netlify.toml
file; this will be the home to all our severless functions.
In order to interact with Airtable within our application without having to repeat the same code everytime in different files, create a utils folder inside the functions folder, and add an airtable.js
file.
Paste the following in it:
1require('dotenv').config();2const Airtable = require('airtable');34Airtable.configure({5 apiKey: process.env.AIRTABLE_API_KEY,6});78const base = Airtable.base(process.env.AIRTABLE_BASE_ID);9const table = base(process.env.AIRTABLE_TABLE_NAME);1011module.exports = {12 base,13 table,14};
This will enable us to achieve the coding principle of don't repeat yourself
by reusing the file.
From the code above, I imported the dotenv package in order to access the environment variables stored in the .env file. I then initialized and configured the Airtable library, which enabled me to create and export variables that enable one to access the base and table globally within the application.
With all of the above setup and configurations, its time to spin up the server. Since we will be leveraging on cloud functions, replace the default React start command npm start
and use Netlify CLI to perform this operation. Run the following command on your terminal:
1netlify dev
Step 5: Create the upload and image display components
The application will have two major components that will be used to upload and display the images. To enable this, create a components
folder inside the src
directory of your react application, and add an Upload.js
& ImageGallery.js
file.
Now, navigate back to the App.js
component, and paste the following code :
1import './App.css';2import Upload from './components/Upload';3import ImageGallery from './components/ImageGallery';4// import Title from './components/Title';56function App() {7 return (8 <div className='container'>9 <Upload />10 <ImageGallery />11 </div>12 );13}1415export default App;
Upload Files Component
We can now build the Upload.js
component that will allow the upload and storage of images.
1import React, { useState } from 'react';23const Upload = () => {4 const [imageDataUrl, setImageDataUrl] = useState('');56 const handleChange = (e) => {7 const file = e.target.files[0];8 const reader = new FileReader();9 reader.readAsDataURL(file);10 reader.onloadend = () => {11 setImageDataUrl(reader.result);12 };13 reader.onerror = () => {14 console.log('error');15 };16 };1718 return (19 <div>20 <form onSubmit={submitHandler}>21 <label>22 <input type='file' onChange={handleChange} />23 <span>+</span>24 </label>2526 <button type='submit' className='button' disabled={!imageDataUrl}>27 {' '}28 Upload Image29 </button>30 </form>31 {imageDataUrl && (32 <img className="height-50 w-50" src={imageDataUrl} alt="aircloud_gallery" />33 )}34 </div>35 </div>36 );37};3839export default Upload;
Using the useState hook in the component will enable you track changes when uploading images.
The handleChange function capture the first file selected, converts the file into a string and sets the application state to have that Url by using setImageDataUrl
We then create a form inside the return statement that will utilize the handleChange
function when triggered.
The last piece to make the component complete is to create a submitHandler
which will be responsible for uploading files
1const submitHandler = async (e) => {2 e.preventDefault();3 console.log('submitting');4 try {5 const res = await fetch(6 deployed_function,7 {8 method: 'POST',9 body: imageDataUrl,10 }11 );1213 const data = res.json();14 // console.log(data);15 setImageDataUrl('');16 } catch (err) {17 console.error(err);18 }
The preventDefault() method is used to prevent the upload button from submitting the form, after which we made a post request to the severless api endpoint that we will create to store the image to Cloudinary, and the url to Airtable.
File UPLOAD SEVERLESS FUNCTION
Inside the functions folder, create an upload.js
file that will hold all the upload logic, and add the following:
1require('dotenv').config();2const cloudinary = require('cloudinary').v2;34cloudinary.config({5 cloud_name: process.env.CLOUDINARY_CLOUD_NAME,6 api_key: process.env.CLOUDINARY_API_KEY,7 api_secret: process.env.CLOUDINARY_API_SECRET,8});910const { table } = require('./utils/airtable');1112exports.handler = async (event) => {13// Capture the file from the event body14 const file = event.body;1516 try {17 // Upload the file captured to cloudinary18 const { public_id, secure_url } = await cloudinary.uploader.upload(file, {19 upload_preset: process.env.CLOUDINARY_UPLOAD_PRESET,20 });2122 console.log(public_id, secure_url);23// Save the secure_url and public id to Airtable24 const record = await table.create({25 imgId: public_id,26 url: secure_url,27 username: 'Musebecodes',28 });29 return {30 statusCode: 200,31 body: JSON.stringify(record),32 };33 } catch (err) {34 console.error(err);35 return {36 statusCode: 500,37 body: JSON.stringify({ err: 'Failed to upload image' }),38 };39 }40};
As a result of the preceding, we bring in environment variables by using the dotenv
module, which is used to configure access to Cloudinary. We also bring Airtable into the file for storage of the image parameters.
Then, we created an asynchronous handler function that listens to the event body and captures a file when an upload is initiated. This then takes the upload, and stores it in Cloudinary inside the aircloud
folder we earlier created. This is made possible by utilizing the upload preset.
After the upload to Cloudinary, We store the public_id
and secure_url
given to us into Airtable by using the table.create
method.
Image Display
To display the stored images, we first need to create a severless function that will GET
all the data stored on the Airtable database. This can be achieved by creating a creating a getImages.js
file inside the functions folder and adding the following :
1const { table } = require('./utils/airtable');23exports.handler = async (event) => {4 try {5 const records = await table.select({}).firstPage();6 const formattedRecords = records7 .map((record) => ({8 id: record.id,9 ...record.fields,10 }))11 .filter((record) => !!record.imgId);12 return {13 statusCode: 200,14 body: JSON.stringify(formattedRecords),15 };16 } catch (err) {17 return {18 statusCode: 500,19 body: JSON.stringify({ err: 'Failed to upload image' }),20 };21 }22};
From the above code, we first import table
from the utils folder in order to access airtable, fetched, and formatted the json data returned.
With all the above done, it's time to display the uploaded images. Create an imageGallery.js
component, and include the following :
1import React, { useEffect, useState } from 'react';2import { Image, Transformation } from 'cloudinary-react';34const ImageGallery = () => {5 const [images, setImages] = useState([]);6 // const local_function = 'http://localhost:58665/api/getImages';7 const deployed_function = 'https://aircloud.netlify.app/.netlify/functions/getImages';89 useEffect(() => {10 const loadImages = async () => {11 try {12 const res = await fetch(13 deployed_function14 );15 const data = await res.json();16 setImages(data);17 console.log(data);18 } catch (error) {19 console.log(error);20 }21 };22 loadImages();23 }, []);2425 return (26 <div className='image-gallery'>27 {images.length > 0 &&28 images.map((image) => (29 <div className='gallery-img' key={image.id}>30 <Image cloudName='hackit-africa' publicId={image.imgId}>31 <Transformation width='280' height='280' crop='fit' />32 </Image>33 </div>34 ))}35 </div>36 );37};3839export default ImageGallery;
By using useEffect
, we have the ability to fetch all the stored images and display them to the user by using the cloudinary-react library,.which enables us to perform image resizing and cropping to attain uniformity on all the images uploaded.
Conclusion
You have successfully built a severless application leveraging on Airtable, Cloudinary and Netlify functions. I hope you have enjoyed this tutorial. Feel free to share your thoughts.
For more information visit: