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. What’s a resource? - Sources of information (documents or services)

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

    to endpoints. resource resource resource URI URI URI
  4. Resources 12 - Must be nouns! - Examples: ! /articles/

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

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

    • POST • PUT • PATCH • DELETE
  7. Representation 17 /articles/ ! - It doesn't include the format

    - A given resource can have different representations (XML, JSON, …)
  8. 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
  9. 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…
  10. 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" }, ]
  11. 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
  12. 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 ☺
  13. 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
  14. - 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
  15. ! -Don’t do this ! ! -Resources should only include

    nouns! 33 Versioning GET /presentations/v1/
  16. Swagger 40 - Document your API with style - No

    more dull .doc files or ugly pages - Give your users a playground
  17. • Publishing of metadata along with querysets • proper HTTP

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

    performance • Documentation • An active community to advance and support the framework 48
  19. 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()})
  20. 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})
  21. 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)), )
  22. 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) ), )
  23. 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)), )
  24. 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)), )
  25. 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())), )
  26. 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)), )
  27. 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
  28. 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