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

APIs & ExpressionEngine Building Apps that Connect

Patrick Pohler
October 15, 2013
110

APIs & ExpressionEngine Building Apps that Connect

Learn how to build an API for you add-on seamlessly into ExpressionEngine.

Patrick Pohler

October 15, 2013
Tweet

Transcript

  1. Application Programming Interface Saturday, November 9, 13 In most procedural

    languages, an API specifies a set of functions or routines that accomplish a specific task or are allowed to interact with a specific software component. Allows developers to write code interfaces with functionality from another platform
  2. Saturday, November 9, 13 APIs are EVERYWHERE, if you’re a

    developer you’ve interacted with an API All of the companies on this slide have been successful because they have great apis that other developers wanted to use.
  3. Why build an API? • Increases the adoption of your

    software • Provides future functionality • Can lead to better code • Makes you a better developer Saturday, November 9, 13
  4. Good APIs Are: • Understandable, Consistent • Secure • Versioned,

    backwards compatible • Graceful when handling errors Saturday, November 9, 13 Understandable & consistent naming is very important in your api, if its hard to use then no matter your features developers wont use it Security is important, you want to make sure only valid allowed programs have access and user data is protected Versioning is also critical, you have to plan for new features without breaking older devs who have built their software on older code Be graceful in handling errors, send back clear and understandable error codes to help the developer using your api diagnose problems
  5. The Client Saturday, November 9, 13 Let me tell you

    about a recent project of mine who needed an api to an ee addon. They're an elite team of soliders who were wrongly accused of a crime they didnt commit, now they work as soliders of fortune. If you have a problem, if no one else can help, and if you can find them...
  6. A-Team Mission Tracker Saturday, November 9, 13 Anecka had previously

    developed a module to allow the A-Team to track their missions Now they need a mobile application to integrate with the module, they have a mobile dev lined up, but they need an API exposed to display their missions in JSON.
  7. Template “API” Saturday, November 9, 13 As a quick prototype

    i decided to rely on the power of ees templating engine by using it to create a simple json feed. Creating a simple JSON feed in EE can be done using EE’s templating methods. A good way to start is to create a new template group. In this case we’ll use “api” then the number 1 to denote the version. This will help us maintain backwards compatibility in the future
  8. Template “API” Saturday, November 9, 13 Next we create a

    template to denote the resource we want to return back, in this case a list of missions. We name the template with a .json extension so EE will return the content as application/json instead of plain-text
  9. Template “API” Saturday, November 9, 13 We already have a

    tag pair to output the missions {exp:mission_tracker:missions}, all we have to do is format it to JSON
  10. Template “API” Tag pair JSON Array Var Saturday, November 9,

    13 JSON is simply name-value pairs enclosed by curly brackets. Since we have multiple missions, we have square blocks representing an array of multiple objects We use our addon tag pair (the same tagpair we developed previously) to insert the values for the json name-value pair
  11. Template “API” Saturday, November 9, 13 We also included a

    conditional “last_item” so we know when to add a separating comma between the missions. On the last item we want to avoid adding a comma as it would not validate as proper JSON
  12. a-team.com/api1/ missions.json Saturday, November 9, 13 Now that we navigate

    to api1/missions.json, we get a nice output in JSON of our missions. We can also use URL segments to pass parameters to get individual missions, or filter by status/ city, etc This same technique can be used for any ee tag, which makes it pretty handy.
  13. • Great for quick & lightweight feed • Read-Only, no

    ability to add, edit or delete • If Debugging is on, could break the feed • Few options to secure the data Template “API” Saturday, November 9, 13
  14. Module API • More Robust, Full CRUD access • Easy

    to install • Reusable for other mercenary clients Saturday, November 9, 13
  15. EE Actions • Accepts send POST & GET requests •

    Output JSON • Maps to Module methods Saturday, November 9, 13 Actions are a way for ExpressionEngine to run code in EE core & third-party addons. Which is great because that means we can use them to access the methods in Mission Tracker They support both GET & POST requests & we can control the output to render JSON or XML.
  16. http://www.a-team.com/index.php?ACT=23 http://www.a-team.com/index.php?ACT=33 http://www.a-team.com/index.php?ACT=99 Saturday, November 9, 13 Here’s the biggest

    problem with ACTions, can you tell me what API methods these actions map to? If any? That’s right, ACTions are not what I’d call clear and understandable which unfortunately rules them out for our API. Also the action ID changes per environment, which makes it hard to distribute to the users of your API What we need is an easy to understand URI structure that is accessible and maps to our API methods
  17. EE Open API Saturday, November 9, 13 The answer came

    from Ben Croker’s Open API for EE
  18. Saturday, November 9, 13 As you can see, our extension

    attaches to the sessions_start ExpressionEngine hook. We then have a conditional that checks to see if segment_1 matches our API trigger, which in this case is simple “api”. If there isn’t a match then the extension ignores the request and passes it on, but if there is a match we attempt to call the API method in segment_2
  19. $this->settings[‘api_trigger’] == segment_1 Saturday, November 9, 13 Here’s some sample

    code from Ben’s module. We have an IF statement checking the first url segment against the trigger
  20. Saturday, November 9, 13 Finally we call a special method

    in our library “call_method” and pass it the second url segment
  21. $method = “get_channels” Saturday, November 9, 13 In our call_method

    method, this second URL segment is the same name as on of our API methods, in this case “get_channels”
  22. $method = “get_channels” Text php executes: $this->get_channels() Saturday, November 9,

    13 PHP takes the method variable and even though it’s a string, is actually able to execute the “get_channels” method
  23. • One class for all api methods, limited extensibility •

    What about more complex urls? /api/mission/show_all /api/equipment/create • Need support for multiple API classes, better separation of business logic Saturday, November 9, 13 I love this technique but there are some room for improvement. First all of our API methods are in one class, which could be problematic if your API has a lot of features, 20 methods crammed in one class file will get very hard to maintain very quickly Also it doesn’t support a REST-style resource base URLs. I’d prefer to design the API using a “trigger/resource/method” type format. This will help developers using the API understand clearly what models they’re working with Finally splitting up methods into multiple API library classes will help me separate business logic which is a good practice for maintainability.
  24. • extension only needs call_method() • as long as our

    API classes implement call_method, route_url() will work Saturday, November 9, 13 So how do be modify this technique to support multiple library classes? Well a clue is that the extension method “route_url()” only needs to know to execute “call_method” it doesn’t care about what other methods in the library it’s executing.
  25. class Mission_lib implements iApi_library { ... } Saturday, November 9,

    13 This means that we can wrap “call_method” into a interface. An interface acts a rule, it basically says “in order to be a valid API class, you must implement a method called “call_method” that accepts one parameter. Notice the interface doesn’t specify how the class should implement call_method. We want the our extension method to have little to no knowledge of the api class beyond call_method. This is what’s called loose coupling & it’s a important concept when designing flexible code.
  26. HTTP Request: /api/mission/show_all load api class: Mission_lib Saturday, November 9,

    13 Now in our extension in the route_url method, we modify it so that segment_2 maps to our resource, in this case it’s Mission. We use ee()->load->library to look for a class “Mission_lib” and load it into EE
  27. HTTP Request: /api/mission/show_all Mission_lib::call_method(“show_all”) Saturday, November 9, 13 We pass

    a third parameter to load->library called “mission_api” as a pointer or bookmark to our Mission_lib api class. Finally we use the third URL segment for our API method, in this case “show_all”
  28. HTTP Request: /api/equipment/show_all Equipment_lib::call_method(“show_all”) load api class: Equipment_lib Saturday, November

    9, 13 If we wanted to extend our API to include Equipment, the extension will load the Equipment_lib class without us having to modify any addition code. Of course this could be pretty dangerous, as it does leave open the possibility of executing any arbitrary code, not just our API methods. That’s why I wrap this code in a conditional that checks the second segment url first to make sure its approved
  29. Saturday, November 9, 13 Going into our Mission_lib class, we

    have a standard PHP method “show_all”
  30. Saturday, November 9, 13 After loading our Mission CodeIgniter model,

    we call a method on the model to return all of the missions as an array
  31. Saturday, November 9, 13 Next we pass the array to

    a method in our Base_api class that will format the data
  32. class: Base_api Saturday, November 9, 13 This was also borrowed

    from Ben’s Open API module. The main work is done by php’s json_encode function. PHP also can decode JSON using json_decode, so it’s really easy to work with JSON in PHP and I prefer it over XML
  33. HTTP POST: /api/mission/create Saturday, November 9, 13 Creating & Update

    works similarly, our mobile developer can send a POST request to /api/ mission/create which lets our API know we’d like to create a new entry.
  34. HTTP POST: /api/mission/create Saturday, November 9, 13 We use EE’s

    built in input class to accept the input and put it into an array which we then pass to the insert method in our Mission CI model
  35. class Mission extends CI_Model Saturday, November 9, 13 Inside of

    our insert method in the model, we call then use EE’s built in db class methods to safely create the new mission Finally we can get back the newly created unique id for the mission by using insert_id
  36. Securing the API • Create & store random API keys

    for registered apps, send via POST • Encrypt using SSL (https://) • Use ee()-input & ee()- db Saturday, November 9, 13 You can go deeper to secure your API, but at the very least create randomly generated API keys for registered apps. Force the apps to send the get in POST requests (you want to avoid using URLs as the key can be written to server logs). Reject requests that don’t have the proper key Use SSL, or HTTPS. It won’t avoid man in the middle attacks, but it makes them harder Use EE’s input & db libraries instead of raw PHP code to process forms. Be paranoid about any input you get from your API and be extremely narrow what data you’ll accept
  37. • Use CI_Model to wrap up CRUD • Organize code

    into libraries for reusability • Use Versioning for backward compatibility • Keep URIs consistent & understandable Best Practices Saturday, November 9, 13 Use CodeIgniter Model to DRY (Don’t Repeat Yourself) the idea should be your API will use the same business logic as the rest of your module Organize your code into libraries for reusability, move common code for your api into base classes. Version your code for backward compatibility, you can use version numbers in the url which means you’ll have to modify your extension slightly. Or use parameters to specify version numbers. Either way ensure updates don’t break the developers on previous versions Your API & the URIs used to access the code should be consistent & understandable
  38. Resources • EE Open API - github.com/ putyourlightson/open-api • Best

    Practices Pragmatic API - http:// bit.ly/1aBT6Dt • Mission Tracker - github.com/ patpohler/EE-Mission-Tracker Saturday, November 9, 13
  39. Tools • JSONLint.com • Postman REST client - http:// getpostman.com

    • Fiddler - http://fiddler2.com/ Saturday, November 9, 13 JSONLint.com to validate/test JSON is valid Postman is a REST client for Chrome, send & receive HTTP request & responses, very good tool for quick inspection of your API without having to right an app. Although I’d highly recommend learning PHPUnit or SimpleTest and writing real unit tests. Finally if you’re on windows there’s Fiddler which is an amazing powerful tool for debugging HTTP requests & responses. It’s an amazing Pro tool and can also execute man-in the middle attacks to test your security. Windows is best but there’s an alpha for Mac/Linux based systems.