OpenAPI (formerly known as Swagger) specification is a language agnostic set of specification used for documenting REST APIs. Swagger team joined SmartBear in 2015 and they open-sourced the Swagger specification which is now called OpenAPI specification.
Documenting APIs is one of the most important steps of API development. This way of documenting not only helps fellow developers but also consumers of the API. Also, Swagger enabled APIs could also be quickly tested without the need for going through detailed documentation.
There are several Restful API frameworks that come with OpenAPI documentation support in Python. In this article, similar to my prior blog, let’s walk through an example with Flask-RestPlus which is an extension to the Flask framework.
Project Setup
- Create a project folder -
mkdir quotes-rest-api
- Setup and activate virtual environment - Virtual Environment Setup
- Install below packages in project’s virtual environment:
pip install flask-restplus
# Flask-restplus needs below version of Werkzeug
pip install Werkzeug==0.16.0
Application Setup
Create a file called app.py
in the project folder and add necessary imports, application setup along with model definition code like shown below:
# necessary Imports
from flask import Flask, jsonify, request
from flask_restplus import Api, Resource, fields
from werkzeug.middleware.proxy_fix import ProxyFix
# application & documentation setup
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
api = Api(app, version='1.0', title='Swagger enabled Quotes API',description='A simple Quotes API')
# namespace definition
ns = api.namespace('quotes', description='Quote CRUD operations')
# model definition - would be automatically displayed as part of API documentation
quote = api.model('quote', {
'id': fields.Integer(readonly=True, description="Quote's unique identifier"),
'quote_desc': fields.String(required=True, description="Quote"),
'quote_type': fields.String(required=True, description="quote's type"),
'author': fields.String(required=True, description="quote's author")
})
Quotes CRUD Operations Setup
Add below DAO (Data Access Object) class to the app.py
file. This class is going to be used by API endpoints to perform necessary CRUD Operations. Also, note that quotes are maintained in application's memory as a Python list object.
# Quote class containing CRUD methods
class QuoteDAO(object):
def __init__(self):
self.counter = 0
self.quotes = []
def get(self, id):
for q in self.quotes:
if q['id'] == id:
return q
api.abort(404, "Quote {} doesn't exist".format(id))
def create(self, data):
q = data
q['id'] = self.counter = self.counter + 1
self.quotes.append(q)
return q
def update(self, id, data):
q = self.get(id)
q.update(data)
return q
def delete(self, id):
q = self.get(id)
self.quotes.remove(q)
# Initialize DAO & create sample quotes
DAO = QuoteDAO()
DAO.create({'quote_desc':'It always seem impossible until it is done.', 'quote_type': 'Motivation', 'author': 'Nelson Mandela'})
DAO.create({'quote_desc':'With the new day comes new strength and new thoughts.', 'quote_type': 'Motivation', 'author': 'Eleanor Roosevelt'})
DAO.create({'quote_desc':'The secret of getting ahead is getting started.', 'quote_type': 'Motivation', 'author': 'Mark Twain'})
API endpoints for GET and POST operations
Below class defines GET and POST operations for the quotes maintenance. Below code could also be added to the end of same app.py
file.
# API Endpoints for listing and creating new quotes
@ns.route('/')
class QuoteList(Resource):
'''Shows a list of all quotes, and lets you POST to add new quotes'''
@ns.doc('list_quotes')
@ns.marshal_list_with(quote)
def get(self):
'''List all quotess'''
return DAO.quotes
@ns.doc('create_quote')
@ns.expect(quote)
@ns.marshal_with(quote, code=201)
def post(self):
'''Create a new quote'''
return DAO.create(api.payload), 201
API endpoints for other operations
Below class is used to define rest of the CRUD operations. Note the use of common namespace decorators for quote id, response and route information.
# API Endpoints for rest of CRUD operations
@ns.route('/<int:id>')
@ns.response(404, 'Quote not found')
@ns.param('id', 'The quote identifier')
class Quote(Resource):
'''Show a single quote item and lets you delete them'''
@ns.doc('get_quote')
@ns.marshal_with(quote)
def get(self, id):
'''Fetch a quote given its identifier'''
return DAO.get(id)
@ns.doc('delete_quote')
@ns.response(204, 'Quote deleted')
def delete(self, id):
'''Delete a quote given its identifier'''
DAO.delete(id)
return '', 204
@ns.expect(quote)
@ns.marshal_with(quote)
def put(self, id):
'''Update a quote given its identifier'''
return DAO.update(id, api.payload)
How to test the API?
Add below code to the end of the app.py file, so we could start the application using python app.py
command.
if __name__ == '__main__':
app.run(debug=True)
API endpoints could be tested in the browser itself. Try it Out
button (shown below) could be used to GET list of quotes.
Top comments (1)
Hello,
Flask Restplus is dead and not maintained anymore. Maybe you should use Flask restx instead as it is the official fork. (If you really want to stick to Flask but know that some major competitors are also out there)
However promoting a framework providing OpenAPI 2 definition only when OpenAPI 3 is out seems a bit odd ;-)