How to solve “No API definition provided” error for Flask-RESTPlus app on Cloud Foundry

When you create an API endpoint that generates a QRCode with Flask-RESTPlus, you may want to deploy it onto Cloud Foundry.

However, when I first tried to do so, I was greeted with a "No API definition provided" when I accessed the Swagger Api portal page.

qrcodeapp showing No API definition provided

So why is there a "No API definition provided" error for Flask-RESTPlus app on Cloud Foundry?

In case you are facing such a problem, this post discuss how I had managed to solve the "No API definition provided" error for my Flask-RESTPlus app on Cloud Foundry.

Having an idea of how the Flask-RESTPlus app was deployed on Cloud Foundry

Before diving into the solution, it is helpful to know how the Flask-RESTPlus app was deployed onto Cloud Foundry.

In addition to the python file, the QRCode app was pushed to a Cloud Foundry environment with the following manifest file:

---
applications:
- name: QRCodeApp
  memory: 256MB
  disk_quota: 256MB
  random-route: true
  buildpack: python_buildpack
  command: python run_app.py
  instances: 1

Given that, Cloud Foundry will make the Flask-RESTPlus app accessible via https://qrcodeapp.somcfurl.com.

Whenever HTTP request is made to that endpoint, the reverse proxy server will proxy HTTP requests to the web application that was started with the following command:

python run_app.py

Finding out why there is a "No API definition provided" error with Chrome Inspector

Whenever there is a problem with a web page that you are accessing, the Chrome Inspector is a tool that can tell you more about the problem.

Given that, I started the Chrome Inspector and found the following error message:

Mixed Content: The page at 'https://qrcodeapp.somcfurl.com/api/' was loaded over HTTPS, but requested an insecure resource 'http://qrcodeapp.somcfurl.com/api/swagger.json'. This request has been blocked; the content must be served over HTTPS.
(anonymous) @ index.js:1

So what does this error message mean?

Since Cloud Foundry had served our portal page over HTTPS, our Chrome browser expects that subsequent requests should be made via HTTPS.

However, our command had started the Flask-RESTPlus app serving HTTP requests over HTTP.

Therefore, there was a call made to retrieve a resource (http://qrcodeapp.somcfurl.com/api/swagger.json) via HTTP in the HTML document that was returned as a HTTP response to the browser.

Since that call was block, the bootstrapping JavaScript code from Swagger was unable to replace the "No API definition provided" text with the elements of the API portal.

Instructing the Flask-RESTPlus Api object to generate urls with the HTTPS scheme instead of HTTP

Since there is a mismatch of scheme used, we have to configure the Flask-RESTPlus Api object to generate urls with HTTPS instead of HTTP.

In addition to that, we want to be able to test our Flask-RESTPlus app in our development machine when we access the API portal via HTTP.

Given that, we need a way for our Flask-RESTPlus app to know when to generate urls with HTTPS.

As I had mentioned previously, we can get the environment variables supplied to our Cloud Foundry application with Python 3 Flask. Given that, we can get a list of environment variables that are supplied to our Flask-RESTPlus app on Cloud Foundry. We can then check the existence of one of the environment variables to determine whether our app is on Cloud Foundry.

For example, the following code checks for the VCAP_SERVICES environment variable before instructing the Flask-RESTPlus Api object to generate urls with HTTPS instead of HTTP:

from flask import url_for
from flask_restplus import Api

if os.environ.get('VCAP_SERVICES'):
    @property
    def specs_url(self):
        return url_for(self.endpoint('specs'), _external=True, _scheme='https')

    Api.specs_url = specs_url

Putting the above code fragment with our QRCode app, we yield the following code:

from flask import Flask, Blueprint, request, send_file, url_for
from flask_restplus import Api, Namespace, Resource, fields
from io import BytesIO
 
import os, qrcode
 
# Create Flask app
app = Flask(__name__)
 
# Associate Api with Blueprint
api_blueprint = Blueprint('API', __name__)

if os.environ.get('VCAP_SERVICES'):
    @property
    def specs_url(self):
        return url_for(self.endpoint('specs'), _external=True, _scheme='https')

    Api.specs_url = specs_url

api = Api(api_blueprint,
    title='Api for QR code app',
    version='1.0',
    description='This is an API for QR code app',
    # All API metadatas
)
 
# Create namespace for containing Qr Code related operations
qrcode_namespace = Namespace('QrCode', description='Qr code related operations')
# Specify uri of qrcode namespace as /qrcode
api.add_namespace(qrcode_namespace, path='/qrcode')
# Specify uri of api blueprint as /api
app.register_blueprint(api_blueprint, url_prefix='/api')
 
# Define input model
qrcode_creation_input = qrcode_namespace.model('QRCode creation Input', {
    'value': fields.String(required=True, description='The value that is supposed to be encoded into qrcode'),
})
 
 
# Define API endpoint for creating Qr Code image
@qrcode_namespace.route('/')
@qrcode_namespace.doc('Creates a QRCode image based on a string value.')
class QrCodeRoot(Resource):
 
    @qrcode_namespace.expect(qrcode_creation_input)
    @qrcode_namespace.produces(['image/png'])
    @qrcode_namespace.response(200, description='Return QR Code png image file.')
    @qrcode_namespace.response(400, description='Invalid input provided.')
    def post(self):
 
        # Get value to encode into QR Code
        json_input = request.get_json()
        value_to_turn_into_qrcode = json_input['value']
 
        # Create qr code image and return it as HTTP response
        pil_img = qrcode.make(value_to_turn_into_qrcode)
        img_io = BytesIO()
        pil_img.save(img_io, 'PNG')
        img_io.seek(0)
        return send_file(img_io, mimetype='image/png')
 
 
if __name__ == '__main__':
    port = int(os.getenv("PORT", "5678"))
    app.run(host='0.0.0.0', port=port)

Once I had deployed the code changes to Cloud Foundry, I am able to access the API portal without the "No API definition provided" error.

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.