Easily deploy serverless React app to IBM Cloud using Cloudant, COS, API and Functions
Published: 2020-12-15 22:50:45

In this tutorial, we will create a React app for a small business that organises meetups in a several cities and deploy it to IBM Cloud. Each event has a list of attendees and practical informations including the address that we will display using the popular JS library [Leaflet](https://leafletjs.com/)! You'll find the code for this tutorial in my [gihub](https://github.com/DjerB/meeto) repo.

The app must enable the user to view the upcoming events and their details and to sign up for them. Here is what the app architecture will look like by the end of this tutorial:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1rnm8nlsguuckt6ecuq9.png)

The client will request (1) the site statically served and stored in a **Cloud Object Storage** bucket. The app will call functions (4) for interacting (5) with a **Cloudant** database (that we will populate) through public API endpoints (2 and 3).

#**Set up your IBM Cloud account for free**

For anyone getting started with Cloud Infrastructure, IBM Cloud is an amazing service to work with. First, suscribe! It’s free and good news you won’t have to enter any credit card details, you even get 200$ for free if you sign up for a paid account! Go to [https://cloud.ibm.com/registration](https://cloud.ibm.com/registration) and follow the steps below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/l55dqgjsybsex0qpu935.png)

#**Set up your serverless app in IBM Cloud**

So far so good, now that our IBM Cloud is set up, we can dive into the interesting part. First, let's create the database for storing the events and all of their attributes. As you can imagine, an event has its own host, location, date, programme, etc. The most important here is the attendees attribute that will be represented as a list containing all the people that signed up for the event.

To create a database, we go to Catalog and type Cloudant in the search bar. Then click on Cloudant service. You automatically land on the creation page as follows:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qamrgh3ex3p8tp9vixop.png)

All we have to do to get started is to fill in a name for our instance and the location. **Remember**, choose one region and stick to it or you may end up with unwanted errors when connection your Cloudant instance to your Functions. We of course are working with our free Lite plan for this tutorial. Then, press **Create**. IBM Cloud will set up everything for you for the next step.

Go to your instance, if you cannot find it, just type its name in the searchbar at the top of the dashboard. You should see something like this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/glgwdpvvxqpci4l4q1mm.png)

Click on **Launch Dashboard**, it will open your instance dashboard in another tab. The great thing with that view is that you can visualise, monitor and secure your database with great ease. Now, we need to store our events somewhere. To do so, click on **Create Database** on top of the page. Choose a name such as *meeto* for example, select **non-partitioned** and create your database. In my case, to speed up things up, I simply populated the database with 11 documents representing events. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pl2ta24meddqgllnw3pv.png)

Great! We now have a database ready. Let's add functions to actually manipulate and read those data. To do so, we are going to create some **actions**: **actions** are simple functions specifically designed to deal with IBM Cloud services. This is an stunningly easy way to deal with data in serverless apps.
Let's go in **Functions**: as before, just type the name in **Catalog** and click on the service icon. Once you get on the presentation page, just click on **Create function** to get started and you should see this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/69nfr5306yjx6thxvb58.png)

You might wonder which card you should click on. No worries, actually the basic block of those options are the **actions**: this is simply a function that is called in a runtime environment of your choice (in our case, nodejs). **Sequences** are chains of successive **actions** and will be very useful to format and return the data as we want them. Moreover, **sequences** are powerful in the sense that they can connect our **actions** to other IBM Cloud services instances.
Let's go back to our app: we want the user to view the list of meetups and click to show his interest for one of them. Therefore, we need 2 things: a way to get all of the entries and a way to update them individually.

Let's start with the former and first create an **action** called set-read-input and create a package (just by giving it a name):

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/aesrsfv6lept8nxdmd6x.png)

Replace the default code with: 
```javascript
function main(params) {
	return {
        params: {
            include_docs: true
        }
    };
}
```
This just indicates that we want our future API to return the data when a request is made.

Now, we want to put this **action** into a sequence. So, click on **Enclosing Sequences** on the left and add a new sequence (e.g read-events-sequence). Again, choose the same package you just created (you will quickly find out that everything must be consistent).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ii4ggrtrp4glcx59yyqr.png)

Great, our sequence is initialised! No we want to link it to our Cloudant instance. Click on the recently created sequence and click on **Add**. Now, choose **Use Public** and **Cloudant**. In the select dropdown, choose **list-documents** to tell the service what we want to do with the data. Finally, create a binding names **bindings-for-meeto**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zhxu0pt4mv3jq8mi0hes.png)

Now, the same way, add a new **action** (e.g format-events) that will handle the data and send them to the client later.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jxoxfag8xzoquhco3ymz.png)

And replace the code with:

```javascript
function main(params) {
  return {
    entries: params.rows.map((row) => { return {
      id: row.doc._id,
      rev: row.doc._rev,
      image: row.doc.image,
      title: row.doc.title,
      topic: row.doc.topic,
      location: row.doc.location,
      description: row.doc.description,
      date: row.doc.date,
      attendees: row.doc.attendees,
      host: row.doc.host,
      currency: row.doc.currency,
      duration: row.doc.duration,
      price: row.doc.price,
      programme: row.doc.programme,
      gps: row.doc.gps
    }})
  };
}
```
As you can see, all the attributes are returned. This is the place to do some preprocessing if you want to or even some sanity checks!

Our sequence is now complete! Let's focus on the update part now:

Exactly as before, start by creating an **action** (e.g prepare-event-for-save) and replace the code with:

```javascript
function main(params) {
    if (!params.params.id) {
        return Promise.reject({ error: 'Missing event id.' });
    }
    if (!params.params.attendees) {
        return Promise.reject({ error: 'Missing attendees.'});
    }

    return {
        doc: {
            _id: params.params.id,
            _rev: params.params.rev,
            attendees: params.params.attendees,
            image: params.params.image,
            title: params.params.title,
            topic: params.params.topic,
            location: params.params.location,
            description: params.params.description,
            date: params.params.date,
            attendees: params.params.attendees,
            host: params.params.host,
            currency: params.params.currency,
            duration: params.params.duration,
            price: params.params.price,
            programme: params.params.programme,
            gps: params.params.gps
        }
    };
}
```
You surely got this part: the **params** are actually coming as part of the body from a HTTP request and is used to update the document in Cloudant. Note that the **rev** and **id** are necessary here to tell Cloudant which document is concerned. Again, enclose your newly created **action** into a new sequence. Then, reproduce the steps above to create a new connection between the sequence and Cloudant.

The final **Functions** dashboard should look like this for your 2 sequences:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/gnq8le03vmdv83lbp2ab.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/to7tof4c3qzobanj8gny.png)

Final touch for the serverless part of this tutorial: the API! We do have functions but they're basically useless if we can't call them. Let's remedy that!

For each sequence, click on **Web Action** and check the box as follows:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/uc2lhcefnm9hclu1vxf5.png)

Now, head into the API Management tab in **Functions** and start creating an API. Simply give it a name and a base path (e.g meeto and /meeto): so every time you call it, you start your API urls with "/meeto/<endpoint>". Now, create an operation: choose GET for the first sequence as we just ask for data and POST for the second as we are pushing data to the database. The pathname is yours to choose but you should make it consistent with its purpose so anyone can understand it. **Careful now**, choose the package name and the corresponding action. You now understand how everything is connected: those endpoints you just created will connect to your functions!

Great, you are set up! Copy the API url, you'll need it in a moment!

#**Setup your React client**

Finally, we've come to the final part: the client. Since this is not a tutorial about React, we are not going to focus on the implementation details. So simply clone my [gihub](https://github.com/DjerB/meeto) repo and change the API Url in `src/api/events.js`. 
You'll notice how simple our client code is for dealing with APIs! 
```javascript
export const getEvents = () => axios.get(apiUrl + "/events");

export const updateEvent = (data) => axios.post(apiUrl + "/attend", { params: data, headers: {"Access-Control-Allow-Origin": "*"} });
``` 
Of course, please feel free to look into the React code to see how data are collected and rendered dynamically. 

#**Deploy your app**

If you made it to that point, congrats! This is the final chapter of our tutorial. You'll see how easy it is to serve your static website using the Cloud Object Storage Service.
 
First go to **COS** (you should know the trick by now) and create a new instance. Now, click on **Create a bucket** and choose to customise it. Needless to say, choose an unique name for your bucket and select **Resiliency Regional** (feel free to take a look at the other options, we just don't need them for the purposes of this tutorial). **Important**: in **Location**, select the one you've been using it for Cloudant. Scroll down to the **Static website hosting** part and add a new rule (this is the fun part): keep the routing rules as they are and type `index.html`. This `index.html` will be the entry point of our app when ready for deployment. Make sure that **Public access is on** or you won't be able to reach your app!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hqqro2xbtruvu18ariz0.png)

Now, go back into the repo and simply run the commands `npm install` and then `npm run build`: this will install all the dependencies the project is built on and create a production build for our app. Once this is done, simply drag and drop the newly created `build` folder into your bucket. Go into the **Configuration** tab and look for the static website hosting endpoints: you can now access to your app online! Congratulations!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xbb3kafa76tehvp8fmzo.png)

The grid of events is dynamically rendered once the data have been retrieved from our Cloudant database through our Functions API! Click on **GO!** on one of the events to access its details page:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/y1ro98kd1wedk4amueth.png)

All the informations about the event are shown, in particular the list of attendees. Note how the location (`gps` attribute in **Cloudant**) is beautifully displayed in a map using the great Leaflet wrapper for React, [`react-leaflet`](https://react-leaflet.js.org/)! Try to add your name in the form below the map: your name will be directly added in the database and rendered in the list! 

#**Conclusion**

This was just an introduction about some of the most widely used services IBM Cloud offers. There are a lot more of them, including Kubernetes, Machine Learning services (the famous IBM Watson for instance!), ... and you should definitely check them out!

A next post is en route to introduce AppID: this is a great service for authenticating users on websites but also securing pretty much every resource in IBM Cloud. I cannot stress enough that anyone in possession of your API URL can mess up your database. We'll see in that post how you can restrict your resources to authorised users using AppID.

I do hope you enjoyed this tutorial and found it very useful for your upcoming projects!