Agile APIs

Matthew Weier O'Phinney / @mwop
apigility.org / @Apigility

You need an API

Many teams, many languages

Many languages

Communicate with partners and clients


Mobile applications


along the way

(APIs are Hard)

The devil is in the details

Devil in the details

1: Format: Old school RPC?


Or Embrace the new hawtness: REST?


2: Error reporting

  • HTTP Status?
  • How are details reported?

3: HTTP method negotiation

HTTP negotiation

4: Content negotiation

Content negotiation

5: Validation

Validation failed!

6: Authentication


7: Authorization


8: Versioning

Version all the things!

9: Discovery and documentation

Document all the things!

Stumbling block: Hypermedia what?


In short:
APIs are hard!

(You may get lost
along the way!)

Get a Map!



We choose
so you don't have to

1: Format: JSON

Specifically, Hypermedia Application Language (HAL)


    "_links": {
        "self": { "href": "/session/1" }
    "session_id": 1,
    "title": "Apigility: Agile APIs",
    "_embedded": {
        "speaker": {
            "_links": { "self": { "href": "/speaker/1" } }
            "speaker_id": 1,
            "name": "Matthew Weier O'Phinney"

2: Error Reporting

Specifically Problem Details for HTTP APIs (API Problem)


    "type": "/api/problems/forbidden",
    "title": "Forbidden",
    "detail": "Your API key is missing or invalid.",
    "status": 403,
    "authenticationUrl": "/api/oauth"

3: HTTP Method Negotiation

POST /session HTTP/1.1
Content-Type: application/xml


405 Method Not Allowed
Allow: GET


OPTIONS /session HTTP/1.1
Content-Type: application/xml

200 OK
Allow: GET

4: Content Negotiation: Accept

GET /session HTTP/1.1
Accept: application/xml

406 Not acceptable
Content-Type: application/problem+json

    "type": "/api/problems/content",
    "title": "Not acceptable",
    "detail": "This API can deliver application/vnd.zend-con.v1+json, application/hal+json, or application/json only.",
    "status": 406

4: Content Negotiation: Content-Type

POST /session HTTP/1.1
Content-Type: application/xml


415 Unsupported Media Type
Content-Type: application/problem+json

    "type": "/api/problems/content",
    "title": "Unsupported Media Type",
    "detail": "This API can accept application/vnd.zend-con.v1+json, application/hal+json, or application/json only.",
    "status": 415

5: Validation

PATCH /session/1 HTTP/1.1
Content-Type: application/json

{ "title": {"foo":"bar"} }

422 Unprocessable Entity
Content-Type: application/problem+json

    "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html",
    "title": "Unprocessable Entity",
    "detail": "Failed validation",
    "status": 422,
    "validation_messages": {
        "title": "Invalid title; must be a non-empty string"

6: Authentication

  • HTTP Basic and Digest (for internal APIs)
  • OAuth2 (for public APIs)
  • Event-driven, to accommodate anything else
  • Return a problem response early if invalid credentials are provided


GET /session/1 HTTP/1.1
Authorization: Basic foobar
Accept: application/json

401 Unauthorized
Content-Type: application/problem+json

    "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html",
    "title": "Unauthorized",
    "detail": "Unauthorized",
    "status": 401

7: Authorization

  • Public by default
  • Restrict by service and/or specific HTTP methods
  • Return a problem response early if the identified user does not have authorization


GET /session/1 HTTP/1.1
Accept: application/json

403 Forbidden
Content-Type: application/problem+json

    "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html",
    "title": "Forbidden",
    "detail": "Forbidden",
    "status": 403

8: Versioning by Default

  • URL-based* versioning: /v1/session, /v2/session
  • Mediatype versioning:
    Accept: application/vnd.status.v2+json
  • Code is versioned by namespace.
* For the lazy

9: Document all the things!

  • Why services exist
  • Why methods are exposed
  • What request and response payloads should contain

Export documentation

  • Export captured documentation
  • Export other captured settings, such as Content Negotiation, allowed HTTP methods, authorization requirements, etc.
  • Export to the format of your choice

Bonus: Hyperlinking: Pagination

  • Invoked automatically by returning Zend\Paginator\Paginator.

    _links: {
        self: { href: "/api/session?page=3" },
        first: { href: "/api/session" },
        last: { href: "/api/session?page=14" },
        prev: { href: "/api/session?page=2" },
        next: { href: "/api/session?page=4" }

and to make things easier …

Use what you want

No framework to learn

Write your own code, however you want …

… but ZF2 is under the hood.

Made of many ZF2 modules!

  • zf-api-problem
  • zf-apigility
  • zf-apigility-admin
  • zf-configuration
  • zf-content-negotiation
  • zf-content-validation
  • zf-development-mode
  • zf-hal
  • zf-mvc-auth
  • zf-oauth2
  • zf-rest
  • zf-rpc
  • zf-versioning
  • and more!

Extend via...

  • event listeners
  • services

To sum up...

  • APIs provide many details to lose yourself in
  • Get a map! Try out tools like Apigility!

Get involved!

Thank You!

Matthew Weier O'Phinney