Using Swagger Client With App Engine

February 24, 2018
gae appengine swagger

Feels like Swagger is getting more traction every day as more and more companies are adopting it to spec out their APIs. That’s actually good! Swagger lets you get stuff done quickly both as a producer - build an API - and as a consumer - generate a Swagger client to consume that API. Easy, awesome and pretty straight forward! Except when you want to use that generated Swagger client in the App Engine to access that Swagger API.

App Engine Standard is a sandboxed environment. You can’t always do stuff the way you are used to. You have to play by their rules. But no need to worry! It’s actually pretty easy to solve once you know where to look.

Generate a Swagger client

In order to access a Swagger API you have to generate a Swagger client. Well, you actually don’t have to if you don’t want but it helps. Just trust me on this one. Lets generate a Petstore Swagger client in Python. NOTE: you might need to install Swagger code generator first.

$ swagger-codegen generate -l python \
    -i http://petstore.swagger.io/v2/swagger.json \
    -o petstore

The command will generate a Swagger client in Python and put it into petstore directory.

Use Swagger client in the app

Lets take a simple Flask app for this example. To save us time I wrote a post on how to create a very simple Flask app on App Engine standard environment.

Swagger client is using urllib3 as default http client library. There are two ways you can use urllib3 in Appengine Standard: patch it to use App Engine’s URLFetch or enable sockets. Which one to use depends on your situation and needs. URLFetch is more cost-effective as long as your usage is within the limitations for free app. Sockets are only available for paid apps and are in beta at the time of writing.

You can read more about various options here.

To be able to use urllib3 with URLFetch in AppEngine together with generated Swagger client you have to monkeypatch it.

import swagger_client
from urllib3.contrib.appengine import AppEngineManager

# create swagger client
client = swagger_client.PetApi()
# patch urllib3 for App Engine to use URLFetch
client.api_client.rest_client.pool_manager = AppEngineManager()

You can skip monkeypatching if you choose to go with sockets. Just add the following lines to the app.yaml.

env_variables:
  GAE_USE_SOCKETS_HTTPLIB : 'true'

Here is the whole application file in its glory.

import json
from flask import Flask, request, jsonify, abort
from urllib3.contrib.appengine import AppEngineManager

# import our swagger client and friends
import swagger_client
from swagger_client.rest import ApiException

app = Flask(__name__)
app.debug = True

# create swagger client
client = swagger_client.PetApi()
# patch urllib3 for App Engine to use URLFetch
client.api_client.rest_client.pool_manager = AppEngineManager()

def jsonify_api_exception(error):
    body = json.loads(error.body)
    err = {'status_code': error.status, 'message': body['message']}
    response = jsonify(err)
    response.status_code = err['status_code']
    return response

@app.route('/')
def index():
    return 'hello from google app engine with swagger client'

@app.route('/pets.byId')
def pet_get():
    # get pet id from query string
    pet_id = request.args.get('id')

    try:
        resp = client.get_pet_by_id(pet_id)
        return jsonify(resp.to_dict())
    except ApiException as e:
        # naive exception handling
        print "Error calling PetApi.get_pet_by_id: %s\n" % e.status
        return jsonify_api_exception(e)

Lets test it!

Lets start the local development server and try to fetch a few pets by id. Note that Swagger petstore is a public API and therefore there are no guarantees that the ids in examples below work.

# in one terminal window
$ dev_appserver.py --log_level debug .

# in another terminal window
$ curl localhost:8080/pets.byId?id=55
$ curl localhost:8080/pets.byId?id=87356

Conclusion

App Engine Standard is a restricted sanboxed environment. In there we have to play by their rules. Sometimes it’s just not that clear what those rules are and where to find them. But once we know them it’s pretty easy to find a way to adjust our code to work with them.

Like what you just read? Sign up for the newsletter!

Read more