Aug 16, 2017 3:00 AM

Build an app with Flask and Oracle Mobile Cloud Service

A step-by-step guide to defining an API with Flask and deploying to Oracle’s mobile back end as a service

Thinkstock

At Synapse, we’ve been developing a “low data” app designed to crowdsource, verify, and redistribute information in the aftermath of a disaster. While our particular use case presented special challenges, like minimizing the size of requests and the number of round trips required to fetch new information, many of our goals were the same as for any large-scale mobile web application.

Along with a scalable, cloud-based back end, we wanted a stack that would allow us to be device agnostic, to plug in information from many different sources, to handle large amounts of data, and to manage requests as simply and efficiently as possible. To meet these goals, we used React Native for the client, Flask for the API, and MySQL for the database.

In between the back end and the client, there is one more important layer: Oracle Mobile Cloud Service. We used Mobile Cloud Service to simplify the management of client requests, to keep requests and responses as small as possible, and to easily track key performance indicators through Oracle’s built-in services.

In this article, we will guide you through setting up the environment, defining an API with Flask, deploying on Mobile Cloud Service, and making custom requests from the client. These instructions aren’t specific to the Synapse app, but can serve as the foundation for most any mobile web app. If you want to learn more about the Synapse app, you can read about it here.

Note that we spun up our Flask API with SQLAlchemy on an Oracle Compute VM. This RESTful framework let us have endpoints to easily query our MySQL database, and then package that information as necessary and forward it on. If you would prefer to use a different SQL database, chances are SQLAlchemy supports it. Just find the appropriate dialect here.

Create an API with Flask

We’ll run our Flask application in a virtual environment. This way, we get absolute control over our dependencies and can make sure they’re exactly the same across machines.

> virtualenv rest-api
> source rest-api/bin/activate

Now that we’re working inside the virtualenv, we’ll install our dependencies.

> pip install flask
> pip install flask-restful
> pip install sqlalchemy
> pip install MySQL-python

Flask is our framework, and flask-restful includes helpful imports for handling requests and defining endpoints. SQLAlchemy will let us connect to our MySQL server, and MySQL-python is the package that will allow SQLAlchemy to do this.

Let’s define our API in a new Python file (filename.py). We’ll want the following imports from our packages:

from flask import Flask, request
from flask_restful import Resource, Api, abort, reqparse
from sqlalchemy import *
from json import

Then we’ll create the SQLAlchemy engine. This will handle executing all of our database commands. Besides some syntax on queries, this is the only part of the Flask application that will have to change if you’re using a different database.

e = create_engine(‘mysql://user:password@localhost/DBName’)

where user and password are your MySQL Credentials, and where localhost and DBName are the address and name of the database you are connecting to.

Next we’ll add a few lines of Flask boilerplate and include a request parser to help us handle arguments. We’ll also add our first argument to our request parser.

app = Flask(__name__)
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument(‘event_id’, type=int)

Now for each API endpoint, we define a class that takes in a Resource as an argument. This class will connect to the database, construct a SQL query, execute it, and then return a response and status code.

Here is an example endpoint, where we query our database for every row in a table called event.

# (GET) Endpoint for viewing event information
class Event_Info_All(Resource):
   def get(self):
       conn = e.connect()
       command ="SELECT * FROM event"
       query = conn.execute(command)
       response = {’data’: [dict(zip(tuple (query.keys()) ,i)) for i in query.cursor]}
       return response, 200

Of course we’ll need to add each resource (endpoint) we’ve defined to our API.

api.add_resource(Event_Info_All, ‘event/info/all’)

This adds the class Event_Info_All, which we just defined, to our Flask API at the URL /event/info/all. When this is deployed, hitting the endpoint host/event/info/all (where host is the URL of our Flask API host) will return all of the events in our database.

Our last piece of boilerplate is getting our app to run. At the end of our file, we’ll add:

if __name__ == ‘__main__’:
     app.run()

This will allow whatever hosting option we use to get our API up and running. So, now we have an API with one endpoint. You can test it out by running

> python filename.py

For our production deployment, we host this API using Nginx and uWSGI. We use uWSGI with an emperor service to make sure that our Flask API is always up and running, and to log potential errors. You can investigate more hosting options in the Flask docs.

Create a back end with Oracle Mobile Cloud Service

Oracle Mobile Cloud Service (MCS) provides a web UI to manage back ends and APIs. Here, we’ll walk you through the steps for initializing your MCS layer with a Flask API. Navigate to the Mobile Cloud Service page. First, create a Mobile Backend, which is under the Applications tab. Then, define a new API by navigating to the APIs tab. This is the definition of the API that clients will hit when they connect to MCS. Implementation, which will come later, is the part where you give the API the functionality it needs to carry out its intended purpose.

You can either upload a RAML file or use a wizard interface to define each endpoint. We chose to define our API in RAML, since the wizard just writes to an editable RAML file anyway. To continue with the endpoint we defined in our Flask API, the bare-bones version of our RAML looks like this:

#%RAML 0.8
title: apiName
version: 1.0
protocols: [HTTPS]
baseUri: /mobile/custom/apiName
mediaType: application/json
/event:
 /info:
   /all:
     get:
       responses:
         200:
           body:
             application/json:
               example: |
                 {}

We start with some RAML boilerplate, where apiName is the name of your custom API. Then we define an endpoint on MCS as part of an API at /event/info/all that will be sending a JSON response. You can always edit the RAML file by clicking the edit icon in the top right corner once the API is open.

Next, while the API is open, navigate to the Implementation tab. Then click the JavaScript Scaffold button. This will download a zip file with the necessary Node.js files to write out the actual implementation of your API. You’ll write all of your code in the file apiName.js, where apiName is the name of your API.

The scaffold will have function definitions for each endpoint you’ve defined. So far, ours only has one. We’ll be using a package called Request to make our requests to our Flask API, so if you have Node.js and NPM properly installed run

> npm install —save request

to add this dependency to your package.json file. Then at the top of your Node.js file add

var request = require(‘request’);

Now we have everything we need to implement our endpoints. At each endpoint, we’re going to want to make sure the request to MCS received the proper status code, and then make another request to our back end, before we carry out any transformations on the data and send the final response back to the client. To directly forward this response, our implementation would look like this:

service.get(‘/mobile/custom/apiName/event/info/all’, function(req,res) {
   var result = {};
  if (res.statusCode == 200){
    request(‘http://flaskAPIHost/event/info/all’, function(error, response, body) {
             if (response.statusCode === 200){
                 result = body;
             } else {
          console.warn(response.statusCode);
         result = {
           ‘statusCode’: response.statusCode,
           ‘body’: body
         };
             }
             res.type(‘application/json’);
             res.status(response.statusCode).send(result);
         });
       }
   });

One of the most useful features of MCS is the console’s ability to log and track each request. Once this is deployed, if you navigate back to the mobile back end and open it, you’ll see a simple dashboard that displays status updates and logs. Any time you use console.log, a new log entry will be created. You can also use console.info(), console.warn(), or console.severe(). All logs go to the same place, but with different labels. This is very handy for testing your implementation.

When you finish your implementation, compress the folder and upload it to the same implementation page you downloaded the scaffold from. As soon as it’s uploaded, the implementation is connected to the API. The last step within MCS is to navigate back to the Mobile Backend you created and add your now functional API in its APIs tab.

Make the client connection

Now we’re ready to begin making requests to MCS from the client. We chose to build our client with React Native, which means our client code is written in JavaScript. We initially wanted to develop for Android first (due to its large share of international users) but picked React Native because it would allow us to easily deploy on iOS down the road. And because MCS has a JavaScript SDK designed for web applications, React Native was easy to include. Download the JavaScript SDK from the Mobile Cloud Service by going to Applications, and then clicking SDK Downloads from the top right of the page.

Add it to your client folder. Then, in your JavaScript files, anywhere you would like to make requests, add

var mcs = require(‘./pathToMCSSDKFolder/mcs’);

You’ll likely want to use mcs.min for production and mcs for development. You’ll also need to set up your mcs_config file with the required information from MCS, which you can find more information about here. You can configure it so that it can be imported with a require, or you can keep it in the same file where you’ll be making calls to MCS.

Before you can make any requests to MCS, you need to initialize the MCS back end and log in. We’re using basic authentication, but other authentication types are also available including oAuth and Facebook.

mcs.mobileBackendManager.platform = new mcs.BrowserPlatform();
mcs.mobileBackendManager.setConfig(mcs_config);
var mcsBackend = mcs.mobileBackendManager.getMobileBackend(“synapse”);
if(mcsBackend != null){
    mcsBackend.setAuthenticationType(“basicAuth”);
}

Then you need to authenticate on MCS via your preferred method, passing in a success and failure callback. We chose to simply use anonymous authentication, but one of the strengths of MCS is that it handles authentication for you through a variety of systems with a call that’s almost exactly the same as this one:

mcsBackend.Authorization.authenticateAnonymous(success, failure);

Now, to actually make a request to our endpoint, we’ll call mcsBackend.CustomCode.invokeCustomJSONRequest, which takes the URL of your mobile backend API, the type of request, any arguments, and callbacks for success and failure.

mcsBackend.CustomCode.invokeCustomCodeJSONRequest(API_URL, “GET”, null, requestSuccess, requestFailure);

And that’s it—the MCS SDK handles almost all of the request logic for you. You just need to deal with the responses in your success and failure callbacks.

Now you have everything you need to deploy an end-to-end application with Oracle Mobile Cloud Service, including an easy way to make requests from a native client. While we’ve laid the groundwork for grabbing data from a MySQL instance with a Flask API, you could easily swap in a different database. Using Mobile Cloud Service’s connector APIs, you can draw on data from many other sources as well.

New Tech Forum provides a venue to explore and discuss emerging enterprise technology in unprecedented depth and breadth. The selection is subjective, based on our pick of the technologies we believe to be important and of greatest interest to InfoWorld readers. InfoWorld does not accept marketing collateral for publication and reserves the right to edit all contributed content. Send all inquiries to newtechforum@infoworld.com.