<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Andrew McIntosh</title>
    <description>The latest articles on Forem by Andrew McIntosh (@amcintosh).</description>
    <link>https://forem.com/amcintosh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F281997%2F28820b7c-f4c8-4833-afd3-77eb1a27d9c1.jpeg</url>
      <title>Forem: Andrew McIntosh</title>
      <link>https://forem.com/amcintosh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/amcintosh"/>
    <language>en</language>
    <item>
      <title>API Client Design Across Languages - Part 2 - Making Requests</title>
      <dc:creator>Andrew McIntosh</dc:creator>
      <pubDate>Tue, 29 Mar 2022 18:53:59 +0000</pubDate>
      <link>https://forem.com/freshbooks/api-client-design-across-languages-part-2-making-requests-4i28</link>
      <guid>https://forem.com/freshbooks/api-client-design-across-languages-part-2-making-requests-4i28</guid>
      <description>&lt;p&gt;It's been a while since my last post (&lt;a href="https://dev.to/freshbooks/api-client-design-across-languages-part-1-5hmk"&gt;API Client Design Across Languages - Part 1&lt;/a&gt;), but life and work have gotten in the way. Regardless, I'm am finally continuing my dive into how API clients can differ in style and usage across languages while still maintaining the same features.&lt;/p&gt;

&lt;p&gt;The first post focused on the basic structure of different API clients. In this post I will go into how a client may make requests against the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request Libraries
&lt;/h2&gt;

&lt;p&gt;Languages vary in how well supported making HTTP requests is in their core implementations or standard libraries. Almost by definition, languages used on the web generally have easy ways to make HTTP requests. However, there are often dedicated request libraries that can make this simpler or cleaner, and in general I recommend their use unless the language has very clear and clean support.&lt;/p&gt;

&lt;p&gt;There are reasons to not want to include a request library in an SDK as every additional dependency added is one that developers using it will have to add as well. Keeping a small dependency graph also makes it easier to maintain updates. But, letting a library do the low-level work is also good for maintenance and security, and often someone looking to use your SDK will already be using a request library.&lt;/p&gt;

&lt;p&gt;I'll show some examples of the choices FreshBooks made for different languages below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sane Request Defaults
&lt;/h2&gt;

&lt;p&gt;Both to make it easier for developers to use our SDKs, and to make our own lives easier in supporting them, we set some defaults for HTTP requests.&lt;/p&gt;

&lt;p&gt;Timeouts are especially important. If a request takes too long, it may can impact both the user (slowing the response to their customer) and us (if our servers get saturated by slow requests, we stop service our customers). Most HTTP clients have easily set timeouts but often they are not enabled by default.&lt;/p&gt;

&lt;p&gt;Setting user-agent strings is also helpful. Including things like the SDK language and version helps FreshBooks figure our SDK usage and what languages are popular with our developers. Of course we let users over-ride the user-agent if desired. It can also help an API support team track down a reported error if the client has a unique user-agent string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interface
&lt;/h2&gt;

&lt;p&gt;The SDK should try to be as consistent and intuitive as possible, especially if the API itself is a fairly CRUD-heavy RESTful API versus one with more unique behaviours around resources.&lt;/p&gt;

&lt;p&gt;Try to make to keep things standardized like resorce pluralization (eg. &lt;code&gt;clients&lt;/code&gt;, &lt;code&gt;invoices&lt;/code&gt; vs. &lt;code&gt;client&lt;/code&gt;, &lt;code&gt;invoices&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Try to keep method signature variables in a similar order. For instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;versus&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The more standard and intuitive the SDK is, the easier it is for developers and the fewer support tickets.&lt;/p&gt;

&lt;h2&gt;
  
  
  FreshBook's SDKs
&lt;/h2&gt;

&lt;p&gt;Like last time, I'll show some examples of how FreshBook's SDKs are built in a few different languages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python
&lt;/h3&gt;

&lt;p&gt;In python we're using the &lt;a href="https://docs.python-requests.org/en/latest/"&gt;requests&lt;/a&gt; library for simplicity. Requests is widely used (see the &lt;a href="https://github.com/stripe/stripe-python/blob/master/setup.py#L34"&gt;Stripe&lt;/a&gt; and &lt;a href="https://github.com/auth0/auth0-python/blob/master/setup.py#L30"&gt;Auth0&lt;/a&gt; SDKs) so it isn't too onerous of a requirement. In fact, FreshBooks' Python SDK is &lt;a href="https://github.com/freshbooks/freshbooks-python-sdk/blob/release/1.0.0/requirements.txt"&gt;very light on dependencies&lt;/a&gt; in general.&lt;/p&gt;

&lt;p&gt;You can see where we &lt;a href="https://github.com/freshbooks/freshbooks-python-sdk/blob/release/1.0.0/freshbooks/api/resource.py#L30"&gt;instantiate a session&lt;/a&gt; (to allow for retries), and make the &lt;a href="https://github.com/freshbooks/freshbooks-python-sdk/blob/release/1.0.0/freshbooks/api/resource.py#L57-L83"&gt;HTTP requests&lt;/a&gt;). In the client we can instantiate the &lt;a href="https://github.com/freshbooks/freshbooks-python-sdk/blob/release/1.0.0/freshbooks/api/projects.py#L69-L83"&gt;shared client code&lt;/a&gt; for each &lt;a href="https://github.com/freshbooks/freshbooks-python-sdk/blob/release/1.0.0/freshbooks/client.py#L363-L365"&gt;different resource&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Usage looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;freshBooksClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invoice_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;freshBooksClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;organization&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"FreshBooks"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Node.js
&lt;/h3&gt;

&lt;p&gt;Like Python, our Node.js SDK is using a well-known library &lt;a href="https://axios-http.com/"&gt;axios&lt;/a&gt;. While it is not quite as ubiquitous as Python's requests, it is very commonly used. For instance, it is used by &lt;a href="https://github.com/auth0/node-auth0/blob/v2.40.0/package.json#L37"&gt;Auth0&lt;/a&gt; (if you're looking for a different example, &lt;a href="https://github.com/MONEI/Shopify-api-node/blob/3.8.2/package.json#L19"&gt;Shopify&lt;/a&gt; makes use of &lt;a href="https://github.com/sindresorhus/got"&gt;Got&lt;/a&gt;). You can find it &lt;a href="https://github.com/freshbooks/freshbooks-nodejs-sdk/blob/%40freshbooks/api%403.0.0/packages/api/src/APIClient.ts#L169-L177"&gt;configured here&lt;/a&gt;. The &lt;a href="https://github.com/freshbooks/freshbooks-nodejs-sdk/blob/%40freshbooks/api%403.0.0/packages/api/src/APIClient.ts#L264-L292"&gt;shared client code&lt;/a&gt; takes reqeust and response transform functions for &lt;a href="https://github.com/freshbooks/freshbooks-nodejs-sdk/blob/%40freshbooks/api%403.0.0/packages/api/src/APIClient.ts#L350-L360"&gt;each resource&lt;/a&gt; to convert the repsonses to objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP
&lt;/h3&gt;

&lt;p&gt;Like Node.js, the PHP ecosystem has quite a number of good HTTP request libraries. &lt;a href="https://github.com/guzzle/guzzle"&gt;Guzzle&lt;/a&gt; is perhaps one of the most well known, but there are &lt;a href="https://github.com/WordPress/Requests"&gt;many&lt;/a&gt; &lt;a href="https://github.com/php-http/httplug"&gt;other&lt;/a&gt; &lt;a href="https://github.com/nategood/httpful"&gt;popular&lt;/a&gt; libraries out there. Luckily, PHP also has some interface standards around HTTP clients and messages, particularly &lt;a href="https://www.php-fig.org/psr/psr-7/"&gt;PSR-7&lt;/a&gt;, &lt;a href="https://www.php-fig.org/psr/psr-17/"&gt;PSR-17&lt;/a&gt;, and &lt;a href="https://www.php-fig.org/psr/psr-18/"&gt;PSR-18&lt;/a&gt;,&lt;/p&gt;

&lt;p&gt;Implementing the FreshBooks SDK to these standards means that we don't force any particular library on developers. They are free to choose any library that implements those standards.&lt;/p&gt;

&lt;p&gt;In our &lt;a href="https://github.com/amcintosh/freshbooks-php-sdk"&gt;README&lt;/a&gt; we provide an example for those who have no particular preference:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Requires a PSR-18 implementation client. If you do not already have a compatible client, you can install one with it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;composer require amcintosh/freshbooks php-http/guzzle7-adapter&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Again, &lt;a href="https://github.com/amcintosh/freshbooks-php-sdk/blob/0.4.0/src/FreshBooksClient.php#L73-L90"&gt;here is the configuration&lt;/a&gt;, and the &lt;a href="https://github.com/amcintosh/freshbooks-php-sdk/blob/0.4.0/src/FreshBooksClient.php#L204-L207"&gt;resources&lt;/a&gt; using &lt;a href="https://github.com/amcintosh/freshbooks-php-sdk/blob/0.4.0/src/Resource/AccountingResource.php#L119-L124"&gt;shared client code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Usage looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$freshBooksClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$invoiceId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$clients&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$freshBooksClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$accountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$clients&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;organization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'FreshBooks'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Up Next
&lt;/h2&gt;

&lt;p&gt;So there you can see a number of different HTTP client options, and examples of how FreshBooks' SDKs utilize them.&lt;/p&gt;

&lt;p&gt;I hope you found something interesting or useful here and I hope you can catch the next post where I plan to go into request data and response structures.&lt;/p&gt;

</description>
      <category>sdk</category>
      <category>python</category>
      <category>php</category>
      <category>node</category>
    </item>
    <item>
      <title>API Client Design Across Languages - Part 1</title>
      <dc:creator>Andrew McIntosh</dc:creator>
      <pubDate>Thu, 11 Nov 2021 17:39:03 +0000</pubDate>
      <link>https://forem.com/freshbooks/api-client-design-across-languages-part-1-5hmk</link>
      <guid>https://forem.com/freshbooks/api-client-design-across-languages-part-1-5hmk</guid>
      <description>&lt;p&gt;In my recent post &lt;a href="https://dev.to/freshbooks/some-best-practices-on-building-an-integration-1gbb"&gt;Some Best Practices On Building An Integration&lt;/a&gt;, I espoused the benefits of &lt;a href="https://dev.to/freshbooks/some-best-practices-on-building-an-integration-1gbb#use-libraries-tools-and-sdks"&gt;using API owner supplied tools and libraries&lt;/a&gt;, and mentioned areas where a well-built SDK hides complexity from, or otherwise makes things easier for, a developer.&lt;/p&gt;

&lt;p&gt;A colleague suggested that it might be useful to present examples of some of these areas to give some pointers for someone who needs to implement that functionality themselves, can't make use of an SDK, or simply for someone looking to build their own API client. So, this is part 1 of a deep dive into functionality in FreshBooks' (and some other API owner's) SDKs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Structure
&lt;/h2&gt;

&lt;p&gt;This first post won't go too much into functionality as I think it's best to start on structure.&lt;/p&gt;

&lt;p&gt;A RESTful API is language agnostic and clients built any number of languages must all support the same API features and resources. However, the actual design of the client and usage of the client itself can, and probably should, be different language to language. For example, a Ruby client versus a Java client will still call the same API endpoint, but the form of the methods to make that call, and the form of the returned data could look very different.&lt;/p&gt;

&lt;p&gt;I feel it's best to build an API client in a way that is natural to the specific language it's written in. This extends from the project layout, to the client initialization, the method calls themselves, and the returned data. This makes things more intuitive and easy for a developer to use.&lt;/p&gt;

&lt;p&gt;The language influences the design primarily in two ways: language capabilities, and common language conventions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capabilities
&lt;/h3&gt;

&lt;p&gt;By capabilities, I'm talking about language design and features. A statically typed language usually needs a bit more structure than a dynamically typed one. For instance, an API client in a language like PHP or Python could return JSON results as associative arrays (array and dictionary respectively), as you don't have to declare the various return value's types are. It would be difficult to do the same in Java with a HashMap (possible, but it would not be clean), so you're much more likely to build data objects for the responses with all the fields included and nicely typed.&lt;/p&gt;

&lt;p&gt;Other features play in as well. How does the language handle functions with different options? Function overloadings? Optional arguments? These all affect the design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conventions
&lt;/h3&gt;

&lt;p&gt;Beyond what you &lt;em&gt;can&lt;/em&gt; do with a language, there's also what you &lt;em&gt;should&lt;/em&gt; do. You &lt;em&gt;can&lt;/em&gt; write your Python or Ruby in a very Java-like way, but it might not feel as natural to a Ruby developer using your library. Of course conventions aren't so cut-and-dry as capabilities; there are many ways to do something and sometimes one is considered "more right" than others, but often not as well. Looking at how other libraries are implemented and getting to know a language helps informs a lot of design choices. The best advice is to try to make things clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  FreshBook's SDKs
&lt;/h2&gt;

&lt;p&gt;At the time of writing, FreshBooks has first-party Python and Node.js SDKs, and a community-supported Java one (all three are listed &lt;a href="https://www.freshbooks.com/api/libraries"&gt;here&lt;/a&gt;). As I said, I'm going to walk through some of the differences in the design, but today I'll get started with the basics of client initialization and configuration.&lt;/p&gt;

&lt;p&gt;First, let's talk about the configuration the FreshBooks' SDKs need to support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We require the clients to be initialized with their application's unique client id for the user-agent string, so that's a required parameter.&lt;/li&gt;
&lt;li&gt;To use the API requires authentication. Depending on what a developer has implemented, they'll either have a valid OAuth2 access token to initialize the client with, or they'll want to go through the authorization flow, which would require their client secret and redirect urls. Ideally the SDK supports both.&lt;/li&gt;
&lt;li&gt;If they have an expired token, they may want to refresh it, which would require the refresh token to be supplied.&lt;/li&gt;
&lt;li&gt;The developer may want to override some of the default settings like user-agent string, timeouts, or disabling automatic retries on failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java
&lt;/h3&gt;

&lt;p&gt;I'll start with the Java SDK because the features of the Java language makes it a good first example to set the others against.&lt;/p&gt;

&lt;p&gt;Java supports function overloading, but with the number of possible options mentioned above, that would get very compicated combination-wise. You could just use nullable parameters, but that would be confusing and ugly. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FreshBooksClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;clientId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;clientSecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;redirectUri&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userAgent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which could like anything like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FreshBooksClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FreshBooksClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FreshBooksClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what the &lt;a href="https://en.wikipedia.org/wiki/Builder_pattern"&gt;builder pattern&lt;/a&gt; is for. You can see the full code for&lt;br&gt;
&lt;a href="https://github.com/amcintosh/freshbooks-java-sdk/blob/v0.3.0/lib/src/main/java/net/amcintosh/freshbooks/FreshBooksClient.java#L73-L88"&gt;the client&lt;/a&gt; and &lt;a href="https://github.com/amcintosh/freshbooks-java-sdk/blob/v0.3.0/lib/src/main/java/net/amcintosh/freshbooks/FreshBooksClient.java#L410"&gt;the builder&lt;/a&gt; on github but essentially the client is not initialized directly. You initialize a "client builder", which has a constructor for each of the base cases (&lt;strong&gt;"client_id"&lt;/strong&gt; versus &lt;strong&gt;"client_id, secret, url"&lt;/strong&gt;) and different methods for the various options, and the builder returns a client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;FreshBooksClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FreshBooksClientBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FreshBooksClientBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;clientId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;clientSecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;redirectUri&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FreshBooksClientBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;clientId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;FreshBooksClientBuilder&lt;/span&gt; &lt;span class="nf"&gt;withAccessToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;FreshBooksClientBuilder&lt;/span&gt; &lt;span class="nf"&gt;withReadTimeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which allows you to instantiate the client in the various differing ways cleanly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FreshBooksClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FreshBooksClientBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FreshBooksClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FreshBooksClientBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withAccessToken&lt;/span&gt;&lt;span class="o"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FreshBooksClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FreshBooksClientBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withAccessToken&lt;/span&gt;&lt;span class="o"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withReadTimeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This requires much more structure in the client, but allows much cleaner usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python
&lt;/h3&gt;

&lt;p&gt;By comparison, Python allows for a much more concise implementation. Python is an object-oriented language and you could implement a builder pattern, but as python also supports &lt;a href="https://en.wikipedia.org/wiki/Named_parameter"&gt;named parameters&lt;/a&gt;, and there actually aren't too many options for the client, we can get away with something much simpler and more in the pythonic style (again, &lt;a href="https://github.com/freshbooks/freshbooks-python-sdk/blob/release/0.8.0/freshbooks/client.py#L33"&gt;full code on github&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;auto_retry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which allows for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;redirect_uri&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the language features of Python can lead to a very different implementation and usage than Java.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node.js
&lt;/h3&gt;

&lt;p&gt;FreshBooks' Node.js SDK is written in TypeScript. Again, there are different ways to go about implementation, but we took a fairly common javascript pattern and passed &lt;a href="https://github.com/freshbooks/freshbooks-nodejs-sdk/blob/%40freshbooks/api%403.0.0/packages/api/src/APIClient.ts#L1290-L1298"&gt;a configuration object&lt;/a&gt; as &lt;a href="https://github.com/freshbooks/freshbooks-nodejs-sdk/blob/%40freshbooks/api%403.0.0/packages/api/src/APIClient.ts#L137"&gt;a parameter&lt;/a&gt;. The &lt;a href="https://github.com/stripe/stripe-node"&gt;Stripe Node.js Library&lt;/a&gt; does something similar (in general Stripe is a great place to look for any "how have others"-type API questions.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Options&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;redirectUri&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;retryOptions&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IAxiosRetryConfig&lt;/span&gt;
    &lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultRetry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;retryDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;axiosRetry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exponentialDelay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;retryCondition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APIClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isNetworkRateLimitOrIdempotentRequestError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;redirectUri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FRESHBOOKS_API_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;retryOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defaultRetry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with initialization looking like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;redirectUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also happens to be a fairly common pattern in PHP, thus a possible future FreshBooks PHP SDK would likely look similar. &lt;a href="https://github.com/auth0/auth0-PHP#sdk-initialization"&gt;auth0's PHP SDK&lt;/a&gt; has an example of this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up Next
&lt;/h2&gt;

&lt;p&gt;I hope you found it interesting seeing the different ways a client for the same API can look language-to-language. As I said, I'll dive a bit more into functionality differences next time, but feel free to dig around the projects and if you have any questions, please reach out.&lt;/p&gt;

</description>
      <category>sdk</category>
      <category>java</category>
      <category>python</category>
      <category>node</category>
    </item>
    <item>
      <title>Advent of Code</title>
      <dc:creator>Andrew McIntosh</dc:creator>
      <pubDate>Thu, 04 Nov 2021 02:41:50 +0000</pubDate>
      <link>https://forem.com/amcintosh/advent-of-code-3fi6</link>
      <guid>https://forem.com/amcintosh/advent-of-code-3fi6</guid>
      <description>&lt;p&gt;Hacktoberfest is done, but the &lt;a href="https://adventofcode.com/"&gt;Advent of Code&lt;/a&gt; is less than a month away.&lt;/p&gt;

&lt;p&gt;Anyone planning to participate?&lt;br&gt;
What language do you want to use?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>adventofcode</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>My First Hacktoberfest As A Maintainer</title>
      <dc:creator>Andrew McIntosh</dc:creator>
      <pubDate>Fri, 29 Oct 2021 23:31:29 +0000</pubDate>
      <link>https://forem.com/amcintosh/my-first-hacktoberfest-as-a-maintainer-m4</link>
      <guid>https://forem.com/amcintosh/my-first-hacktoberfest-as-a-maintainer-m4</guid>
      <description>&lt;p&gt;I've been participating in Hacktoberfest for a few year now and it's always been a fun and interesting experience looking for projects I can lend a hand on. &lt;/p&gt;

&lt;p&gt;This year however I happened to have a few public projects myself, one of my own and a few through work that I'm a  maintainer of, so I figured I might as well open them up to Hacktoberfest contributions and see if there was any interest. I wasn't expecting much, but I was taken by surprise!&lt;/p&gt;

&lt;p&gt;I've had public repos in past years, but never something really of interest to a wider audiance or with clear enough needs and defined work. &lt;/p&gt;

&lt;p&gt;So the first thing I did was get a few github issues together for each. The work projects already had some, but I tidied them up and pull a few more from our internal bug tracker. For my own project I went though my local todo list and made issues for them. Finally I labelled the issues and the projects with the "hacktoberfest" tag, and marked a few more straight forward ones as "good first issues".&lt;/p&gt;

&lt;p&gt;And then I went about my life. One morning however I saw a message about a pull request against the one repo. Someone had come along and was contributing! I reviewed the work, which was good (and actually improved some existing work), thanked them, and merged. The next morning there was another from the same developer! This issue had some ambiguity so we chatted about the work and before long they came back with changes and it too was merged. I was thrilled as these two issues were things that I had been meaning to get to for a bit, but hadn't had a chance. &lt;/p&gt;

&lt;p&gt;I was even more surprised a few weeks later when someone opened a pull request against the one project with a minor refactor that improved the "pythonic" style of it. There was no issue around it, someone had just read through the code and saw a way it could be better. I don't know for sure that this developer came in via the hacktoberfest, but I suspect they may have. &lt;/p&gt;

&lt;p&gt;So for me, this hacktoberfest helped me cross off a bunch of work from my todo, helped me clarified some of the work that when I documented the issues and agian when I discuss them with the other developer, and showed me some improvements that I hadn't even thought of. Overall a great experience.&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>Python API Client Using Magic Methods</title>
      <dc:creator>Andrew McIntosh</dc:creator>
      <pubDate>Fri, 29 Oct 2021 11:30:09 +0000</pubDate>
      <link>https://forem.com/amcintosh/python-api-client-using-magic-methods-5d5</link>
      <guid>https://forem.com/amcintosh/python-api-client-using-magic-methods-5d5</guid>
      <description>&lt;p&gt;Python "magic methods" allow for some very fun and powerful code.&lt;/p&gt;

&lt;p&gt;Magic methods refers to all those special methods in Python classes that start and end with &lt;code&gt;__&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The documentation for &lt;a href="https://docs.python.org/3/reference/datamodel.html#special-method-names"&gt;Special method names&lt;/a&gt; says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A class can implement certain operations that are invoked by special syntax (such as arithmetic operations or subscripting and slicing) by defining methods with special names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For instance the &lt;code&gt;__init__&lt;/code&gt; method is called when a new instance of a class is instantiated. &lt;code&gt;__str__&lt;/code&gt; is a method that returns a string representation of the class. As stated in the docs, these are not methods that generally get invoked directly on the class. For example, you don't call &lt;code&gt;my_object.__str__()&lt;/code&gt; but &lt;code&gt;__str__&lt;/code&gt; would be called by &lt;code&gt;print(my_object)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I'm going to demonstrate using the &lt;code&gt;__getattr__&lt;/code&gt; and &lt;code&gt;__call__&lt;/code&gt; magic methods to build a dynamic API client.&lt;/p&gt;

&lt;p&gt;Let's say you're building a client for an API with endpoints like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GET &lt;a href="https://some-api.net/user"&gt;https://some-api.net/user&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GET &lt;a href="https://some-api.net/user/permissions"&gt;https://some-api.net/user/permissions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;POST &lt;a href="https://some-api.net/article"&gt;https://some-api.net/article&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GET &lt;a href="https://some-api.net/article/"&gt;https://some-api.net/article/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a lot of ways to build a client for this. One might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;APIClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://some-api.net"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unexpected Response '%s' from '%s'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_body&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_permissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user/permissions"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"article/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We would then use the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;APIClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_user_permissions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;new_article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_article&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"some"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;existing_article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is fine. &lt;/p&gt;

&lt;p&gt;But what if the API is changing often? Or if there are dozens of endpoints and you don't know which you need to call? If later on you discover you need to call &lt;code&gt;GET https://some-api.net/user/roles&lt;/code&gt; or &lt;code&gt;GET https://some-api.net/some_other_thing?some_param=foo&lt;/code&gt;, you'll need to go back to you client and add matching methods.&lt;/p&gt;

&lt;p&gt;It would be nice to have things a bit more dynamic, and one way to do that is with magic methods. Specifically, I'll be using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;__getattr__&lt;/code&gt; which is "Called when the default attribute access fails with an AttributeError" (see &lt;a href="https://docs.python.org/3/reference/datamodel.html#object.__getattr__"&gt;the docs&lt;/a&gt;). This means that if you call &lt;code&gt;my_object.some_missing_attr&lt;/code&gt;, then &lt;code&gt;__getattr__&lt;/code&gt; will get invoked with &lt;code&gt;"some_missing_attr"&lt;/code&gt; as the &lt;code&gt;name&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;__call__&lt;/code&gt; which is "Called when the instance is "called" as a function" (see &lt;a href="https://docs.python.org/3/reference/datamodel.html#object.__call__"&gt;the docs&lt;/a&gt;), eg. &lt;code&gt;my_object()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using these, we can build something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;APIClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://some-api.net"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;BASE_URL&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;APIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;object_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unexpected Response '%s' from '%s'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_body&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to call the API like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;APIClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;new_article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"some"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;existing_article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How does this work? &lt;/p&gt;

&lt;p&gt;Well in the case of &lt;code&gt;client.user.permissions()&lt;/code&gt;, &lt;code&gt;.user&lt;/code&gt; is an attribute and since the client doesn't have that attribute, it calls &lt;code&gt;__getattr__&lt;/code&gt; with &lt;code&gt;name="user"&lt;/code&gt;, which then returns an &lt;code&gt;APIClient&lt;/code&gt; instance with the name appended to the endpoint URL. The same happens when &lt;code&gt;.permissions&lt;/code&gt; is invoked on the &lt;code&gt;APIClient&lt;/code&gt; instance that was returned by &lt;code&gt;.user&lt;/code&gt;, in turn giving us another &lt;code&gt;APIClient&lt;/code&gt; instance, now with a path of &lt;code&gt;https://some-api.net/user/permissions&lt;/code&gt;. Finally this &lt;code&gt;APIClient&lt;/code&gt; instance is called by &lt;code&gt;()&lt;/code&gt;, which invokes the &lt;code&gt;__call__&lt;/code&gt; method. This method makes the actually HTTP call to the constructed URL based in any parameteres passed in, but defaults to a &lt;code&gt;GET&lt;/code&gt; request.&lt;/p&gt;

&lt;p&gt;In the case of the article calls, &lt;code&gt;client.article(id=123)&lt;/code&gt; works much the same way, but the &lt;code&gt;APIClient&lt;/code&gt; is called with an id parameter. This id gets appended to the url by the same internal &lt;code&gt;_endpoint()&lt;/code&gt; method that &lt;code&gt;__getattr__&lt;/code&gt; uses, which results in a GET call to &lt;code&gt;https://some-api.net/article/&amp;lt;id&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;client.article(method="POST", data={"some": "data"})&lt;/code&gt;, we override the method and add a data payload to the POST.&lt;/p&gt;

&lt;p&gt;As you can see, the client itself can handle a large variety of API calls without any significant changes, allowing the utilizing application a lot of flexibility. &lt;/p&gt;

&lt;p&gt;In the example of the newly required calls, &lt;code&gt;GET https://some-api.net/user/roles&lt;/code&gt; and &lt;code&gt;GET https://some-api.net/some_other_thing?some_param=foo&lt;/code&gt;, no changes to the client are needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;thing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;some_other_things&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_param&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>programming</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Some Best Practices On Building An Integration</title>
      <dc:creator>Andrew McIntosh</dc:creator>
      <pubDate>Tue, 19 Oct 2021 10:57:35 +0000</pubDate>
      <link>https://forem.com/freshbooks/some-best-practices-on-building-an-integration-1gbb</link>
      <guid>https://forem.com/freshbooks/some-best-practices-on-building-an-integration-1gbb</guid>
      <description>&lt;p&gt;Hi, I’m Andrew McIntosh. I’m a software engineer at FreshBooks. I’ve moved around development teams a couple of times, but right now I’m working on our API and Integrations team, trying to make things better for developers looking to build API integrations. If that’s you, or you want that to be you, here are five bits of advice on how you can build an (or build a better) API integration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get To Know The API&lt;/li&gt;
&lt;li&gt;Use Libraries, Tools, and SDKs&lt;/li&gt;
&lt;li&gt;Limit The Scope of Requests&lt;/li&gt;
&lt;li&gt;Properly Handle Errors&lt;/li&gt;
&lt;li&gt;Do More Asynchronous Work&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Get To Know The API
&lt;/h2&gt;

&lt;p&gt;This might seem obvious, &lt;strong&gt;but read the docs of whatever API you’re working with&lt;/strong&gt;. This is really the starting point on figuring out how you can do the thing you want to do. Reading through, you might even find a better way to do something than you initially thought. A good API is intuitive and predictable, but even good APIs can have odd, unexpected behaviour in places. Look at examples too as they can really help in cases where the documentation is dated or sparse. &lt;/p&gt;

&lt;p&gt;Keeping API documentation up to date is not always easy, so if you find some place where it’s wrong, someone will be very happy if you report it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Libraries, Tools, and SDKs
&lt;/h2&gt;

&lt;p&gt;A company with an API wants it to be used, so often they invest time and effort into making things that will make your life using it easier (like this!). &lt;strong&gt;Take advantage of the work they did for you and consider using libraries or SDKs they’ve provided&lt;/strong&gt;. If a company doesn’t have an SDK, or if what they have doesn’t fit your language or framework of choice, take a look for a 3rd-party solution (often API docs will list a bunch of these in addition to company-build ones).&lt;/p&gt;

&lt;p&gt;Using an SDK or library isn’t generally required, but there are advantages to not having to reinvent the wheel. Many of the topics I’m going to cover next might already be handled by a well-built SDK! Often SDK developers know the API quite well (especially if they’re employees of the company) and they can often simplify, clarify, or even paper over those oddities or inconsistencies I mentioned in documentation.&lt;/p&gt;

&lt;p&gt;For example, for historical reasons a lot of the oldest accounting endpoints in FreshBooks’ API return dates in North American Eastern Time (US/Eastern aka EST/EDT, time zones are complicated), while all newer endpoints to return dates in UTC (we’re moving everything to UTC, but it takes time). If you’re using one of our SDKs, we hide that from you and return all the dates in UTC. We’ve done the work so YOU don’t have to figure out which is which!&lt;/p&gt;

&lt;p&gt;Just like documentation, if you find a bug or missing feature in a tool, the owner would love your feedback, and if it’s open source and you’re keen, you could even try to fix it yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limit The Scope of Requests
&lt;/h2&gt;

&lt;p&gt;For your own benefit, as well as the API owner’s you want to &lt;strong&gt;fetch data in an efficient way&lt;/strong&gt;. This means not grabbing data that you don’t care about and will just throw away, as well as not fetching too much data at once and then having memory or performance issues trying to process it that can spill over into slow responsiveness for your users.&lt;/p&gt;

&lt;p&gt;This means that you should look at how an API handles filters (to limit returned records to only those you care about), pagination (fetch records in smaller batches so you can handle them in chunks), sorting (so those batches come in an order that works for you), and maybe even what fields are included in the response (so the record itself isn’t filled with data you don’t need). Here is FreshBooks’ &lt;a href="https://www.freshbooks.com/api/parameters"&gt;Search, Paging and Includes documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, one integration I was debugging would sync invoices from another service to a particular client. It would check to see if that existed before creating it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matchingClients&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;freshbooksClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matchingClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;matchingClients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;currClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;organization&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;clientOrganization&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it was sometimes creating duplicate clients. This was because FreshBooks defaults to returning 30 records at a time with the newest ones first. This worked fine when it first created the client, but as customers used the app and made more clients, the client to sync with got bumped off of the first 30 results and was no longer found. In addition to that, the code was fetching as many clients as it could, and then filtered them in memory. It either needed a filter or pagination (or both).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clientSearchQueryBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SearchQueryBuilder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Organization_like&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clientOrganization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matchingClients&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;freshBooksApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fbAccountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;searchQueryBuilder&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matchingClients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matchingClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;matchingClients&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let the API do that work!&lt;/p&gt;

&lt;h2&gt;
  
  
  Properly Handle Errors
&lt;/h2&gt;

&lt;p&gt;There’s a lot to proper error handling, so let's look at things in pieces.&lt;/p&gt;

&lt;h4&gt;
  
  
  API Errors
&lt;/h4&gt;

&lt;p&gt;A lot of API docs will have information on error codes, states, messages. You don’t want your integration to break unexpectedly, so you should look to handle these. Logging response messages is really helpful when building an application to understand business rules or validation failures. When you’re up and running in production, it’s equally important to help you know why something might be failing. For example, a good API won’t just give you a &lt;code&gt;422 Unprocessable Entity&lt;/code&gt;, but might return a message like &lt;code&gt;422 - At least one field among first_name, last_name, email or organization is required&lt;/code&gt; (a &lt;a href="https://www.freshbooks.com/api/clients"&gt;FreshBooks client&lt;/a&gt; validation error message)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The API isn’t your code, and you don’t control what comes out. This is a black box, and it’s helpful to follow defensive coding practices.&lt;/strong&gt; You should check http response codes, wrap your calls in try/catches, etc.. Don’t assume that a response you get has an object or resource data structure as a failure may return an error data structure instead, so you should be prepared to parse or otherwise handle either. If a REST APIs backend service is down, a company’s API gateway might not even return you JSON but instead give you an HTML error page that your JSON-expecting code will just fail to parse. In short, don’t assume that the response will always come in the form you expect, and ensure you handle cases when it doesn’t.&lt;/p&gt;

&lt;h4&gt;
  
  
  Timeouts and Connection Exceptions
&lt;/h4&gt;

&lt;p&gt;The API you're using isn’t perfect. It could be slow or offline. In these cases, your integration could run into issues if all the processors or workers are stuck waiting on the API. &lt;strong&gt;For this reason, you should configure timeouts on your calls.&lt;/strong&gt; HTTP clients have easily set timeouts but often they are not enabled by default. Again, a good SDK will have some sane defaults for you (we &lt;a href="https://github.com/freshbooks/freshbooks-python-sdk/blob/release/0.8.0/freshbooks/client.py#L51"&gt;default to 30 seconds but let you easily override&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In general there are connect timeouts (how long you wait to establish a connection), and read timeouts (how long you wait for the response). Some clients let you set them together or individually, but you’ll want to set both to protect from an unresponsive server (connect), or a really slow response (read). &lt;/p&gt;

&lt;p&gt;Figuring out exactly what the timeout values should be is very dependent on your integration and the API you’re using, but even setting them to something high (15-30 seconds) can save you a lot of pain.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rate Limits
&lt;/h4&gt;

&lt;p&gt;Most APIs will limit traffic to prevent a bad actor from hindering other integration’s performance or degrading the entire system. You should build your integration to respect and accommodate these limits. Running up against them constantly doesn’t do you any good, as your work won’t get done faster, and could result in your integration being banned. &lt;/p&gt;

&lt;p&gt;As mentioned above, it’s best to build your integration with efficient calls in mind. Reducing the number of calls you need to make means you’re less likely to hit call limits. &lt;/p&gt;

&lt;p&gt;Another good practice is to properly handle rate limit errors (generally HTTP 429 errors), and then rather than retry right away, wait for a bit before making the next call. If the next call is still limited, wait even longer. This is called an exponential backoff, and again, most HTTP libraries will have a way to enable this and many SDKs will have implemented this (our SDKs do it &lt;a href="https://github.com/freshbooks/freshbooks-python-sdk/blob/release/0.8.0/freshbooks/api/resource.py#L34-L42"&gt;here&lt;/a&gt; and &lt;a href="https://github.com/freshbooks/freshbooks-nodejs-sdk/blob/%40freshbooks/api%402.0.1/packages/api/src/APIClient.ts#L115-L119"&gt;here&lt;/a&gt;). &lt;/p&gt;

&lt;h2&gt;
  
  
  Do More Asynchronous Work
&lt;/h2&gt;

&lt;p&gt;If your integration is handling a lot of data via an API, you should consider &lt;strong&gt;moving as much work to asynchronous tasks as possible&lt;/strong&gt;. In this way you can not block your users, manage and throttle your work loads, and easily retry failures. The above advice on handling rate limits gets a lot easier if your processing code isn’t blocking user actions while retrying. It also lets you throttle the calls in whatever queue system you’re using. &lt;/p&gt;

&lt;p&gt;Look into &lt;a href="https://aws.amazon.com/sqs/"&gt;Amazon’s SQS&lt;/a&gt;, &lt;a href="https://cloud.google.com/tasks/docs/creating-queues"&gt;Google’s Cloud Tasks queues&lt;/a&gt;, Python’s &lt;a href="https://docs.celeryproject.org/en/stable/getting-started/introduction.html"&gt;celery&lt;/a&gt; and something like &lt;a href="https://www.cloudamqp.com/"&gt;CloudAMQP&lt;/a&gt;, or &lt;a href="https://github.com/OptimalBits/bull"&gt;Bull&lt;/a&gt; with Redis. There are a lot of options out there.&lt;/p&gt;

&lt;p&gt;Another good idea is to use webhooks if the API supports them (&lt;a href="https://www.freshbooks.com/api/webhooks"&gt;FreshBooks does&lt;/a&gt;). This allows you to register to receive messages when an event happens. Rather than polling an API every 5 minutes to see if a new resource has been created, you can tell the API to send you a message when that happens. This can save you a lot of calls and overhead. &lt;/p&gt;

&lt;p&gt;Going back to that old integration I was debugging, it would sync invoices with FreshBooks every 20 minutes, but the process involved gathering up all the invoices it hadn’t pushed and looping through them. However, the process was driven by a synchronous HTTP call that would timeout after just a couple minutes, killing the process. It could take days to move everything over. Calling the process more often would only help a little as there were only so many calls the service could handle at one time and these long-running calls could block workers from handling user interaction with the app. We redesigned the whole sync process of the integration so that everything was done in small asynchronous tasks. On first signup we had an async task that would fetch each invoice (paginated), and put a message on the queue to process that invoice. The invoice processing tasks could thus be retried, scaled up, or throttled as needed. We also utilized webhooks for real-time updates. Each event received would just create another invoice process task. Instead of days, things could be processed in seconds, minutes, or perhaps hours for very large workloads. &lt;/p&gt;

&lt;h2&gt;
  
  
  Go Out And Build
&lt;/h2&gt;

&lt;p&gt;Well, I hope that gives you a few ideas on building a robust integration. If you know the API you’re using and the tools available, keep your calls efficient, handle the unexpected gracefully, and keep your time consuming processing away from the user’s actions, you’re on a great path to succeed. If you have any questions, please reach out to me, and if you have anything related to FreshBooks’ API you can email &lt;a href="mailto:newapi@freshbooks.com"&gt;newapi@freshbooks.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>freshbooks</category>
      <category>sdk</category>
    </item>
  </channel>
</rss>
