Upgrade to Pro — share decks privately, control downloads, hide ads and more …

All About API's (v2)

All About API's (v2)

Presentation about best practices on RESTful APIs, in general and within the Django ecosystem.
Created by Diogo Laginha & Ricardo Vitorino:

diogo laginha

March 10, 2015
Tweet

Other Decks in Technology

Transcript

  1. All about APIs > GET /presentations/apis > Accept: application/vnd.apple.keynote version=2.0

    ! < Date: Tue, 10 Mar 2015, 12:30:00 GMT < Content: ! { authors: [ Diogo Laginha, Ricardo Vitorino ], awesome: true }
  2. The Basics

  3. What is REST? 3 “It's about transferring representations of the

    state... of resources” Steve Klabnik
  4. What’s a resource? - Sources of information (documents or services)

    - Any interaction with a RESTful API is an interaction with a resource
  5. RESTful APIs 5 A set of URIs that map entities

    to endpoints. resource resource resource URI URI URI
  6. Challenges

  7. Typical scenario 7 Web application

  8. The problem 8 Don’t give your users sh_t work!

  9. Challenges 9 - API discovery - Resource browsing - Resource

    versioning - Documentation
  10. API design

  11. Designing APIs 11

  12. Resources 12 - Must be nouns! - Examples: ! /articles/

    /authors/ /books/ /presentations/
  13. Actions 13 The way to perform an action in a

    RESTful API is to use HTTP verbs (request methods)
  14. Actions 14 HTTP methods: • GET • HEAD • OPTIONS

    • POST • PUT • PATCH • DELETE
  15. Actions 15 Don’t include the verb in the resource !

    /presentations/delete/1
  16. Actions 16 Use the DELETE method instead ! /presentations/1

  17. Representation 17 /articles/ ! - It doesn't include the format

    - A given resource can have different representations (XML, JSON, …)
  18. Representation 18 ! -Don’t do this GET /presentations/ format=json

  19. ! -Don’t do this either GET /presentations.json Representation 19

  20. ! -Use the Accept Header Representation GET /presentations/ Accept: application/json

    20
  21. 21 Resources are mapped to URLs Actions are mapped to

    verbs The rest goes in the headers Resources are mapped to URIs Actions are mapped to verbs The rest goes in the headers
  22. Request headers: -Accept-Language -User-Agent -Authentication -... Other Headers 22

  23. Response headers: -Link -Content-Type -Status -... Other Headers 23

  24. Hypermedia

  25. Hyper-whaa?! 25 Ignore this, we’re talking about hyperlinks here

  26. 26 You want to say “Happy Birthday” on some social

    network. Option A Go directly to friend’s page and post. Option B Go to Homepage > search friend > post. What if…
  27. 27 Then… Why shouldn’t you do the same for APIs?

    > GET /apis < Content: ! apis: [ { path: "/presentations/", description: "IPNLis thematic presentations" }, { path: "/pizzas/", description: "Pizza for the presentations" }, ]
  28. 28 That’s hypermedia! Actually, it’s called HATEOAS. Hypermedia as the

    Engine of Application State - Single endpoint for the resource - All actions discovered by inspecting resource
  29. 29 HATEOAS Get a certain keynote: presentation: { id: 1337,

    name: "All About APIs", date: "2013-03-12", authors: { path: "/presentations/1337/authors/", description: "Keynote’s authors", } } We know what to do next ☺
  30. 30 HATEOAS Get all the keynotes: presentations: [ {...}, ...

    {...} ], Meta: { total: 100, paginated_objects: 25, next_page: "/presentations?offset=25" } }25 presentations Where to get some more
  31. Versioning

  32. - Version a resource representation, not the API -Add a

    new version when the changes in the resource representation are non-backward compatible First of all 32
  33. ! -Don’t do this ! ! -Resources should only include

    nouns! 33 Versioning GET /presentations/v1/
  34. 34 Versioning ! -Use the Accept Header GET /presentations/ Accept:

    version=1
  35. Documentation

  36. Use case If you want your application to use some

    awesome APIs... 36
  37. Read the docs ... you need to read their manual.

    37
  38. ! Wouldn’t it be nicer to get some hands- on?

    Idea 38 DELETE POST PUT GET
  39. Swagger https://developers.helloreverb.com/swagger/

  40. Swagger 40 - Document your API with style - No

    more dull .doc files or ugly pages - Give your users a playground
  41. Benefits 41 API discovery:

  42. Benefits 42 Check the result schema with actual data:

  43. Benefits 43 Swagger has a family:

  44. Requires work, though 44

  45. Django unRESTed

  46. What makes a decent API Framework?

  47. • Publishing of metadata along with querysets • proper HTTP

    response handling • posting of data with validation • serialization • API discovery • throttling • permissions • authentication • pagination 47
  48. • Really good test coverage of their code • Decent

    performance • Documentation • An active community to advance and support the framework 48
  49. Function-based views 49 def foobar(request): template_name = 'foobar.html' form_class =

    MyForm if request.method == 'POST': form = form_class(request.POST) if form.is_valid(): #TODO return render(request, template_name, {'form': form}) elif request.method == 'GET': return render(request, template_name, {'form': form_class()})
  50. Class-based views 50 class FooBar(View): template_name = 'foobar.html' form_class =

    MyForm def get(self, request): form = self.form_class() return render(request, self.template_name, {'form': form) def post(self, request): form = self.form_class(request.POST) if form.is_valid(): #TODO return render(request, self.template_name, {'form': form})
  51. Class-based Generic Views 51 class FooBar(FormView): template_name = 'foobar.html' form_class

    = MyForm ! def form_valid(self, form): #TODO ! urlpatterns += patterns('', url(r'^books/$', ListView.as_view(model=Book)), url(r'^books/(?P<pk>\d+)/$', DetailView.as_view(model=Book)), url(r'^books/create/$', CreateView.as_view(model=Book)), url(r'^books/(?P<pk>\d+)/delete/$', DeleteView.as_view(model=Book)), )
  52. 52 Django URL Framework class FooController(ActionController): def index(self, request): #

    /foo/ return {} def edit(self, request, id = None): # /foo/edit/ # /foo/edit/(\d+)/ return {} def remove(self, request, id): # /foo/remove/(\d+)/ return {} ! django_url_framework.site.autodiscover(settings.INSTALLED_APPS) urlpatterns = patterns('', (r'^', include(django_url_framework.site.urls) ), )
  53. 53 Tastypie class FooResource(ModelResource): bar = fields.ForeignKey(BarResource, 'bar') class Meta:

    queryset = Foo.objects.all() fields = ['name', 'bar'] allowed_methods = ['get', 'post'] serializer = Serializer(formats=['json', 'jsonp']) filtering = {"name": ('exact', 'startswith',),} ! def get_list(self, request, **kwargs): pass ! api = Api(api_name='v1') api.register(FooResource()) ! urlpatterns = patterns('', url(r'^', include(api.urls)), )
  54. 54 Conduit class FooResource(ModelResource): class Meta(ModelResource.Meta): model = Foo allowed_methods

    = ['get'] allowed_filters = ['name__icontains', 'bar__name'] class Fields: bar = ForeignKeyField( resource_cls=BarResource, attribute='bar') def get_list(self, request, **kwargs): pass api = Api() api.register(FooResource()) ! urlpatterns = patterns('', url(r'^', include(api.urls)), )
  55. 55 Restless class FooResource(DjangoResource): preparer = FieldsPreparer(fields={ 'id': 'id', 'title':

    'title', 'author': 'user.username', }) ! def list(self): pass def detail(self, pk): pass urlpatterns = patterns('', url(r'/foo/', include(FooResource.urls())), )
  56. 56 Django Rest Framework 3 class FooSerializer(serializers.ModelSerializer): class Meta: model

    = Foo ! class FooViewSet(viewsets.ViewSet): queryset = Foo.objects.all() serializer_class = FooSerializer ! def list(self, request): pass ! def retrieve(self, request, pk=None): pass router = routers.DefaultRouter() router.register(r'foo', FooViewSet) ! urlpatterns = patterns('', url(r'^', include(router.urls)), )
  57. 57 more about Django Rest Framework 3 class FooViewSet(viewsets.ModelViewSet): queryset

    = Foo.objects.all() serializer_class = FooSerializer ! @detail_route() def edit(self, request, pk=None): # /foo/(\d+)/edit pass @list_route() def bar(self, request): # /foo/bar pass
  58. 58 YARD 2 class FooResource(resources.Resource): model = Foo fields =

    { 'title': fields.Unicode, 'author': { 'name': fields.Unicode }, } ! class Parameters: year = forms.IntegerParam(min=1970, max=2012) title = forms.CharParam(alias='title__icontains') author = forms.CharParam(alias='author__id') __logic__ = year, title & (author|year) ! def index(self, request, params): return self.model.objects.filter(**params) ! def show(self, request, pk): pass
  59. @OST 59 - API discovery - Documentation - Versioning -

    Browsing
  60. References - http://www.apievangelist.com/ - http://publish.luisrei.com/articles/ rest.html - http://blog.steveklabnik.com - http://www.designinghypermediaapis.com

    - https://twitter.com/hypermediaapis - https://developers.helloreverb.com/ swagger/ 60
  61. GET /questions/ 61

  62. None