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
    }

    View Slide

  2. The
    Basics

    View Slide

  3. What is REST?
    3
    “It's about transferring
    representations of the state... of
    resources”
    Steve Klabnik

    View Slide

  4. What’s a resource?
    - Sources of information (documents
    or services)
    - Any interaction with a RESTful API is
    an interaction with a resource

    View Slide

  5. RESTful APIs
    5
    A set of URIs that map entities
    to endpoints.
    resource
    resource
    resource
    URI
    URI
    URI

    View Slide

  6. Challenges

    View Slide

  7. Typical scenario
    7
    Web application

    View Slide

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

    View Slide

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

    View Slide

  10. API design

    View Slide

  11. Designing APIs
    11

    View Slide

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

    View Slide

  13. Actions
    13
    The way to perform an action in a
    RESTful API is to use HTTP verbs
    (request methods)

    View Slide

  14. Actions
    14
    HTTP methods:
    • GET
    • HEAD
    • OPTIONS
    • POST
    • PUT
    • PATCH
    • DELETE

    View Slide

  15. Actions
    15
    Don’t include the verb in the resource
    !
    /presentations/delete/1

    View Slide

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

    View Slide

  17. Representation
    17
    /articles/
    !
    - It doesn't include the format
    - A given resource can have different
    representations (XML, JSON, …)

    View Slide

  18. Representation
    18
    !
    -Don’t do this
    GET /presentations/
    format=json

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  22. Request headers:
    -Accept-Language
    -User-Agent
    -Authentication
    -...
    Other Headers
    22

    View Slide

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

    View Slide

  24. Hypermedia

    View Slide

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

    View Slide

  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…

    View Slide

  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"
    },
    ]

    View Slide

  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

    View Slide

  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 ☺

    View Slide

  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

    View Slide

  31. Versioning

    View Slide

  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

    View Slide

  33. !
    -Don’t do this
    !
    !
    -Resources should only include
    nouns!
    33
    Versioning
    GET /presentations/v1/

    View Slide

  34. 34
    Versioning
    !
    -Use the Accept Header
    GET /presentations/
    Accept: version=1

    View Slide

  35. Documentation

    View Slide

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

    View Slide

  37. Read the docs
    ... you need to read their manual.
    37

    View Slide

  38. !
    Wouldn’t it be nicer to get some hands-
    on?
    Idea
    38
    DELETE
    POST PUT
    GET

    View Slide

  39. Swagger
    https://developers.helloreverb.com/swagger/

    View Slide

  40. Swagger
    40
    - Document your API with style
    - No more dull .doc files or ugly pages
    - Give your users a playground

    View Slide

  41. Benefits
    41
    API discovery:

    View Slide

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

    View Slide

  43. Benefits
    43
    Swagger has a family:

    View Slide

  44. Requires work, though
    44

    View Slide

  45. Django unRESTed

    View Slide

  46. What makes a decent
    API Framework?

    View Slide

  47. • Publishing of metadata
    along with querysets
    • proper HTTP response
    handling
    • posting of data with
    validation
    • serialization
    • API discovery
    • throttling
    • permissions
    • authentication
    • pagination
    47

    View Slide

  48. • Really good test coverage of their code
    • Decent performance
    • Documentation
    • An active community to advance and
    support the framework
    48

    View Slide

  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()})

    View Slide

  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})

    View Slide

  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\d+)/$',
    DetailView.as_view(model=Book)),
    url(r'^books/create/$',
    CreateView.as_view(model=Book)),
    url(r'^books/(?P\d+)/delete/$',
    DeleteView.as_view(model=Book)),
    )

    View Slide

  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) ),
    )

    View Slide

  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)), )

    View Slide

  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)), )

    View Slide

  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())),
    )

    View Slide

  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)), )

    View Slide

  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

    View Slide

  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

    View Slide

  59. @OST
    59
    - API discovery
    - Documentation
    - Versioning
    - Browsing

    View Slide

  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

    View Slide

  61. GET /questions/
    61

    View Slide

  62. View Slide