As an API evangelist, I’m frequently put in a position where I need to provide sample code for my client developers, the folks who consume the API I’m charged with evangelizing. Our sample code is mostly in python at this point, because it’s the easiest language to understand if you don’t know python (if that makes any sense). These samples are really simple, demonstrating how to use the signing library for the language and then make a few HTTP calls to the actual APIs. However, I’ve run into several cases where customers are simply unwilling or unable to look at a “foreign” programming language to suss out what’s happening.
I am a firm believer that any programmer can read another programming language and understand what the underlying logic is, especially for fairly simple examples. The polyglot repository shows the same functionality (a very simple API engine) in 5 different languages. I’m hoping that providing this kind of apples to apples comparison will make it easier for developers to feel comfortable and confident looking at code in different languages. This is great for your career, for lowering your frustration level, and for just generally increasing your ability to code creatively and efficiently.
If you are on a platform which doesn’t appreciate interpreted languages, you can use the Docker Image (fastest way to get started). The new native clients for docker in Mac and Windows are fantastic and I highly recommend them. Just start up the docker engine and grab the repository with:
docker run -i -t -p 8080:8080 synedra/polyglot
9. Kirsten Hunter
@synedra
http://www.princesspolymath.com
RAML 0.8
title: Fortune Cookie
version: V1
baseUri: http://www.fortunecookieserver.com/api/v1
/quotes:
displayName: Quotes
get:
description: Get a list of quotes
queryParameters:
random:
description: Retrieve a random quote
required: false
default: false
type: boolean
responses:
200:
body:
application/json:
example: |
[
{"content":"That which does not kill us makes us stronger.",
"author":"Sun Yi",
"id":1
}
]
10. Kirsten Hunter
@synedra
http://www.princesspolymath.com
RAML 0.8
title: Fortune Cookie
version: V1
baseUri: http://www.fortunecookieserver.com/api/v1
/quotes:
displayName: Quotes
get:
description: Get a list of quotes
queryParameters:
random:
description: Retrieve a random quote
required: false
default: false
type: boolean
responses:
200:
body:
application/json:
example: |
[
{"content":"That which does not kill us makes us stronger.",
"author":"Sun Yi",
"id":1
}
]
11. Kirsten Hunter
@synedra
http://www.princesspolymath.com
RAML 0.8
title: Fortune Cookie
version: V1
baseUri: http://www.fortunecookieserver.com/api/v1
/quotes:
displayName: Quotes
get:
description: Get a list of quotes
queryParameters:
random:
description: Retrieve a random quote
required: false
default: false
type: boolean
responses:
200:
body:
application/json:
example: |
[
{"content":"That which does not kill us makes us stronger.",
"author":"Sun Yi",
"id":1
}
]
18. Kirsten Hunter
@synedra
http://www.princesspolymath.com
Perl - Mongo Setup
use Dancer;
use Dancer::Plugin::CRUD;
use MongoDB;
my $client = MongoDB->connect();
my $db = $client->get_database('test');
my $quotes = $db->get_collection('quotes');
my $json = JSON->new->allow_nonref;
set content_type => 'application/json';
get '/' => sub{
return {message => "Hello from Perl and Dancer"};
};
set public => path(dirname(__FILE__), '..', 'static');
get "/demo/?" => sub {
send_file '/index.html'
};
get '/api/quotes' => sub {
my $response = $quotes->find()->sort({'index' => -1})->limit(10);
my @results = ();
while(my $quote = $response->next) {
push (@results,
{"content" => $quote->{'content'},
"index" => $quote->{'index'},
"author" => $quote->{'author'}
}
);
}
if (! scalar (@results)) {
status 404;
return;
}
return @results;
};
19. Kirsten Hunter
@synedra
http://www.princesspolymath.com
Perl - Static Files
use Dancer;
use Dancer::Plugin::CRUD;
use MongoDB;
my $client = MongoDB->connect();
my $db = $client->get_database('test');
my $quotes = $db->get_collection('quotes');
my $json = JSON->new->allow_nonref;
set content_type => 'application/json';
get '/' => sub{
return {message => "Hello from Perl and Dancer"};
};
set public => path(dirname(__FILE__), '..', 'static');
get "/demo/?" => sub {
send_file '/index.html'
};
get '/api/quotes' => sub {
my $response = $quotes->find()->sort({'index' => -1})->limit(10);
my @results = ();
while(my $quote = $response->next) {
push (@results,
{"content" => $quote->{'content'},
"index" => $quote->{'index'},
"author" => $quote->{'author'}
}
);
}
if (! scalar (@results)) {
status 404;
return;
}
return @results;
};
20. Kirsten Hunter
@synedra
http://www.princesspolymath.com
Perl - Routes
get '/api/quotes' => sub {
my $response = $quotes->find()->sort({'index' => -1})->limit(10);
my @results = ();
while(my $quote = $response->next) {
push (@results,
{"content" => $quote->{'content'},
"index" => $quote->{'index'},
"author" => $quote->{'author'}
}
);
}
if (! scalar (@results)) {
status 404;
return;
}
return @results;
};
21. Kirsten Hunter
@synedra
http://www.princesspolymath.com
Python - Mongo Setup
resp = Response(dumps(quotes, default=default),
mimetype='application/json')
return resp
def delete(self, quote_id):
print "Quote id is %s" % quote_id
try:
mongo.db.quotes.remove({
'index': int(quote_id)
})
except Exception as ve:
print ve
abort(400, str(ve))
return '', 204
def put(self, quote_id):
args = parser.parse_args()
if not (args['content'] or args['author']):
return 'Missing data', 400
existing_quote = mongo.db.quotes.find_one({"index": int(quote_id)})
args['content'] = args['content'] if args['content'] else existing_quote["content"]
args['author'] = args['author'] if args['author'] else existing_quote["author"]
try:
mongo.db.quotes.update({
'index': quote_id
},{
'$set': {
'content': args['content'],
'author': args['author']
}
}, upsert=False)
except Exception as ve:
print ve
abort(400, str(ve))
return 201
# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class QuoteList(Resource):
def get(self):
quotes = mongo.db.quotes.find().sort("index", -1).limit(10)
resp = Response(dumps(quotes, default=default),
mimetype='application/json')
return resp
22. Kirsten Hunter
@synedra
http://www.princesspolymath.com
Python - Static
return resp
…
@app.route('/')
def hello_world():
return 'Hello from Flask!'
@app.route('/demo')
def serve_page():
return send_from_directory(STATIC_ROOT, "index.html")
abort(400, str(ve))
return '', 204
def put(self, quote_id):
args = parser.parse_args()
if not (args['content'] or args['author']):
return 'Missing data', 400
existing_quote = mongo.db.quotes.find_one({"index": int(quote_id)})
args['content'] = args['content'] if args['content'] else existing_quote["content"]
args['author'] = args['author'] if args['author'] else existing_quote["author"]
try:
mongo.db.quotes.update({
'index': quote_id
},{
'$set': {
'content': args['content'],
'author': args['author']
}
}, upsert=False)
except Exception as ve:
print ve
abort(400, str(ve))
return 201
# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class QuoteList(Resource):
def get(self):
quotes = mongo.db.quotes.find().sort("index", -1).limit(10)
resp = Response(dumps(quotes, default=default),
mimetype='application/json')
return resp
def post(self):
args = parser.parse_args()
23. Kirsten Hunter
@synedra
http://www.princesspolymath.com
Python - Route
# shows a list of all quotes, and lets you POST to add new quotes
class QuoteList(Resource):
def get(self):
quotes = mongo.db.quotes.find().sort("index", -1).limit(10)
resp = Response(dumps(quotes, default=default),
mimetype='application/json')
return resp
def post(self):
args = parser.parse_args()
quotes = mongo.db.quotes.find().sort("index", -1).limit(1)
print quotes[0]
args["index"] = int(quotes[0]["index"]) + 1
print args
try:
mongo.db.quotes.insert(args)
except Error as ve:
abort(400, str(ve))
return 201
api.add_resource(QuoteList, '/api/quotes')
api.add_resource(Quote, '/api/quotes/<quote_id>')
24. Kirsten Hunter
@synedra
http://www.princesspolymath.com
Ruby - Mongo Setup
end
get '/' do
content_type :html
"Hello World from Sinatra"
end
before do
content_type 'application/json'
end
namespace "/api" do
# list all
get '/quotes' do
Quote.all.desc(:index).limit(10).to_json
end
get '/quotes/random' do
newnumber = Quote.count
random_num = rand(newnumber)
quote = Quote.find_by(index: random_num.to_i)
return status 404 if quote.nil?
quote.to_json
end
# view one
get '/quotes/:index' do
quote = Quote.find_by(index: params[:index].to_i)
return status 404 if quote.nil?
quote.to_json
end
# create
post '/quotes' do
newnumber = Quote.count + 1
@json = JSON.parse(request.body.read)
quote = Quote.new(
content: @json['content'],
author: @json['author'],
index: newnumber)
quote.save
newnumber.to_json
end
# update
put '/quotes/:index' do
25. Kirsten Hunter
@synedra
http://www.princesspolymath.com
end
get '/' do
content_type :html
"Hello World from Sinatra"
end
before do
content_type 'application/json'
end
namespace "/api" do
# list all
get '/quotes' do
Quote.all.desc(:index).limit(10).to_json
end
get '/quotes/random' do
newnumber = Quote.count
random_num = rand(newnumber)
quote = Quote.find_by(index: random_num.to_i)
return status 404 if quote.nil?
quote.to_json
end
# view one
get '/quotes/:index' do
quote = Quote.find_by(index: params[:index].to_i)
return status 404 if quote.nil?
quote.to_json
end
# create
post '/quotes' do
newnumber = Quote.count + 1
@json = JSON.parse(request.body.read)
quote = Quote.new(
content: @json['content'],
author: @json['author'],
index: newnumber)
quote.save
newnumber.to_json
end
# update
put '/quotes/:index' do
Ruby - Static
26. Kirsten Hunter
@synedra
http://www.princesspolymath.com
namespace "/api" do
# list all
get '/quotes' do
Quote.all.desc(:index).limit(10).to_json
end
get '/quotes/random' do
newnumber = Quote.count
random_num = rand(newnumber)
quote = Quote.find_by(index: random_num.to_i)
return status 404 if quote.nil?
quote.to_json
end
# view one
get '/quotes/:index' do
quote = Quote.find_by(index: params[:index].to_i)
return status 404 if quote.nil?
quote.to_json
end
# create
post '/quotes' do
newnumber = Quote.count + 1
@json = JSON.parse(request.body.read)
quote = Quote.new(
content: @json['content'],
author: @json['author'],
index: newnumber)
quote.save
newnumber.to_json
end
# update
put '/quotes/:index' do
@json = JSON.parse(request.body.read)
quote = Quote.find_by(index: params[:index].to_i)
return status 404 if quote.nil?
quote.update(
content: @json['content'],
author: @json['author']
)
quote.save
params[:index].to_json
end
Ruby - Route
27. Kirsten Hunter
@synedra
http://www.princesspolymath.com
app.use('/api', router);
// REST API
router.route('/quotes/random')
.get(function(req, res, next) {
var random = Math.floor(Math.random() * quotecount);
Quote.findOne({"index":random},
function (err, result) {
if (err) {
console.log(err);
res.redirect('/quotes/random');
}
res.send(result);
})
});
router.route('/quotes')
.get(function(req, res, next) {
var result = Quote.find().sort({'index': -1}).limit(10);
result.exec(function(err, quotes) {
res.send(quotes);
});
})
.post(function(req, res, next) {
if(!req.body.hasOwnProperty('content')) {
res.statusCode = 400;
return res.send('Error 400: Post syntax incorrect.');
}
quotecount = quotecount+1;
var newQuote;
if (req.body.hasOwnProperty('author')) {
newQuote = new Quote({'content': req.body.content, 'author': req.body.author, 'index': quotecount});
} else {
newQuote = new Quote({'content': req.body.content, 'index':quotecount});
}
newQuote.save(function (err, newQuote) {
if (err) return console.error(err);
res.json(quotecount);
});;
});
router.route('/quotes/:index')
.get(function(req, res, next) {
Quote.findOne({"index":req.params.index},
function (err, result) {
res.send(result);
Node - Mongo Setup