GET /path/to/foo?type=bar HTTP/1.1 Host: api.example.com
url = scheme://host:port/path?query_string
(version simplifée)
méthode = GET, POST, PUT, DELETE, ...
=> qu'en est-il des bonnes pratiques ?
var request = $.ajax({
url: "https://api.navitia.io/v1/" + path,
method: "get",
dataType: "json"
});
var point = {
lat: 48.874192
lng: 2.353241
};
var path = "/coverage/fr-idf/coords/" + point.lng + ";" + point.lat + "/stop_schedules";
var request = $.ajax({
url: "https://api.navitia.io/v1/" + path,
method: "get",
dataType: "json"
});
var point = {
lat: 48.874192
lng: 2.353241
};
var path = "/coverage/fr-idf/coords/" + point.lng + ";" + point.lat + "/stop_schedules";
var request = $.ajax({
url: "https://api.navitia.io/v1/" + path,
method: "get",
dataType: "json",
xhrFields: {
withCredentials: true
}
})
request.done(function (data, textStatus, jqXHR) { /* do something */ });
request.fail(function (jqXHR, textStatus, error) { /* do something */ });
var authKey = "XXXXXXX";
var lang = "fr";
var point = {
lat: 48.874192
lng: 2.353241
};
Data
Building query: concatenate
var authKey = "XXXXXXX";
var lang = "fr";
var point = {
lat: 48.874192
lng: 2.353241
};
var query =
"key=" + authKey +
"&lang=" + lang +
"&position=" + point.lat + "," + point.lng;
Building service url
var authKey = "XXXXXXX";
var lang = "fr";
var point = {
lat: 48.874192
lng: 2.353241
};
var query =
"key=" + authKey +
"&lang=" + lang +
"&position=" + point.lat + "," + point.lng;
var path = "/position";
$.ajax({
url: "http://api.what3words.com" + path + (query ? "?" + query : ""),
method: "get",
dataType: "json"
})
var request = queryWords({
lat: 48.874192
lng: 2.353241
});
Il est toujours possible de faire un refactoring
On cache la complexité avec une fonction queryWords
function queryWords(point) {
var authKey = "XXXXXXX";
var lang = "fr";
var path = "/position";
var query =
"key=" + authKey +
"&lang=" + lang +
"&position=" + point.lat + "," + point.lng;
return $.ajax({
url: "http://api.what3words.com" + path + (query ? "?" + query : ""),
method: "get",
dataType: "json"
});
}
var request = queryWords({
lat: 48.874192
lng: 2.353241
});
var point = {
lat: 48.874192
lng: 2.353241
};
var path = "/coverage/fr-idf/coords/" + point.lng + ";" + point.lat + "/stop_schedules";
var request = $.ajax({
url: "https://api.navitia.io/v1/" + path,
method: "get",
dataType: "json",
});
var point = {
lat: 48.874192
lng: 2.353241
};
var path = "/coverage/fr-idf/coords/" + point.lng + ";" + point.lat + "/stop_schedules";
var request = $.ajax({
url: "https://api.navitia.io/v1/" + path,
method: "get",
dataType: "json"
});
var username = "username";
var password = "very-secret-password";
// generate base64 encoded string "Basic XXXXXX"
var authorization = btoa(username + ":" + password);
var point = {
lat: 48.874192
lng: 2.353241
};
var path = "/coverage/fr-idf/coords/" + point.lng + ";" + point.lat + "/stop_schedules";
var request = $.ajax({
url: "https://api.navitia.io/v1/" + path,
method: "get",
dataType: "json"
});
var username = "username";
var password = "very-secret-password";
// generate base64 encoded string "Basic XXXXXX"
var authorization = btoa(username + ":" + password);
var point = {
lat: 48.874192
lng: 2.353241
};
var path = "/coverage/fr-idf/coords/" + point.lng + ";" + point.lat + "/stop_schedules";
var request = $.ajax({
url: "https://api.navitia.io/v1/" + path,
method: "get",
dataType: "json",
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", authorization);
}
});
Maintien d'anciennes versions d'API: v0, v1, ...
http://recette.domaine.tld https://preprod.domaine.tld https://prod.domaine.tld/v1 https://prod.domaine.tld/v2
Ou sous forme de variables ?
$.ajax({
url: "http://recette.domaine.tld/foo"
});
var apiUrl = "http://recette.domaine.tld";
$.ajax({
url: apiUrl + "/foo"
});
Hardcodées ?
Comment gérer des paths paramétrés?
// PATH /coverage/fr-idf/coords/<resource>/stop_schedules
var path = "/coverage/fr-idf/coords/_resource_/stop_schedules";
Où sont stockés les paths ?
hardcodées ?
variables ?
// PATH /coverage/fr-idf/coords/<resource>/stop_schedules
var path = "/coverage/fr-idf/coords/_resource_/stop_schedules";
$.ajax({
url: "https://api.navitia.io/v1" + path
});
// PATH /coverage/fr-idf/coords/<resource>/stop_schedules
var path = "/coverage/fr-idf/coords/_resource_/stop_schedules";
var point = {
lat: 48.874192
lng: 2.353241
};
path.replace("_resource_", point.lng + ";" + point.lat);
$.ajax({
url: "https://api.navitia.io/v1" + path
});
Comment générer ce genre de query ?
// ?from=2.354255;48.869424&to=2.267189;48.8811491&datetime=20150624T190000
// &datetime_represents=arrival&max_duration_to_pt=300&max_nb_tranfers=2
// ?from=2.354255;48.869424&to=2.267189;48.8811491&datetime=20150624T190000
// &datetime_represents=arrival&max_duration_to_pt=300&max_nb_tranfers=2
Par une concaténation de chaînes de caractères ?
var query = "?";
query += "from=" + from.lng + ";" + from.lat;
query += "&to=" + to.lng + ";" + to.lat;
query += "&datetime=" + moment.format(time);
query += "&datetime_represents=arrival";
query += "&max_duration_to_pt=" + duration;
query += "&max_nb_tranfers=" + transfers;
var data = {
from: "2.354255;48.869424",
to: "2.267189;48.8811491",
datetime: "20150624T190000",
datetime_represents: "arrival",
max_duration_to_pt: 300,
max_nb_tranfers: 2
};
Hashmap pour les cas simples ?
var data = {
from: "2.354255;48.869424",
to: "2.267189;48.8811491",
datetime: "20150624T190000",
datetime_represents: "arrival",
max_duration_to_pt: 300,
max_nb_tranfers: 2
};
var queryArgs = [];
for (var param in data) {
queryArgs.push(param + "=" + data[param]);
}
var query = "?" + queryArs.join("&");
var data = {
from: "2.354255;48.869424",
to: "2.267189;48.8811491",
datetime: "20150624T190000",
datetime_represents: "arrival",
max_duration_to_pt: 300,
max_nb_tranfers: 2
};
var queryArgs = [];
for (var param in data) {
queryArgs.push(param + "=" + data[param]);
}
var query = "?" + queryArs.join("&");
// ?from=2.354255;48.869424&to=2.267189;48.8811491&datetime=20150624T190000
// &datetime_represents=arrival&max_duration_to_pt=300&max_nb_tranfers=2
Building query
var from = {lng: 2.354255, lat: 48.869424};
var to = {lng: 2.267189, lat: 48.8811491}
var query = journeyQuery(from, to);
var journeyQuery = function (from, to) {
var data = {
datetime: "20150624T190000",
datetime_represents: "arrival",
max_duration_to_pt: 300,
max_nb_tranfers: 2
};
var queryArgs = [];
for (var param in data) {
queryArgs.push(param + "=" + data[param]);
}
queryArgs.push("from=" + from.lng + ";" + from.lat);
queryArgs.push("from=" + to.lng + ";" + to.lat);
return "?" + queryArs.join("&");
}
Fonction pour les cas plus complexes ?
var from = {lng: 2.354255, lat: 48.869424};
var to = {lng: 2.267189, lat: 48.8811491}
var query = journeyQuery(from, to);
var from = {lng: 2.354255, lat: 48.869424};
var to = {lng: 2.267189, lat: 48.8811491}
var query = journeyQuery(from, to);
var journeyQuery = function (from, to) {
var data = {
datetime: "20150624T190000",
datetime_represents: "arrival",
max_duration_to_pt: 300,
max_nb_tranfers: 2
};
var queryArgs = [];
for (var param in data) {
queryArgs.push(param + "=" + data[param]);
}
queryArgs.push("from=" + from.lng + ";" + from.lat);
queryArgs.push("from=" + to.lng + ";" + to.lat);
return "?" + queryArs.join("&");
}
// ?from=2.354255;48.869424&to=2.267189;48.8811491&datetime=20150624T190000
// &datetime_represents=arrival&max_duration_to_pt=300&max_nb_tranfers=2
$.get()
$.post()
$.ajax({method: "put"})
$.ajax({method: "delete"})
$.ajax({method: "head"})
request.get()
request.post()
request.put()
request.del()
request.head()
fetch(url, {method: "get"})
https://github.com/github/fetch
fetch(url, {method: "post"})
fetch(url, {method: "put"})
fetch(url, {method: "delete"})
fetch(url, {method: "head"})
// https://github.com/what3words/w3w-javascript-wrapper/blob/master/what3words.js
var what3words = new function (language) {
this.API_KEY = 'YOURAPIKEY'; // Change to your what3words API key
this.language = language || 'en'; // Change to your default language
// --
this.postRequest = function (url, data, callback) {
data.key = this.API_KEY;
data.lang = this.language;
$.post('http://api.what3words.com/' + url, data, callback, 'JSON');
};
};
Authentification par un paramètre dans la query
https://api.what3words.com/position?key=YOURAPIKEY&lang=en&...
ex. Basic Auth
var username = "username";
var password = "very-secret-password";
// generate base64 encoded string "Basic XXXXXX"
var authorization = btoa(username + ":" + password);
$.ajax({
// ...
});
var username = "username";
var password = "very-secret-password";
// generate base64 encoded string "Basic XXXXXX"
var authorization = btoa(username + ":" + password);
$.ajax({
// ...
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", authorization);
}
});
// PATH /coverage/fr-idf/coords/<resource>/stop_schedules
var path = "/coverage/fr-idf/coords/_resource_/stop_schedules";
var point = {
lat: 48.874192
lng: 2.353241
};
path.replace("_resource_", point.lng + ";" + point.lat);
var request = $.ajax({
url: "https://api.navitia.io/v1" + path
});
request.done(function() {
// do something
});
En 3 points ...
Au commencement, une configuration
{
"baseUrl": "https://api.navitia.io/v1",
"services": {
"stopSchedules": {
path: "/coverage/fr-idf/coords/:lng;:lat/stop_schedules",
method: "GET"
}
},
options: {
type: "form"
},
headers: {
"Authorization": "Basic ZWRkNWYxODQt...."
}
}
fichier JSON
Ensuite, la mise en place
var api = new superapi.default.Api();
var api = new superapi.default.Api();
api.configure("navitia", navitiaConf);
Fire!
api.navitia.stopSchedule({
params: point,
query: {
distance: distance
}
});
api.navitia.stopSchedule();
Quelques explications
{
"baseUrl": "",
"services": {},
"headers": {},
"options": {}
}
{
"baseUrl": "https://api.navitia.io/v1",
"services": {
"stopSchedules": {
"path": "/coverage/fr-idf/coords/:lng;:lat/stop_schedules",
"method": "GET",
"options": {
"type": "json"
}
}
},
"headers": {
"Authorization": "Basic ZWRkNWYxODQt...."
}
}
API endpoint
{
"baseUrl": "https://api.navitia.io/v1",
"services": {
"stopSchedules": {
"path": "/coverage/fr-idf/coords/:lng;:lat/stop_schedules",
"method": "GET",
"options": {
"type": "json"
}
}
},
"headers": {
"Authorization": "Basic ZWRkNWYxODQt...."
}
}
Services
services est un hash map de services configurés
{
"baseUrl": "https://api.navitia.io/v1",
"services": {
"stopSchedules": {
"path": "/coverage/fr-idf/coords/:lng;:lat/stop_schedules",
"method": "GET",
"options": {
"type": "json"
}
}
},
"headers": {
"Authorization": "Basic ZWRkNWYxODQt...."
}
}
Service id
{
"baseUrl": "https://api.navitia.io/v1",
"services": {
"stopSchedules": {
"path": "/coverage/fr-idf/coords/:lng;:lat/stop_schedules",
"method": "GET",
"options": {
"type": "json"
}
}
},
"headers": {
"Authorization": "Basic ZWRkNWYxODQt...."
}
}
Service path
{
"baseUrl": "https://api.navitia.io/v1",
"services": {
"stopSchedules": {
"path": "/coverage/fr-idf/coords/:lng;:lat/stop_schedules",
"method": "GET",
"options": {
"type": "json"
}
}
},
"headers": {
"Authorization": "Basic ZWRkNWYxODQt...."
}
}
Service method
{
"baseUrl": "https://api.navitia.io/v1",
"services": {
"stopSchedules": {
"path": "/coverage/fr-idf/coords/:lng;:lat/stop_schedules",
"method": "GET",
"options": {
"type": "json"
}
}
},
"headers": {
"Authorization": "Basic ZWRkNWYxODQt...."
}
}
Service options
{
"baseUrl": "https://api.navitia.io/v1",
"services": {
"stopSchedules": {
"path": "/coverage/fr-idf/coords/:lng;:lat/stop_schedules",
"method": "GET",
"options": {
"type": "json"
}
}
},
"headers": {
"Authorization": "Basic ZWRkNWYxODQt...."
}
}
global headers
{
"baseUrl": "https://api.navitia.io/v1",
"services": {
"stopSchedules": {
"path": "/coverage/fr-idf/coords/:lng;:lat/stop_schedules",
"method": "GET"
}
},
"options": {
"type": "json",
"accept": "json"
},
"headers": {
"Authorization": "Basic ZWRkNWYxODQt...."
}
}
global options
Also supported : global options
var api = new superapi.default.Api();
api.withSuperagent(superagent);
api.configure("navitia", navitiaConf);
1. setup API wrapper
3. add configuration
// controllers/api.js
define([
"superapi",
"superagent",
"json!config/api/navitia.json"
],
function (superapi, superagent, navitiaConf) {
"use strict";
var api = new superapi.default.Api();
api.withSuperagent(superagent);
api.configure("navitia", navitiaConf);
return api;
});
2. add XHR agent
return function(point, distance) {
return api.navitia.stopSchedule({
params: point,
query: {
distance: distance
}
});
};
XHR call
Exemple avec utilisation d'un wrapper
// api/stopSchedule.js
define([
"controllers/api"
],
function (api) {
"use strict";
return function(point, distance) {
return api.navitia.stopSchedule({
params: point,
query: {
distance: distance
}
});
};
});
var point = {
lat: 48.874192,
lng: 2.353241
};
stopSchedule(point, 500).then(/* */);
calling our XHR wrapper
// somewhere in your code
define([
"api/stopSchedule"
], function (stopSchedule) {
// ...
var point = {
lat: 48.874192,
lng: 2.353241
};
stopSchedule(point, 500).then(/* */);
// ...
});
En quelques lignes
// somewhere in your code
define([
"api/stopSchedule"
], function (stopSchedule) {
// ...
var point = {
lat: 48.874192,
lng: 2.353241
};
stopSchedule(point, 500)
.then(function (res) {
// success
})
.catch(function (error) {
// error!
});
// ...
});
returning a Promise
sous forme d'une promesse
// api/stopSchedule.js
define([
"controllers/api"
],
function (api) {
"use strict";
return function(point, distance) {
return api.navitia.stopSchedule({
params: point,
query: {
distance: distance
}
});
};
});
// somewhere in your code
define([
"api/stopSchedule"
], function (stopSchedule) {
// ...
var point = {
lat: 48.874192,
lng: 2.353241
};
var callback = function (err, res) {
// do something
};
stopSchedule(point, 500, callback);
// ...
});
passing a callback
ou d'un bon vieux callback
// api/stopSchedule.js
define([
"controllers/api"
],
function (api) {
"use strict";
return function(point, distance, cb) {
return api.navitia.stopSchedule({
params: point,
query: {
distance: distance
},
callback: cb
});
};
});
il suffit d'ajouter autant de configurations que nécessaires
var api = new superapi.default.Api();
api.withSuperagent(superagent);
// controllers/api.js
define([
"superapi",
"superagent",
"json!config/api/navitia.json",
"json!config/api/uber.json"
],
function (superapi, superagent, navitia, uber) {
"use strict";
var api = new superapi.default.Api();
api.withSuperagent(superagent);
api.configure("navitia", navitia);
api.configure("uber", uber);
return api;
});
var api = new superapi.default.Api();
api.withSuperagent(superagent);
api.configure("navitia", navitia);
var api = new superapi.default.Api();
api.withSuperagent(superagent);
api.configure("navitia", navitia);
api.configure("uber", uber);
Accéder à la requête
api.navitia.stopSchedule({
params: point,
query: {
distance: distance
}
})
api.navitia.stopSchedule({
params: point,
query: {
distance: distance
},
edit: function (req) {
// before xhr call
}
})
https://github.com/stephanebachelier/backbone-superapi-sync
define([
'backbone',
'backbone.superapiSync',
'controllers/api',
],
function (Backbone, backboneSuperapiSync, superapi) {
'use strict';
return Backbone.Collection.extend({
sync: function (method, model, options) {
return backboneSuperapiSync(superapi).call(this, method, model, options);
}
});
});
define([
'backbone',
'controllers/api'
],
function (Backbone, api) {
'use strict';
return Backbone.Collection.extend({
url: function () {
var query = {
q: this.q,
offset: this.offset,
limit: this.size,
'filters[type]': 'entities',
};
return api.buildUrl('search', undefined, query);
}
});
});
/!\ code 0.10.5
Some middlewares