<?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: G Adventures</title>
    <description>The latest articles on Forem by G Adventures (@gadventurestech).</description>
    <link>https://forem.com/gadventurestech</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%2Forganization%2Fprofile_image%2F196%2F4e9ae74d-5450-4ffc-9c6c-b39ea4e77f43.png</url>
      <title>Forem: G Adventures</title>
      <link>https://forem.com/gadventurestech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gadventurestech"/>
    <language>en</language>
    <item>
      <title>Migrating our API Gateway from Flask to Django</title>
      <dc:creator>Adam McKerlie</dc:creator>
      <pubDate>Tue, 17 Apr 2018 16:12:56 +0000</pubDate>
      <link>https://forem.com/gadventurestech/migrating-our-api-gateway-from-flask-to-django-56k0</link>
      <guid>https://forem.com/gadventurestech/migrating-our-api-gateway-from-flask-to-django-56k0</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AT-NaIlxpxpB0uvmZ." class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AT-NaIlxpxpB0uvmZ."&gt;&lt;/a&gt;“Fresh pineapple sitting on the ocean shoreline” by &lt;a href="https://unsplash.com/@pineapple?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Pineapple Supply Co.&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At G Adventures, we’ve been building a &lt;a href="https://tech.gadventures.com/why-we-created-a-rest-api-at-g-adventures-b9a99abc86d" rel="noopener noreferrer"&gt;publicly accessible API&lt;/a&gt; for the past three years. We’ve been pushing hard to make the G Adventures API a standard within the Adventure Travel industry, and we believe we’re doing so by providing superb documentation &amp;amp; support, as well as enabling our customers with a standards-compliant JSON/XML API, powered by a suite of services and tools.&lt;/p&gt;

&lt;p&gt;When we first tackled the construction of our API, we wanted to keep things simple. We have many systems at G Adventures which own data, and we wanted to ensure that the API would not be an owner of data, simply focused on delivering data.&lt;/p&gt;

&lt;p&gt;Thus, we looked towards frameworks that kept things simple. In the Python ecosystem, one of the most popular frameworks for keeping things simple is Flask.&lt;/p&gt;

&lt;p&gt;Flask is a micro-framework that truly does not get in your way. It ensures you can write plain Python, with additional batteries included. This was great for our needs, as our mandate was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a system that can capture a single request, and transform that into potentially multiple requests to source systems. Effectively, an API Gateway&lt;/li&gt;
&lt;li&gt;Ensure we don’t re-invent the wheel when it comes to HTTP Standards&lt;/li&gt;
&lt;li&gt;Keep it lean and fast. Speed is important. Both from development ramp-up and data delivery perspectives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thus we began iterating on our API prototype. Flask truly did not get in our way. In a couple of days, we had a prototype that would proxy a single client request into multiple requests behind-the-scenes. We iterated on this prototype until we came closer to a reasonable product. Building out our resource definitions in pure Python, we were able to define common patterns, and store them as simple dictionaries. We’ll touch a bit more on what this looks like today shortly.&lt;/p&gt;

&lt;p&gt;Eventually, we launched our API. It was a success! We had many internal, and external clients consuming G Adventures data. We began expanding our system by offering more resources, tools like OAuth, payload validation, and so on. As we began to add these tools, we introduced concepts like an ORM (Via SQLAlchemy) and migrations (Alembic) to our infrastructure.&lt;/p&gt;

&lt;p&gt;These aren’t foreign concepts, and the tools mentioned are superb, but we’d began to slide away from our core fundamentals. Our API was becoming larger in scope, we began re-inventing the wheel, and our simple approach of dictionaries for resource definition worked, but didn’t. Essentially, we’ve hit a point where we could identify that if we continue to build upon this micro-framework, we’d build upon our technical debt. This was an opportunity to rethink&lt;/p&gt;

&lt;p&gt;We began to think of how we move forward. We want to keep the API layer simple, and allow other microservices to provide the various features of the G Adventures API, but we wanted to have a consistent, well-structured set of ideas we could leverage and build from.&lt;/p&gt;

&lt;p&gt;And thus, we waved hello to Django Rest Framework. Django Rest Framework is a perfect companion to a system where you have Django Models, and you want to expose an API to them. It’s so simple that most of our internal systems that expose an API to our API Gateway use it. However, we were in a different situation than a typical Django project.&lt;/p&gt;

&lt;p&gt;We were actively running an API service which was receiving millions of hits per week, we wanted to leverage Django Rest Framework, but would need to pivot it to fit our needs of multiple HTTP backend services. Essentially, we had to make Django Rest Framework work in the ways we’ve structured our API. Let’s take a look at what that looked like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Migrating while the machine is running&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the API, we support 90 resources, each with a different degree of complexity. A read-only resource on one backend is easier to test than a read-write resource using many backends. At the end of the day, migrating more than 5 simple resources at once is asking for trouble. Our decision was to migrate a few at a time and proxy the requests to the appropriate process (Flask or DRF). This allowed us to migrate slowly, communicate with our clients in case of errors and easily route back to Flask if needed.&lt;/p&gt;

&lt;p&gt;Being able to revert to Flask served us well. You can migrate resources with precise respect to the documentation, but that does not guarantee success. Django Rest Framework could reject a request while the Flask framework wouldn’t. For example, Flask will happily process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST to /bookings POST to /bookings/resource
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While Django Rest Framework will only accept:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST to /bookings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our documentation always said to use bookings. bookings/resource was not prohibited in our documentation, it simply wasn’t mentioned. One of our clients used bookings/resource, it went through and they continued to use it. When we migrated bookings to DRF, the client messaged us about it. Flask overlooked this error but DRF did not. Well, we were surprised bookings/resource was a valid request on our own API. I told the client to remove resource for all our resources. Now imagine we migrated all 90 resources at once — all integrations using &amp;lt;resource&amp;gt;/resource would be sad.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizing our endpoints&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With Django Rest Framework, a resource is served by binding an endpoint to a view. For example, to serve bookings on the API, we simply had to register it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;router.register(bookings, BookingView)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using DRF generic router, this will support requests on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bookings/\&amp;lt;booking\_id\&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our Flask supported other URL variations, and one of our challenges was to properly route all existing requests. Here is what we needed to serve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\&amp;lt;resource\&amp;gt;/\&amp;lt;id\&amp;gt;/ \&amp;lt;resource\&amp;gt;/\&amp;lt;id\&amp;gt;/\&amp;lt;resource\_2\&amp;gt; \&amp;lt;resource\&amp;gt;/\&amp;lt;id\&amp;gt;/\&amp;lt;variation\_id\&amp;gt; \&amp;lt;resource\&amp;gt;/\&amp;lt;id\&amp;gt;/\&amp;lt;variation\_id\&amp;gt;/\&amp;lt;resource\_2\&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we found how DRF parses URL: &lt;a href="https://github.com/encode/django-rest-framework/blob/1c53fd32125b4742cdb95246523f1cd0c41c497c/rest_framework/routers.py#L230" rel="noopener noreferrer"&gt;https://github.com/encode/django-rest-framework/blob/1c53fd32125b4742cdb95246523f1cd0c41c497c/rest_framework/routers.py&lt;/a&gt;&lt;a href="https://github.com/encode/django-rest-framework/blob/master/rest_framework/routers.py#L240" rel="noopener noreferrer"&gt;#L241.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means our bookings/&amp;lt;id&amp;gt; will be valid through DRF’s default regex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;base\_regex=’(?P\&amp;lt;{lookup\_prefix}{looking\_url\_kwarg}\&amp;gt;{lookup\_value})’
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;URLs are constructed in get_urls, we can easily overwrite this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get\_urls(self): urls = super().get\_urls() return urls + \&amp;lt;our custom urls\&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our custom URLs will take care of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\&amp;lt;resource\&amp;gt;/\&amp;lt;id\&amp;gt;/\&amp;lt;resource\_2\&amp;gt; \&amp;lt;resource\&amp;gt;/\&amp;lt;id\&amp;gt;/\&amp;lt;variation\_id\&amp;gt; \&amp;lt;resource\&amp;gt;/\&amp;lt;id\&amp;gt;/\&amp;lt;variation\_id\&amp;gt;/\&amp;lt;resource\_2\&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s say we had an itineraries resource with ID 2222, a possible variation ID of 3333 and also a nested resource of itinerary_maps. These are all valid requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Variation 1: itineraries/2222/3333 Variation 2: itineraries/2222/itinerary\_maps Variation 3: itineraries/2222/3333/itinerary\_maps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we overwrote this method &lt;a href="https://github.com/encode/django-rest-framework/blob/1c53fd32125b4742cdb95246523f1cd0c41c497c/rest_framework/routers.py#L230" rel="noopener noreferrer"&gt;https://github.com/encode/django-rest-framework/blob/1c53fd32125b4742cdb95246523f1cd0c41c497c/rest_framework/routers.py#L23&lt;/a&gt;0 to add extra regexes, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\&amp;lt;base\&amp;gt; + "/\*(?P\&amp;lt;variation\_id\&amp;gt;[^/]\*)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice in our get_urls method, we call super() before we create our nested URLs. super().get_urls() calls self.get_lookup_regex(viewset) which constructs our variation_ids before constructing our nested URLs (i.e. those ending with &amp;lt;resource_2&amp;gt;).&lt;/p&gt;

&lt;p&gt;The call to super() creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;itineraries/2222 itineraries/2222/3333
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While making our nested URLs, we append these urls, also creating:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;itineraries/2222/itinerary\_maps itineraries/2222/3333/itinerary\_maps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By constructing our regex orderly, we are able to create these custom URLs and bind them to the appropriate views.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Routing our backends&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I mentioned backends a few times in this article, but never formally defined them. A backend is what we call one of our internal systems that provides data into the API. Our reservation system provides bookings, our Salesforce piece provides contact data, our operations tool provides tour data and so on. This means our API must know which backend(s) to use for which resource. We did this by defining a set of backends and binding resources (example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Backend: config\_variable = 'B1'

def request: \&amp;lt;initializes a request with the system\&amp;gt; def get\_object: \&amp;lt;uses request to get an object\&amp;gt; def update\_object: \&amp;lt;uses request to update an object\&amp;gt; def create\_object: \&amp;lt;uses request to create an object\&amp;gt;

class Resource: object\_backends = [(READ\_WRITE, 'B1'), (READ\_ONLY, 'B2')]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above declares Resource to read and write from B1 and then use B2. Notice the order, B2 will actually override data from B2 if fields collide. Now we just need to patch DRF to use this structure, and this is easy. Here is just the function to override: &lt;a href="https://github.com/encode/django-rest-framework/blob/7078afa42c1916823227287f6a6f60c104ebe3cd/rest_framework/generics.py#L77" rel="noopener noreferrer"&gt;https://github.com/encode/django-rest-framework/blob/7078afa42c1916823227287f6a6f60c104ebe3cd/rest_framework/generics.py#L77&lt;/a&gt; . We did the same for update_object and create_object, allowing us to select backends for resources. Our Flask application operated the same way, but the code for this functionality was much more complex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now and future of the API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our DRF framework has been serving all resources for over a year now and we could not be happier. While Flask served us well initially, we eventually found ourselves reinventing the wheel. With Flask, all the API basics had to be done from scratch. With DRF, we get basic functionalities and an easy way to add our own. A structured framework allowed us to focus on the actual API while allowing us to slightly customize the framework. DRF is just flexible enough to give us endless possibilities. We are currently customizing the API to route different requests to different reservation systems, stay tuned!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Keep following us on&lt;/em&gt; &lt;a href="https://tech.gadventures.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Medium&lt;/em&gt;&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; &lt;a href="https://twitter.com/gadventurestech" rel="noopener noreferrer"&gt;&lt;em&gt;Twitter&lt;/em&gt;&lt;/a&gt; &lt;em&gt;for more snippets of the Tech world according to G!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to help G Adventures change people’s lives and travel the world?&lt;/em&gt; &lt;a href="https://gadventures.com/careers" rel="noopener noreferrer"&gt;&lt;em&gt;Check out all our jobs and apply today&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>api</category>
      <category>softwaredevelopment</category>
      <category>django</category>
      <category>python</category>
    </item>
    <item>
      <title>Why we created a REST API at G Adventures</title>
      <dc:creator>Adam McKerlie</dc:creator>
      <pubDate>Wed, 11 Apr 2018 13:43:55 +0000</pubDate>
      <link>https://forem.com/gadventurestech/why-we-created-a-rest-api-at-g-adventures-1imf</link>
      <guid>https://forem.com/gadventurestech/why-we-created-a-rest-api-at-g-adventures-1imf</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RwOQ2-X7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A8yz3hkO3ynHV2qYGARynJg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RwOQ2-X7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A8yz3hkO3ynHV2qYGARynJg.jpeg" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/photos/b18TRXc8UPQ?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Artem Sapegin&lt;/a&gt; on &lt;a href="https://unsplash.com/search/photos/internet?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a senior dev at G Adventures, I’ve had the great opportunity to work on the &lt;a href="https://developers.gadventures.com"&gt;REST API&lt;/a&gt; here. This post is the first of many detailing how we got to where we are.&lt;/p&gt;

&lt;p&gt;Our core business at G Adventures is small group travel, a social enterprise that strives to show the world to our travellers and in turn improve both the lives of our travellers and the lives of those who live at the destinations we visit.&lt;/p&gt;

&lt;p&gt;As one can imagine lots goes into planning a tour on the other side of the planet to ensure everything goes smoothly from a day one. As our business grew so did our various systems. Eventually, it was necessary to have one common interface (or one common language) that our systems could use to talk to each other. We chose REST API which stands for Representational State Transfer and API stands for Application Programming Interface.&lt;/p&gt;

&lt;p&gt;We use this interface to expose our business systems to developers both internal and external. As an example&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jS0hIrM1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AdHoovjeEG3t9rs4hUc8GoQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jS0hIrM1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AdHoovjeEG3t9rs4hUc8GoQ.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;contains all kinds of useful details about our &lt;a href="https://www.gadventures.com/trips/peru-panorama/PPP/"&gt;Peru Panorama&lt;/a&gt; tour, including things such as tour’s itinerary or links to specific departures.&lt;/p&gt;

&lt;p&gt;This means of communication has been a huge factor for our business growth as it allows our partners to integrate with us in a much more cost-effective way (in the past booking a tour with us required the use of a specialized website and often even a phone call). With REST API it’s a few HTTP requests instead.&lt;/p&gt;

&lt;p&gt;Internally it allowed us to integrate our various systems and hide implementation details so that application developers no longer have to have intimate knowledge of every single system that we have. Once a developer becomes familiar with the REST API they can use any of our systems in uniform fashion whether they are interacting with our inventory management system or our customer management system (CRM), or anything else we expose via API.&lt;/p&gt;

&lt;p&gt;The existence of this uniform interface even allowed whole new applications previously not possible. Such as our automated email engine or universal &lt;a href="https://developers.gadventures.com/docs/searching.html"&gt;search functionality&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Not long after we launched the API did we discover the need to also be aware of when things change. E.g. if the above Peru Panorama trip no longer starts in Lima, we need to notify the website, our booking engine, all our API clients, who often display the trip on their own websites.&lt;/p&gt;

&lt;p&gt;One way to do this is for every developer to check the above URL at specific intervals (polling). But this isn’t really a good strategy because we run around a thousand different tours with most of them having at least one departure every 10 days. That is a lot of URLs to poll.&lt;/p&gt;

&lt;h3&gt;
  
  
  Webhooks (don’t call us we will call you)
&lt;/h3&gt;

&lt;p&gt;Webhooks can be compared to push notifications and are a simple mechanism to inform our API users when something in our ecosystem changes. In other words, rather than our users always checking the above URL to see if anything changed we will let everyone subscribed know if there are changes to the Peru Panorama tour.&lt;/p&gt;

&lt;p&gt;These webhooks enable a universal mechanism of notifications that various business units use to obtain relevant info as soon as possible. Side effects included us being able to keep a replica of our data that we used for uniform searching and caching to increase functionality and responsiveness of our API.&lt;/p&gt;

&lt;p&gt;Our external partners can use the webhooks to observe our inventory, watch for special deals or be immediately informed when anything happens on a booking they made with us.&lt;/p&gt;

&lt;p&gt;I am proud of what we were able to accomplish with our API, but in my next installment I’ll get more technical as the story of REST API at G Adventures is far from finished.&lt;/p&gt;

&lt;p&gt;Stay tuned.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Keep following us on&lt;/em&gt; &lt;a href="https://tech.gadventures.com/"&gt;&lt;em&gt;Medium&lt;/em&gt;&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; &lt;a href="https://twitter.com/gadventurestech"&gt;&lt;em&gt;Twitter&lt;/em&gt;&lt;/a&gt; &lt;em&gt;for more snippets of the Tech world according to G!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to help G Adventures change people’s lives and travel the world?&lt;/em&gt; &lt;a href="https://gadventures.com/careers"&gt;&lt;em&gt;Check out all our jobs and apply today&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>restapi</category>
      <category>travel</category>
      <category>softwaredevelopment</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Deploying Code at G Adventures</title>
      <dc:creator>Adam McKerlie</dc:creator>
      <pubDate>Mon, 22 Jan 2018 13:35:32 +0000</pubDate>
      <link>https://forem.com/gadventurestech/deploying-code-at-g-adventures-37mh</link>
      <guid>https://forem.com/gadventurestech/deploying-code-at-g-adventures-37mh</guid>
      <description>&lt;h4&gt;
  
  
  At G Adventures we deploy 20+ times a day to many different services that handle millions of requests and millions of dollars in transactions by keeping it simple
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F700%2F1%2ASl6x_lfzl-ith40tfP7Njw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F700%2F1%2ASl6x_lfzl-ith40tfP7Njw.png"&gt;&lt;/a&gt;&lt;a href="https://thenounproject.com/everydaytemplate/" rel="noopener noreferrer"&gt;Danil Polshin&lt;/a&gt; from the Noun Project&lt;/p&gt;

&lt;p&gt;One of my guiding principles for developing and deploying software has always been to keep it as simple as possible for as long as possible. Premature optimization wastes valuable developer time, solving problems that may never be real. One of the areas where this is apparent at G Adventures is how we deploy code. Today I’m going to talk about the common ways we deploy code, the pros and cons of each way and what the future may look like for us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment
&lt;/h3&gt;

&lt;p&gt;At G, we pride ourselves in using the right tool for the job. While we’re primarily a Python shop, using Django for our websites, we also have a few services written in Go and have started building out our front ends using React. Each environment comes with different challenges and best practices when it comes to deployment, which I’ll describe these below.&lt;/p&gt;

&lt;p&gt;We use &lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; to host our code, &lt;a href="https://jenkins-ci.org/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt; and &lt;a href="https://travis-ci.com/" rel="noopener noreferrer"&gt;Travis CI&lt;/a&gt; to automate our tests, and right now most of our applications and code live in VMs in a Colo just outside of Toronto. We’re in the process of transitioning to AWS but I’ll leave that for another post.&lt;/p&gt;

&lt;p&gt;Other common technologies we use are &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt; (primary DBs), &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; (cache and key/value store), &lt;a href="https://www.rabbitmq.com/" rel="noopener noreferrer"&gt;RabbitMQ&lt;/a&gt; (messaging broker) and &lt;a href="http://www.celeryproject.org/" rel="noopener noreferrer"&gt;Celery&lt;/a&gt; (task queue). In future blog posts, we’ll break down the individual teams and stacks.&lt;/p&gt;

&lt;p&gt;Historically each team developed their own processes around code reviews, migrations and deployments. As we’ve grown we’ve made an effort to standardize our processes to make it easier for teams to share developers and resources. It’s still apparent in how we deploy with each team doing it slightly differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Current Deploy Strategies
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Fabric + Git Pull&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This is our simplest and oldest method of deploying our applications. It uses &lt;a href="http://www.fabfile.org/" rel="noopener noreferrer"&gt;Fabric&lt;/a&gt;, a command line tool for simplifying SSH connections and &lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;git&lt;/a&gt; to update the code on our servers and deploy a new version of our applications.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The above gist is pretty close to what we do in production for our Django applications. We do have some code that determines which environment to deploy to as well as posting the status of deploys to Slack.&lt;/p&gt;

&lt;p&gt;As you can see this deploy strategy is extremely simple and has worked across multiple teams for many years. The simplicity allows us to deploy new applications quickly and confidently and bring new employees up to date and deploying their changes within the first week of them starting.&lt;/p&gt;

&lt;p&gt;There are some downsides to such a simple deploy process. Server, GitHub and PyPI connection issues can leave the application in an inconsistent state. In theory, running multiple deploys at the same time could cause migration issues. In practice, we rarely run into connection issues and because we post the status of deploys to a slack channel all it takes is for a developer to check that other deploys have finished before starting their own.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Symlink&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;An approach we’ve taken more recently is to build out a new environment entirely on each of the machines and then update the symlinks Nginx uses to serve the content. This allows us to deploy and verify that the application is in a consistent state before switching the symlinks and restarting the application servers.&lt;/p&gt;

&lt;p&gt;Below is an example of how we deploy one of our React applications.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This deploy is almost as simple as the Git Pull method but adds an additional step of cloning into a new folder (e.g. app-2018010410224 ) and only switching the symlink once it’s been deployed across all of our servers.&lt;/p&gt;

&lt;p&gt;The main benefit to this strategy is that if any of the requirements fail (connecting to a server, cloning the repo, building the application) across any of the servers the entire deploy will halt and production won’t be updated. This has been extremely useful as we’ve started moving to AWS and noticed an increase in connection timeouts. Rolling back deploys is also easier with this strategy. You simply update the symlinks to a previous working deploy and restart Nginx.&lt;/p&gt;

&lt;p&gt;There are still a couple of downsides to this deploy strategy. Servers can still have different dependencies if you don’t lock down your pip requirements or node requirements and you need to manage how many past deployments you keep on the server or you can potentially run into using up the entire disk space. For a React application, you’re also still installing all of your node modules on each server which not only takes a long time but also uses a lot of disk space. This can be easily fixed by building locally and shipping off the application to each server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Future
&lt;/h3&gt;

&lt;p&gt;As you can see from the above examples we’ve been able to deploy tens of thousands of times by using simple deployment strategies. As we start to push past 50 developers we’re looking at ways to improve how we deploy code.&lt;/p&gt;

&lt;p&gt;The biggest thing we’re looking at right now is continuous deployment after all of our tests are run. We already have Continuous Integration for all of our applications so the next logical step is to bundle up the environment, requirements and code and ship it off after the tests pass successfully. We’re also looking at containerizing all of our applications to help us manage deployment (we’ve just started looking at this).&lt;/p&gt;

&lt;p&gt;How do you test and deploy your code? What strategies are you most comfortable with? We’d love to hear from you down in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Keep following us on&lt;/em&gt; &lt;a href="https://tech.gadventures.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Medium&lt;/em&gt;&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; &lt;a href="https://twitter.com/gadventurestech" rel="noopener noreferrer"&gt;&lt;em&gt;Twitter&lt;/em&gt;&lt;/a&gt; &lt;em&gt;for more snippets of the Tech world according to G!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to help G Adventures join our growing team and travel the world?&lt;/em&gt; &lt;a href="https://gadventures.com/careers" rel="noopener noreferrer"&gt;&lt;em&gt;Check out all our jobs and apply today&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://tech.gadventures.com/deploying-code-at-g-adventures-577678a145c2" rel="noopener noreferrer"&gt;tech.gadventures.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>ci</category>
      <category>python</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Translating React Apps</title>
      <dc:creator>Adam McKerlie</dc:creator>
      <pubDate>Wed, 27 Sep 2017 20:40:28 +0000</pubDate>
      <link>https://forem.com/gadventurestech/translating-react-apps-7h0</link>
      <guid>https://forem.com/gadventurestech/translating-react-apps-7h0</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://tech.gadventures.com/translating-react-apps-99dede52d924" rel="noopener noreferrer"&gt;tech.gadventures.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Imagine you’re in a Roman train station in the middle of the night, and nobody’s around. You need to purchase a ticket to Venice. The ticketing software is in Italian. You don’t speak Italian. After a bit of a struggle, you manage to get a ticket. You get on the train. More Italian. A 3 hour trip. You look at the welcome sign. â€˜Welcome to Florence’. Sigh. Florence is not Venice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F850%2F1%2ApqLmZeeFn8P24_4eOOSdsA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F850%2F1%2ApqLmZeeFn8P24_4eOOSdsA.jpeg"&gt;&lt;/a&gt;Roma Termini TrainÂ Station&lt;/p&gt;

&lt;p&gt;In the ideal world, we’d all speak many languages, like some of our &lt;a href="http://ec.europa.eu/commfrontoffice/publicopinion/archives/ebs/ebs_243_en.pdf" rel="noopener noreferrer"&gt;European travelers&lt;/a&gt;. But not everyone understands English, so it’s better to support as many languages as possible.&lt;/p&gt;

&lt;p&gt;As some of you may know, G has a travel app called Good to Go. There are certain requirements you may need to complete before you travel. These include sending us a copy of your visa, your jacket size, your food preference, etcâ€Š–â€Šwe refer to these as “trip requirements”. Before travelers start their adventure, Good to Go helps them make sure they have all the things they need before they travel.&lt;/p&gt;

&lt;p&gt;Thousands of travelers use it to complete their trip requirements before they begin. &lt;em&gt;Every single one of our departures goes through Good to Go.&lt;/em&gt; It’s a big star. In fact, G-Stock 23 marks the 1 year anniversary of Good to Go. We continue to work on it and make it better every single day.&lt;/p&gt;

&lt;p&gt;Last month, we brought German support to Good to Go, which is written in React. It was pretty easy thanks to &lt;a href="https://github.com/yahoo/react-intl" rel="noopener noreferrer"&gt;Yahoo’s React-Intl library&lt;/a&gt;. This post details my experience adding internationalization to Good to Go.&lt;/p&gt;

&lt;p&gt;Firstly, when it comes to translating, the order of the words is important, as well as the formatting of dates and names. Grouped together, these things are known as “locale data”.&lt;/p&gt;

&lt;p&gt;Installing react-intl is &lt;a href="https://github.com/yahoo/react-intl/wiki#the-react-intl-package" rel="noopener noreferrer"&gt;simple enough&lt;/a&gt;, but there are a few additional steps you need to complete after the initial yarn add react-intl. Let’s go over them now.&lt;/p&gt;

&lt;h4&gt;
  
  
  Babel
&lt;/h4&gt;

&lt;p&gt;I’m going to illustrate how to do this with Babel and Webpack. First, install the babel-plugin-react-intl plugin (yarn add babel-plugin-react-intl). It will extract the strings marked for translation. To activate the plugin, you have to then add its name to your .babelrc file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"plugins": [
    ...,
    ["react-intl", {
        "messagesDir": "./src/translations",
        "enforceDescriptions": false
    }],
    ...
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, react-intl will extract tagged messages into src/translations. The messages are stored in JSON files, inside directories that are named after your components.&lt;/p&gt;

&lt;p&gt;When it’s time to send the messages to a translation service, you will probably want all of them in a single file. For this reason, the creators of the library have &lt;a href="https://gist.githubusercontent.com/iam-peekay/5a4e9431c9c785d3e62e584503619ecc/raw/65c6b5174652e31f5446b1fd0d70ece3ae1d0b9b/reactintl9.js" rel="noopener noreferrer"&gt;written a script&lt;/a&gt; that you can use to combine all the strings into a single file. To see a more detailed example, &lt;a href="https://medium.freecodecamp.org/internationalization-in-react-7264738274a0" rel="noopener noreferrer"&gt;see this post.&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Locale Data
&lt;/h4&gt;

&lt;p&gt;The locale data for each language is provided and imported independently so you’re only sending down what you need. In the following example, we’re importing the locale data for English and German.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { addLocaleData } from 'react-intl'
import enLocaleData from 'react-intl/locale-data/en'
import deLocaleData from 'react-intl/locale-data/de'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the process is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the library&lt;/li&gt;
&lt;li&gt;Install the babel plugin&lt;/li&gt;
&lt;li&gt;Import the locale data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, you wrap your root component with IntlProvider, so that all the translated messages and locale data can be loaded into the React Intl Components used by your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;IntlProvider locale="de" messages={messages}&amp;gt;
     &amp;lt;Root /&amp;gt;
&amp;lt;/IntlProvider&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another method is to inject these values right into the intl reducer during the “boot up part of your app’s lifecycle, like index.js or a configureStore method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    ...,
    intl: {
        defaultLocale: 'en',
        locale: 'en',
        messages: allMessages.en
    },
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Formatting with React-Intl
&lt;/h4&gt;

&lt;p&gt;The heart of react-intl is its tagging system. You can tag (or mark) strings for translation by wrapping them in a Rect component or using the API.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A React Component
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { FormattedMessage } from 'react-intl'

&amp;lt;FormattedMessage
    id="header.selectPax"
    defaultMessage="Select the traveller for whom you'd like to enter requirement information." 
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the component is straight forward, and powerful. For example, they can take values, illustrated in this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;FormattedMessage
    id="form.name"
    defaultMessage="{name}"
    values={{ name }} 
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The object you provide to values can be another FormattedMessage, meaning you can string them along like a chain to create a message with HTML tags!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;FormattedMessage
    id="body.text"
    defaultMessage="{bodyText}"
    values={{
        bodyText:
            &amp;lt;div&amp;gt;
                &amp;lt;h2&amp;gt;
                    &amp;lt;FormattedMessage 
                        id="bodyText.1"
                        defaultMessage="Ready for a G Adventure?" /&amp;gt;
                &amp;lt;/h2&amp;gt;
            &amp;lt;/div&amp;gt;
    }} 
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;API
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineMessages } from 'react-intl'

const formButtons = defineMessages({
    login: {
        id: 'formButtons.login',
        defaultMessage: 'Login'
    }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the API, you define your message ahead of time in a simple Javascript object.&lt;/p&gt;

&lt;p&gt;The API provides you with translated strings in places where a React component would not be appropriate. For example, inside of a &amp;lt;button&amp;gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { injectIntl } from 'react-intl'

const myForm = ({ intl }) =&amp;gt; {
    &amp;lt;button
        type="submit"
        onClick={handleSubmit}&amp;gt;
        {intl.formatMessage(formButtons.login)}
    &amp;lt;/button&amp;gt;
}

export default injectIntl(Waiver)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ve seen two ways you can use the react-intl library to translate your strings. I recommend using it everywhere. You will delight your users when they see your content in their native language.&lt;/p&gt;

&lt;p&gt;Look out for Part 2 of this series on translation, where we go over how to build out a Language Picker that uses react-intl.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Keep following us on&lt;/em&gt; &lt;a href="https://tech.gadventures.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Medium&lt;/em&gt;&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; &lt;a href="https://twitter.com/gadventurestech" rel="noopener noreferrer"&gt;&lt;em&gt;Twitter&lt;/em&gt;&lt;/a&gt; &lt;em&gt;for more snippets of the Tech world according to G!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Want to work at G Adventures? Join our growing team and travel the world! &lt;a href="https://gadventures.com/careers" rel="noopener noreferrer"&gt;&lt;em&gt;Check out all our jobs and apply today&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>softwaredevelopment</category>
      <category>react</category>
      <category>tech</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
