<?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: Tom Houlé</title>
    <description>The latest articles on Forem by Tom Houlé (@tomhoule).</description>
    <link>https://forem.com/tomhoule</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%2F15107%2F7a485cd1-a1b3-4268-819b-5546c7e99480.png</url>
      <title>Forem: Tom Houlé</title>
      <link>https://forem.com/tomhoule</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tomhoule"/>
    <language>en</language>
    <item>
      <title>What changed in our stack at store2be in 2018 - A retrospective</title>
      <dc:creator>Tom Houlé</dc:creator>
      <pubDate>Tue, 05 Feb 2019 14:53:55 +0000</pubDate>
      <link>https://forem.com/tomhoule/what-changed-in-our-stack-at-store2be-in-2018---a-retrospective-4h9</link>
      <guid>https://forem.com/tomhoule/what-changed-in-our-stack-at-store2be-in-2018---a-retrospective-4h9</guid>
      <description>&lt;p&gt;As our team and codebase grow, we sometimes find the need to cast a look back at how things changed, and remember fondly the code we wrote and deleted, the abstractions we built and scrapped, how our code used to be, and how much fun it was to get it to where it is now.&lt;/p&gt;

&lt;p&gt;We are building a platform to make live marketing scalable, measurable and targeted — as such we have multiple apps for the various actors of the platform and one API.  We want to make sure we can keep growing and adapting the platform to changing needs, maintaining agility and stability in the process.  This is our high-level mindset when making decisions regarding our tech stack.  This post is a recap of how it has worked for us in 2018.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhpfe64rk6thzrz1qyuq8.jpg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhpfe64rk6thzrz1qyuq8.jpg" alt="store2be developer, early 2018"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;store2be developer, early 2018 (photo © Tambako The Jaguar, flickr)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend - the React apps
&lt;/h2&gt;

&lt;p&gt;We introduced Typescript in our React apps at the end of 2017, and we are only now getting close to completely typed frontend apps. Before Typescript, we had suffered from churn in our JS architecture as we experimented with more scalable approaches to state management and asynchronous actions - we had our own abstractions on top of &lt;a href="https://github.com/redux-saga/redux-saga" rel="noopener noreferrer"&gt;redux-saga&lt;/a&gt; and even open sourced our way to deal with boilerplate in redux, &lt;a href="https://github.com/store2be/redux-belt/" rel="noopener noreferrer"&gt;redux-belt&lt;/a&gt;. These abstractions were constantly evolving, and it was a challenge to evolve core abstractions in very stateful apps without introducing type errors which translated directly to crashes for our users. There are enough Typescript success stories on the internet, so I will not elaborate too much on this.&lt;/p&gt;

&lt;p&gt;Good tooling is something we appreciate, so in addition to Typescript we adopted &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;prettier&lt;/a&gt;, &lt;a href="https://github.com/palantir/tslint/" rel="noopener noreferrer"&gt;TSLint&lt;/a&gt; (in addition to ESLint), &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;jest&lt;/a&gt; (replacing jasmine), and &lt;a href="https://jestjs.io/docs/en/snapshot-testing.html" rel="noopener noreferrer"&gt;snapshot testing&lt;/a&gt;. All of these lifted a lot of mental burden: no more worrying about or manually fiddling with code formatting, automatically enforced conventions, more certainty that the UI does not change by surprise, etc. Linting, testing and a lot more is enforced in CI so we can focus on the hard problems during code reviews.&lt;/p&gt;

&lt;p&gt;We liked the experience so much we implemented a backend service for email templating in typescript and &lt;a href="https://tech.store2be.com/email/sendwithus/mjml/typescript/react/2018/06/14/email-templates-at-store2be-and-gdpr/" rel="noopener noreferrer"&gt;wrote about it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another notable and much-appreciated addition to our toolbox was &lt;a href="https://github.com/storybooks/storybook" rel="noopener noreferrer"&gt;react-storybook&lt;/a&gt; for our shared UI components.&lt;/p&gt;

&lt;h2&gt;
  
  
  An unexpected journey - GraphQL in our apps
&lt;/h2&gt;

&lt;p&gt;Despite the numerous changes to the frontend workflow, architectural issues were still making our lives difficult.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Our RESTful API structure had not evolved much: most of our endpoints were mapping directly to database tables, with little additional logic.  With more resources and more complex business logic, that meant &lt;strong&gt;a lot of business logic had to be implemented in the frontend&lt;/strong&gt; and merely validated by the API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State management and synchronization in the React apps was getting too complex&lt;/strong&gt; - some operations and queries required several network calls and dispatching multiple redux actions. Some critical operations were not atomic, when they should have been.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding new developers to our redux-saga based abstractions&lt;/strong&gt; and all the related conventions &lt;strong&gt;proved challenging&lt;/strong&gt;, as documentation was not as comprehensive as it should have been and old conventions coexisted with new ones.&lt;/li&gt;
&lt;li&gt;With most of the data in our apps coming from the API, we felt that manually writing Typescript interfaces for all these resources was a lot of error prone manual work. We thought about generating them from our &lt;a href="https://swagger.io/specification/" rel="noopener noreferrer"&gt;OpenAPI/Swagger&lt;/a&gt; documentation, but the experiment wasn't very conclusive. Coupled with the type information loss from the redux-saga generators, we felt that we were &lt;strong&gt;not realizing Typescript's potential&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation was maintained manually as a Swagger specification&lt;/strong&gt;. This was also a lot of error-prone, manual work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Faced with these problems, we decided to try and address them by progressively introducing a new GraphQL API, in the same Rails codebase as the existing RESTful API. Here is what we observed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The additional layer gave us more room and structure to &lt;strong&gt;move business logic back to the backend&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data fetching in the frontend is now a lot less complex&lt;/strong&gt;, with queries mapping more or less 1-to-1 with views, and mutations to forms and buttons.  We are not using any redux-based abstraction for CRUD anymore, and the overall amount of state management code has decreased. State is (or appears) also more localized to where it belongs in the component tree. We found this easier to reason about.&lt;/li&gt;
&lt;li&gt;The simplicity, but also the adherence to a well-adopted and documented standard for data fetching made &lt;strong&gt;onboarding easier&lt;/strong&gt;. There are a lot of good GraphQL resources for frontend developers (&lt;a href="https://graphql.org/learn/" rel="noopener noreferrer"&gt;the official guide&lt;/a&gt;, to mention just one). In our experience picking up the technology was easy for everyone.&lt;/li&gt;
&lt;li&gt;The tooling is amazing, it lets us &lt;strong&gt;leverage Typescript's strengths by generating precise types for our API data&lt;/strong&gt;. We use apollo-client and apollo-codegen, they are well-documented, worked fine and the caching layer is the cherry on top.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Our API documentation is now in much better shape&lt;/strong&gt;, because most of it is automated, keeping it in sync with the implementation is trivial, and &lt;a href="https://github.com/graphql/graphiql" rel="noopener noreferrer"&gt;GraphiQL&lt;/a&gt; offers a good interface to explore it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It took some time to have a viable minimum implementation, but once this was deployed, it became clear very quickly that we wanted to go in that direction.&lt;/p&gt;

&lt;p&gt;Two more pain-points that we did not identify before migrating, but improved as a result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debuggability&lt;/strong&gt;. &lt;a href="https://github.com/graphql/graphiql" rel="noopener noreferrer"&gt;GraphiQL&lt;/a&gt; has been a boon to identify where errors come from.  With multiple requests that cannot be reproduced easily, it was sometimes hard to distinguish between backend and frontend issues (the network tools do not let you tweak and reproduce the requests easily). With queries you can share directly as a link — &lt;a href="https://github.com/graphql/graphiql" rel="noopener noreferrer"&gt;GraphiQL&lt;/a&gt; can take queries from URL query parameters — we saved a lot of debugging time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evolvability&lt;/strong&gt;. The way fields can be added and deprecated progressively has radically decreased the frequency of necessary breaking changes and made deployment smoother as a result. We use &lt;a href="https://github.com/apollographql/eslint-plugin-graphql" rel="noopener noreferrer"&gt;eslint-plugin-graphql&lt;/a&gt; to warn us when an app is using a field that has been deprecated.&lt;/li&gt;
&lt;/ul&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhejwkjhf60nqknyagmyf.jpg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhejwkjhf60nqknyagmyf.jpg" alt="store2be developer discovering GraphiQL"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;store2be developer discovering GraphiQL (photo © unknown)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL in our Rails API
&lt;/h2&gt;

&lt;p&gt;Adopting GraphQL was, in retrospect, an easy win in the frontend. It did require some changes, but towards a more standard workflow with a wide array of tools to make our lives easier. It was not the same in the API. GraphQL APIs pose a different set of problems and require different solutions, compared to purely RESTful APIs. To list a few of the challenges we faced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code organization&lt;/strong&gt;. We naturally started by separating the API we expose to users and the one we use for our internal tools, since we had separate sets of endpoints. It seemed like the easiest way to separate what admins and normal users should see and be able to do, and code-sharing would still be possible. For a variety of reasons, this turned out to be a bad decision and we ended up merging the two APIs after a few months. This is only one example of the difficulties we had with proper code organization and reuse in the GraphQL API. We now have a good set of conventions and abstractions but it took a fair amount of exploration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;. Some of the abstractions in &lt;a href="http://graphql-ruby.org/" rel="noopener noreferrer"&gt;graphql-ruby&lt;/a&gt; are not very friendly to test, so we had to come up with a test-friendly code structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limiting access and visibility&lt;/strong&gt;. Nothing particularly challenging here, but this also needs to be learned.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proper use of more "advanced" features like interfaces&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoiding the n+1 query problem&lt;/strong&gt; - there is one well-supported, well-documented and straightforward solution, &lt;a href="https://github.com/Shopify/graphql-batch" rel="noopener noreferrer"&gt;graphql-batch&lt;/a&gt; (by Shopify), but this is one more thing to learn and implement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It took us a few months to settle on satisfactory abstractions. Resources like the &lt;a href="https://github.com/Shopify/graphql-design-tutorial" rel="noopener noreferrer"&gt;Shopify GraphQL design tutorial&lt;/a&gt; were helpful. In general, having someone with prior knowledge or significant interest/experience advocating GraphQL good practices and tools helps.&lt;/p&gt;

&lt;p&gt;Now that the abstractions are well defined, we find it easier to onboard developers to the API, and we work more with plain ruby classes instead of Rails-specific methods in models and controllers.&lt;/p&gt;

&lt;p&gt;In short, the change was received enthusiastically by the whole team once the initial hurdles were behind us, and we are very happy with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backend and infrastructure - events, k8s
&lt;/h2&gt;

&lt;p&gt;We started implementing an event log for our platform, for auditing, debugging and in the future for decoupling tasks. The primary feature at the moment is a timeline in our internal app, but it opens up a lot of perspectives and challenges for 2019.&lt;/p&gt;

&lt;p&gt;Another change we have been working on is the migration to Kubernetes. We strictly adhere to &lt;em&gt;infrastructure as code&lt;/em&gt; and have all of our infrastructure defined as terraform scripts and helm charts. We want to complete the migration this year. We discovered a lot of tools on the way, but one fun tool that bears mentioning is &lt;a href="https://github.com/databricks/click" rel="noopener noreferrer"&gt;click&lt;/a&gt;; it acts as a kind of REPL/terminal UI for your kubernetes cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Handbook
&lt;/h2&gt;

&lt;p&gt;At the end of 2017 our team was still at a scale where every developer had a relatively complete knowledge of the codebase in their head, but this could not last. We started and grew our internal development team documentation, what we call the Tech Handbook. It takes the shape of a GitHub repository with simple markdown files — with indexes and ToCs — so the editing workflow is comfortable for everyone. We discovered that as the codebase grows, this is not a nice-to-have but a necessity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sign-off
&lt;/h2&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdyms2tecc4eucy0d8vdi.jpg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdyms2tecc4eucy0d8vdi.jpg" alt="store2be developer, early 2019"&gt;&lt;/a&gt; &lt;em&gt;store2be developer, early 2019 (photo © HuffingtonPost)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We made rather dramatic changes to our stack in 2018 and did not compromise the delivery of actual features in the process. There was no hard, breaking change, migration or rewrite, only improvements. Let us hope the same goes for 2019.&lt;/p&gt;

&lt;p&gt;If you would like to know more, feel free to reach out to us on &lt;a href="https://twitter.com/store2be_tech" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hi there, we’re store2be, a Berlin based startup that builds a SaaS enabled marketplace for short term retails space. If you like what we are posting you might wanna check out the store2be tech page or follow our Medium channel.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>stack</category>
      <category>graphql</category>
      <category>api</category>
      <category>react</category>
    </item>
    <item>
      <title>graphql_client 0.6, to the browser and beyond</title>
      <dc:creator>Tom Houlé</dc:creator>
      <pubDate>Sun, 30 Dec 2018 14:11:26 +0000</pubDate>
      <link>https://forem.com/tomhoule/graphqlclient-06-to-the-browser-and-beyond-3cb8</link>
      <guid>https://forem.com/tomhoule/graphqlclient-06-to-the-browser-and-beyond-3cb8</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/graphql-rust/graphql-client" rel="noopener noreferrer"&gt;graphql_client&lt;/a&gt; is getting a new release today! The most visible new feature is a new &lt;a href="https://github.com/graphql-rust/graphql-client/tree/master/graphql_client_web" rel="noopener noreferrer"&gt;graphql_client_web&lt;/a&gt; crate that makes calling a GraphQL API from Rust code running in browsers boilerplate-free.&lt;/p&gt;

&lt;p&gt;Another notable change is the move into the &lt;a href="https://github.com/graphql-rust" rel="noopener noreferrer"&gt;graphql-rust&lt;/a&gt; organization on GitHub, where we can pool documentation and branding efforts with the other GraphQL crates. Thanks to the graphql-rust team for their support with and feedback on graphql_client!&lt;/p&gt;

&lt;h2&gt;
  
  
  Web client
&lt;/h2&gt;

&lt;p&gt;Calling a GraphQL API in WebAssembly code inside a browser is now as simple as this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add graphql_client_web as a dependency.&lt;/li&gt;
&lt;li&gt;Construct a client with &lt;code&gt;Client::new("&amp;lt;GraphQL endpoint URL&amp;gt;")&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Call it with a normal graphql_client query struct and the query variables struct.&lt;/li&gt;
&lt;li&gt;You get a Rust future that returns a strongly typed response or a client error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how it looks (copy-pasted directly from the test suite):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(GraphQLQuery)]&lt;/span&gt;
&lt;span class="nd"&gt;#[graphql(&lt;/span&gt;
    &lt;span class="nd"&gt;schema_path&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tests/countries_schema.json"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;query_path&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tests/Germany.graphql"&lt;/span&gt;
&lt;span class="nd"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[wasm_bindgen_test(async)]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_country&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://countries.trevorblades.com/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;country&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Variables&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;country_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"CN"&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map&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="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;continent_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
                &lt;span class="py"&gt;.data&lt;/span&gt;
                &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"response data is not null"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="py"&gt;.country&lt;/span&gt;
                &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"country is not null"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="py"&gt;.continent&lt;/span&gt;
                &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"continent is not null"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="py"&gt;.name&lt;/span&gt;
                &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"country is on a continent"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;continent_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Asia"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;panic!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&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;Network (via the fetch API) and serialization/deserialization are taken care of by graphql_client_web.&lt;/p&gt;

&lt;p&gt;This crate works with the wasm-bindgen ecosystem only (web-sys, js-sys), support for stdweb could be investigated if there is demand for it.&lt;/p&gt;

&lt;p&gt;This is a first release, so expect some minor breaking changes, especially around error handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  CLI
&lt;/h2&gt;

&lt;p&gt;@h-michael has been pushing for code generation using the CLI, and it is shaping up rapidly with new features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support for query documents defining multiple queries&lt;/li&gt;
&lt;li&gt;Optional formatting of the output code with rustfmt&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other fixes and improvements
&lt;/h2&gt;

&lt;p&gt;There were a few other new features, bug fixes and breaking (naming) changes. You can take a look at the &lt;a href="https://github.com/graphql-rust/graphql-client/blob/master/CHANGELOG.md" rel="noopener noreferrer"&gt;changelog&lt;/a&gt; for the full list of changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fyznas6zbllpzftsfef4w.jpg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fyznas6zbllpzftsfef4w.jpg" alt="reform the ninja empire"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The library is now in a very usable state, and we can start thinking about a 1.0 release. There are many enhancement ideas in the issues, but the core abstractions are stable, so we should not be too afraid to take that step once the documentation is in better shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;As always, many warm thanks to &lt;a href="https://github.com/graphql-rust/graphql-client#contributors" rel="noopener noreferrer"&gt;everyone who contributed&lt;/a&gt; to make the crate happen!&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>rust</category>
      <category>api</category>
      <category>webassembly</category>
    </item>
    <item>
      <title>Announcing graphql-client 0.5</title>
      <dc:creator>Tom Houlé</dc:creator>
      <pubDate>Sun, 07 Oct 2018 17:37:48 +0000</pubDate>
      <link>https://forem.com/tomhoule/announcing-graphql-client-05-56cm</link>
      <guid>https://forem.com/tomhoule/announcing-graphql-client-05-56cm</guid>
      <description>&lt;p&gt;Today's release has by far the most improvements since &lt;a href="https://github.com/graphql-rust/graphql-client"&gt;graphql-client&lt;/a&gt; was initially release. This is all thanks to those who started using and contributing to the library.&lt;/p&gt;

&lt;p&gt;In addition to the new features, the project on GitHub has moved from my personal account to the &lt;a href="https://github.com/graphql-rust"&gt;graphql-rust&lt;/a&gt; organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  What graphql_client does, in short
&lt;/h2&gt;

&lt;p&gt;Given a GraphQL query, and the schema of the API you are querying, the library generates a Rust module that gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Correct interaction (request/response shapes)&lt;/li&gt;
&lt;li&gt;Strongly typed responses&lt;/li&gt;
&lt;li&gt;Strongly typed query variables&lt;/li&gt;
&lt;li&gt;Some degree of validation (behaviour for deprecated fields is configurable for example)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I gave a short talk on the library at the Berlin Rust and Tell meetup this week (&lt;a href="http://www.tomhoule.com/talks/graphql-client-oct-2018/"&gt;slides&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  What is in 0.5
&lt;/h2&gt;

&lt;p&gt;0.4 was more than a month ago. Since then users and contributors have stepped up, reported bugs (which were fixed) and contributed a lot of awesome features. Here are a few highlights.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Aliases in queries are now supported. Beyond the ergonomics, this is an important feature since it allows to query the same field on an object multiple times with different arguments, as shown in the &lt;a href="https://graphql.org/learn/queries/#aliases"&gt;official guide&lt;/a&gt;. Thanks a lot to @mathstuf!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configurable behaviour when a query uses a field marked as deprecated in the schema. By default, you will get warnings, but you can configure the library to fail to compile if a query uses a deprecated field, or completely disable the warnings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;@h-michael has split the codegen out of the derive crate, to implement code generation via the CLI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For several classes of items that we generate, we only generate those that are actually used by the query. This way, you do not need to define mappings for &lt;em&gt;every&lt;/em&gt; scalar in the schema you are querying - even for small queries - anymore. This also improves compile times a lot in some scenarios. (#116)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Top-level exported types have been renamed. Types no longer have a &lt;code&gt;GraphQL&lt;/code&gt; prefix. e.g. &lt;code&gt;GraphQLQuery&lt;/code&gt; -&amp;gt; &lt;code&gt;Query&lt;/code&gt;, &lt;code&gt;GraphQLError&lt;/code&gt; -&amp;gt; &lt;code&gt;Error&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are just the highlights - there have been a lot of other significant performance and correctness improvements, and the corner-cases in the spec that we do not support are getting very rare. See the &lt;a href="https://github.com/graphql-rust/graphql-client/blob/master/CHANGELOG.md"&gt;changelog&lt;/a&gt; for the complete list.&lt;/p&gt;

&lt;p&gt;Very special thanks to @h-michael, @mathstuf, @legneato and &lt;a class="comment-mentioned-user" href="https://dev.to/scrogson"&gt;@scrogson&lt;/a&gt;
 for their work!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;The GraphQL specification is nearly completely supported, but there are minor points that need work.&lt;/p&gt;

&lt;p&gt;Here are a few exciting features we want to work on next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Currently code generation works with a custom derive, but we want to implement CLI and build script based flows as well. This should enable more code sharing, therefore reduced code size.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement platform-specific client libraries to further reduce boilerplate. The recent publication of the &lt;a href="https://rustwasm.github.io/2018/09/26/announcing-web-sys.html"&gt;web-sys&lt;/a&gt; crate enables the creation a browser-native client built on top of it - work has already started.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A normalized cache implementation similar to what is implemented by apollo-client.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real query validation, with better error messages. We are exploring sharing code with &lt;a href="https://github.com/graphql-rust/juniper"&gt;juniper&lt;/a&gt; since the spec specifies validation in detail.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>graphql</category>
      <category>rust</category>
      <category>webassembly</category>
      <category>api</category>
    </item>
    <item>
      <title>Fixing a silent logger with OSÂ debugging tools</title>
      <dc:creator>Tom Houlé</dc:creator>
      <pubDate>Fri, 25 Aug 2017 13:20:13 +0000</pubDate>
      <link>https://forem.com/tomhoule/fixing-a-silent-logger-with-osdebugging-tools</link>
      <guid>https://forem.com/tomhoule/fixing-a-silent-logger-with-osdebugging-tools</guid>
      <description>&lt;p&gt;One of the features we wanted in the recent push to release a &lt;a href="https://github.com/store2be/pape-rs/releases/tag/0.2.0"&gt;0.2Â version of Papers&lt;/a&gt; was debugging output uploaded to an S3 bucket for generated documents. One of the major things we want in that debug archive is the logs from the service itself - this means having a second sink for our logs on a per-document-generation basis, ideally with a higher log level. Fortunately, this is possible in a boilerplate-free way with &lt;a href="https://github.com/slog-rs/slog"&gt;slog&lt;/a&gt; 2.0, since loggers are now also sinks.&lt;/p&gt;

&lt;p&gt;So we decided to update to slog 2 and rework our logs configuration, which is just a few lines anyway. There is even a nice high level wrapper around slog called &lt;a href="https://github.com/sile/sloggers"&gt;sloggers&lt;/a&gt; for logging out to files or the terminal with nice colors.  &lt;/p&gt;

&lt;p&gt;It went very smoothly and the result was impeccable, but one does not simply containerize an app that logs to the terminal: in our test deployment, the service was running but there was absolutely no terminal output.&lt;/p&gt;

&lt;p&gt;After checking that yes, release binaries do log in the same docker image locally, and investigating several suspects, including fluentd and slog behaving differently in release builds (this is &lt;a href="https://docs.rs/slog/2.0.9/slog/#notable-details"&gt;documented behaviour&lt;/a&gt;), I resorted to one of my favourite debugging tools: &lt;a href="https://en.wikipedia.org/wiki/Strace"&gt;strace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;strace &amp;lt;your program&amp;gt;&lt;/code&gt; runs your program and prints whatever system calls it makes to the terminal, allowing you to inspect all IO operations. For Papers, you will get something like that (excerpt):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;open("/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0\340\5\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1985472, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5f6ca3d000
mmap(NULL, 3823824, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5f6b57b000
mprotect(0x7f5f6b718000, 2093056, PROT_NONE) = 0
mmap(0x7f5f6b917000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19c000) = 0x7f5f6b917000
mmap(0x7f5f6b91d000, 14544, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5f6b91d000
close(3)                                = 0
open("/usr/lib/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0`]\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1120216, ...}) = 0
mmap(NULL, 3215384, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5f6b269000
mprotect(0x7f5f6b37a000, 2093056, PROT_NONE) = 0
mmap(0x7f5f6b579000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x110000) = 0x7f5f6b579000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5f6ca3a000
arch_prctl(ARCH_SET_FS, 0x7f5f6ca3a8c0) = 0
mprotect(0x7f5f6b917000, 16384, PROT_READ) = 0
mprotect(0x7f5f6b579000, 4096, PROT_READ) = 0
mprotect(0x7f5f6bb36000, 4096, PROT_READ) = 0
mprotect(0x7f5f6bd50000, 4096, PROT_READ) = 0
mprotect(0x7f5f6bf5c000, 4096, PROT_READ) = 0
mprotect(0x7f5f6c160000, 4096, PROT_READ) = 0
mprotect(0x7f5f6c5b7000, 122880, PROT_READ) = 0
mprotect(0x7f5f6c842000, 20480, PROT_READ) = 0
mprotect(0x55873cd6f000, 1167360, PROT_READ) = 0
mprotect(0x7f5f6ca6f000, 4096, PROT_READ) = 0
munmap(0x7f5f6ca41000, 185208)          = 0
set_tid_address(0x7f5f6ca3ab90)         = 13360
set_robust_list(0x7f5f6ca3aba0, 24)     = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x7f5f6bb3d740, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f5f6bb497e0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x7f5f6bb3d7e0, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f5f6bb497e0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
readlink("/etc/malloc.conf", 0x7ffcda2ec870, 4096) = -1 ENOENT (No such file or directory)
brk(NULL)                               = 0x55873d340000
mmap(NULL, 2097152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5f6b069000
munmap(0x7f5f6b069000, 2097152)         = 0
mmap(NULL, 4190208, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5f6ae6a000
munmap(0x7f5f6ae6a000, 1662976)         = 0
munmap(0x7f5f6b200000, 430080)          = 0
open("/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC) = 3
read(3, "0-3\n", 8192)                  = 4
close(3)                                = 0
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f5f6b5ae940}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
mmap(NULL, 2097152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5f6ae00000
open("/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "55873c5e7000-55873cb6f000 r-xp 0"..., 1024) = 1024
read(3, "libc-2.25.so\n7f5f6b917000-7f5f6b"..., 1024) = 1024
read(3, "pthread-2.25.so\n7f5f6bd52000-7f5"..., 1024) = 1024
read(3, "so.1.1\n7f5f6c5b7000-7f5f6c5d5000"..., 1024) = 1024
read(3, "000-7ffcda2f0000 rw-p 00000000 0"..., 1024) = 316
close(3)                                = 0
sched_getaffinity(13360, 32, [0, 1, 2, 3]) = 16
mmap(0x7ffcd9af0000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffcd9af0000
rt_sigaction(SIGSEGV, {sa_handler=0x55873ca20260, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f5f6bb497e0}, NULL, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=0x55873ca20260, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f5f6bb497e0}, NULL, 8) = 0
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5f6ca6d000
sigaltstack({ss_sp=0x7f5f6ca6d000, ss_flags=0, ss_size=8192}, NULL) = 0
getcwd("/home/tom/src/papers", 512)     = 21
open("/home/tom/src/papers/.env", O_RDONLY|O_CLOEXEC) = 3
ioctl(3, FIOCLEX)                       = 0
read(3, "PAPERS_AWS_ACCESS_KEY=&amp;lt;REDACTED&amp;gt;"..., 8192) = 228
getrandom("", 0, GRND_NONBLOCK)         = 0
getrandom("\x3d\xfd\x3c\x08\x48\x6d\x03\x13", 8, GRND_NONBLOCK) = 8
getrandom("\x80\x11\x02\x43\x2e\xa6\x6f\x1b", 8, GRND_NONBLOCK) = 8
read(3, "", 8192)                       = 0
close(3)                                = 0
stat("/home/tom/.terminfo", 0x7ffcda2eb7d0) = -1 ENOENT (No such file or directory)
stat("/etc/terminfo", 0x7ffcda2eb7d0)   = -1 ENOENT (No such file or directory)
stat("/lib/terminfo", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/lib/terminfo/s/screen", {st_mode=S_IFREG|0644, st_size=1587, ...}) = 0
open("/lib/terminfo/s/screen", O_RDONLY|O_CLOEXEC) = 3
ioctl(3, FIOCLEX)                       = 0
read(3, "\32\1*\0+\0\20\0i\1z\2screen|VT 100/ANSI X"..., 8192) = 1587
close(3)                                = 0
stat("/home/tom/.terminfo", 0x7ffcda2eb790) = -1 ENOENT (No such file or directory)
stat("/etc/terminfo", 0x7ffcda2eb790)   = -1 ENOENT (No such file or directory)
stat("/lib/terminfo", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/lib/terminfo/s/screen", {st_mode=S_IFREG|0644, st_size=1587, ...}) = 0
open("/lib/terminfo/s/screen", O_RDONLY|O_CLOEXEC) = 3
ioctl(3, FIOCLEX)                       = 0
read(3, "\32\1*\0+\0\20\0i\1z\2screen|VT 100/ANSI X"..., 8192) = 1587
close(3)                                = 0
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
futex(0x7f5f6c161048, FUTEX_WAKE_PRIVATE, 2147483647) = 0
mmap(NULL, 2101248, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f5f6abff000
mprotect(0x7f5f6abff000, 4096, PROT_NONE) = 0
clone(child_stack=0x7f5f6adfee30, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f5f6adff9d0, tls=0x7f5f6adff700, child_tidptr=0x7f5f6adff9d0) = 13361
epoll_create1(EPOLL_CLOEXEC)            = 3
pipe2([4, 5], O_NONBLOCK|O_CLOEXEC)     = 0
epoll_ctl(3, EPOLL_CTL_ADD, 4, {EPOLLIN|EPOLLET, {u32=4294967295, u64=18446744073709551615}}) = 0
socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP) = 6
setsockopt(6, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks scary at first, but you absolutely don't need to know what every system call does to gain insight from it (if you wonder, they all have manpages so you can go &lt;code&gt;man futex&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You can follow the program linearly, and it's easy to spot things like &lt;a href="https://github.com/store2be/pape-rs/blob/1f4aeb791f14662ce5bcfe84aa17e4e58a7f3bbe/src/config.rs#L60"&gt;the moment we call &lt;code&gt;dotenv&lt;/code&gt; in &lt;code&gt;config.rs&lt;/code&gt;&lt;/a&gt; In this strace output it's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;open("/home/tom/src/papers/.env", O_RDONLY|O_CLOEXEC) = 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also have, at the end of the trace, the moment where the server starts listening (it's binding to a TCP socket). &lt;code&gt;epoll&lt;/code&gt; is used by the tokio event loop, so our logger configuration is happening before that, and after dotenv.&lt;/p&gt;

&lt;p&gt;A lot of calls in-between are for reading the terminfo database to figure out the capabilities of the terminal.&lt;/p&gt;

&lt;p&gt;This was missing in the strace output inside the container in our kubernetes cluster.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;And that was it: when we set the &lt;code&gt;TERM&lt;/code&gt; environment variable, our log output is back.&lt;/p&gt;

&lt;p&gt;This is because when not specified, kubernetes does not allocate a TTY to containers, so &lt;code&gt;TERM&lt;/code&gt; is not set, and since sloggers does not set up terminal output without it, nothing got printed.&lt;/p&gt;

&lt;p&gt;Our solution was to set &lt;code&gt;TERM&lt;/code&gt; to &lt;code&gt;screen&lt;/code&gt;, although it can also be set to &lt;code&gt;xterm&lt;/code&gt; or &lt;code&gt;dumb&lt;/code&gt;. Another possible solution would be to configure the deployment to allocate a TTY to the container (similar to the &lt;code&gt;-t&lt;/code&gt; flag in &lt;code&gt;docker run&lt;/code&gt; and &lt;code&gt;docker exec&lt;/code&gt;), but this is not necessary for simple logging.&lt;/p&gt;

&lt;p&gt;This post is a testimony to the usefulness of the OS debugging tools and strace especially. There is a very good series of posts by Julia Evans on the many tracing tools for Linux, I would recommend them to anyone writing server-side software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jvns.ca/blog/2015/04/14/strace-zine/"&gt;A zine about strace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jvns.ca/blog/2017/07/05/linux-tracing-systems/"&gt;Linux tracing systems and how they fit together&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jvns.ca/blog/2017/03/19/getting-started-with-ftrace/"&gt;ftrace: trace your kernel functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Hi there, we’re &lt;a href="https://www.store2be.com"&gt;store2be&lt;/a&gt;, a Berlin based startup that builds a SaaS enabled marketplace for short term retails space. If you like what we are posting you might wanna check out &lt;a href="https://tech.store2be.com"&gt;the store2be tech page&lt;/a&gt; or follow our &lt;a href="https://medium.com/store2be-tech"&gt;Medium channel&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>kubernetes</category>
      <category>rust</category>
    </item>
    <item>
      <title>Releasing Papers, a PDF generation service based on LaTeX, written  in Rust</title>
      <dc:creator>Tom Houlé</dc:creator>
      <pubDate>Fri, 26 May 2017 07:36:19 +0000</pubDate>
      <link>https://forem.com/tomhoule/releasing-papers-a-pdf-generation-service-based-on-latex-written--in-rust</link>
      <guid>https://forem.com/tomhoule/releasing-papers-a-pdf-generation-service-based-on-latex-written--in-rust</guid>
      <description>

&lt;p&gt;The store2be team is happy to announce the &lt;a href="https://github.com/store2be/pape-rs/releases/tag/0.1.0"&gt;first public release&lt;/a&gt; of &lt;a href="https://github.com/store2be/papers"&gt;Papers&lt;/a&gt;, an HTTP service that receives LaTeX templates, variables and assets needed for the generated LaTex file, and generates good looking PDFs. It is designed to be stateless, safe, reliable, easy to use and to deploy. We even have a second binary to test your templates locally before deploying them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What? Why?
&lt;/h2&gt;

&lt;p&gt;We needed a PDF generation service at store2be for a new feature in our advertising space manager. We investigated four options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a pure Ruby PDF library like &lt;a href="http://prawnpdf.org/"&gt;prawn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Generate PDFs from HTML and CSS with something like &lt;a href="https://wkhtmltopdf.org/"&gt;wkhtmltopdf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Use a paying service like &lt;a href="https://docraptor.com/"&gt;DocRaptor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Write it ourselves&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing templates as code takes too long for the number of templates we are likely to write, and generating from HTML ourselves did not yield very nice documents, so the remaining solutions were an external service or Papers. Given the small scope of the project, what we would learn from the experiment of using a different programming ecosystem (we only used Ruby in the backend before), and the much better looking PDFs produced by TeX, we went ahead and wrote Papers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I use it?
&lt;/h2&gt;

&lt;p&gt;You will need three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have the Papers service running somewhere so you can make HTTP calls to it&lt;/li&gt;
&lt;li&gt;Have the (&lt;a href="https://github.com/Keats/tera"&gt;Tera&lt;/a&gt;) templates and assets hosted somewhere (we use S3/CloudFront)&lt;/li&gt;
&lt;li&gt;Have an HTTP endpoint in your app where the PDF or errors will be posted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Generating a PDFÂ is then a simple matter of posting a JSON object describing your wish to the &lt;code&gt;/submit&lt;/code&gt; endpoint of your Papers service. For example:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"assets_urls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://static.cook.example.com/spaghetti.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://static.cook.example.com/pesto.jpg"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"callback_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cook.example.com/illustrated-recipes/generated&amp;amp;token=18e8d495ac34c541b5a5167bffcbac96"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"template_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://static.cook.example.com/templates/pesto_spaghetti.tex"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In order to deploy Papers easily, we prepared a &lt;a href="https://hub.docker.com/r/store2be/pape-rs/"&gt;docker image&lt;/a&gt;. There is also a &lt;a href="https://github.com/store2be/pape-rs/tree/master/examples/kubernetes"&gt;Kubernetes example&lt;/a&gt; in the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our experience with Rust
&lt;/h2&gt;

&lt;p&gt;Overall, Rust was a very good fit for the goals we had.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We wanted a crash-free service, and the explicit error handling made that possible with a high level of confidence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We wanted the service to be stable and not leak memory, disk space or file descriptors. Once again, it was very helpful that Rust gives you the tools to apply the same logic that makes memory safety without garbage collection possible for other resources, like files or temporary directories. And we get fine-grained concurrency control with tokio as well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since most of the overhead of the service comes from LaTeX itself, we wanted it to be as lightweight as possible, mostly memory-wise, and there Rust is ideal again compared to Ruby.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We wanted easy deployment and administration, and having a compiled binary makes it very easy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing a small service was also a way for our team to assess Rust as a tool for other projects in the future. Our perceptions have adjusted in more than a few ways from this experience.&lt;/p&gt;

&lt;p&gt;We set out with a perception of Rust as the cool new thing, and expected to get high level abstractions from it. The previous experience with Rust in the team ranged from having read the Book to having completed small programs, and learning curve was a bigger deal than we thought. Rust &lt;em&gt;does&lt;/em&gt; force you to think at a lower level and handle more details than something like Ruby or Python: you have to make decisions about errors, even if you want to ignore them, and even if there are ways to simplify memory management problems (&lt;code&gt;Rc&lt;/code&gt; for example), they are opt-in.&lt;/p&gt;

&lt;p&gt;It was hard to accept that you sometimes have to write code to make the program compile and does not contribute at all towards solving the business problem (in our minds as programmers used to high-level languages), for example:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;make_pasta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Satisfaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OvercookedError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.logger&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cook_pasta&lt;/span&gt;&lt;span class="nf"&gt;.and_then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pasta&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Pasta is very hot, be careful"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;eat_pasta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pasta&lt;/span&gt;&lt;span class="p"&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;The line where we clone the logger so it can be moved into the Future is purely there to satisfy the borrow checker, and while it makes complete sense once you "get" the concept ownership, it still feels like clutter in the context of high level code.&lt;/p&gt;

&lt;p&gt;The cargo ecosystem, and especially cargo doc, is fantastic. Period.&lt;/p&gt;

&lt;p&gt;Library-wise, we found good quality libraries for everything. The elephant in the room now for server programming is of course the work-in-progress conversion of the ecosystem to asynchronous APIs based on &lt;a href="https://github.com/alexcrichton/futures-rs"&gt;futures&lt;/a&gt; and &lt;a href="https://tokio.rs/"&gt;tokio&lt;/a&gt;, which was inconvenient but not a show stopper. We went for hyper with the master branch, as there was no higher level alternative that worked with futures.&lt;/p&gt;

&lt;p&gt;In general, futures were hard. There is a clear rightward drift, and error handling differs from synchronous code (in a bad way). The interaction with the event loop also has a learning curve of its own. To be fair, things are going to improve a lot with time and a few language features that are already planned, in particular &lt;a href="https://github.com/rust-lang/rfcs/pull/1951"&gt;impl Trait&lt;/a&gt;, &lt;a href="https://github.com/rust-lang/rfcs/pull/1733"&gt;trait aliases&lt;/a&gt; and, albeit more distant, async/await syntax. The current need to box everything or write complicated types nudged us in the direction of big, monolithic functions when we have to return a Future.&lt;/p&gt;

&lt;p&gt;Compile times are long. Coming from interpreted languages, this is the most frustrating part of developing in Rust. They are getting better though, and incremental compilation has yielded promising results for us.&lt;/p&gt;

&lt;p&gt;Our conclusion is not very original: Rust is a good tool for the use-cases where it makes sense, but we wouldn't use it as a language to write a classical web app (yet).&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;The service is usable in production for small volumes of PDFs (depending on the size, a dozen per minute seems reasonable) and behind a firewall. We do plan to make it faster, and the first obvious step in that direction would be caching the assets. We also need a smarter way to manage the latex processes, and benchmarks to measure and improve performance.&lt;/p&gt;

&lt;p&gt;There are already many other features that go in the direction of more configurability and better debugging experience planned for the next releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Epilogue
&lt;/h2&gt;

&lt;p&gt;If you read this far, we'd be really interested in feedback on our endeavour and on the code if you have some. Just shoot us an email at &lt;a href="mailto:tech@store2be.com"&gt;tech@store2be.com&lt;/a&gt; :)&lt;/p&gt;

&lt;p&gt;For comprehensive documentation, you can head to the &lt;a href="https://github.com/store2be/pape-rs/README.md"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is of course all open source, released under the MIT license, and &lt;a href="https://github.com/store2be/pape-rs"&gt;available on Github&lt;/a&gt;. We are very much open to contributions.&lt;/p&gt;


</description>
      <category>latex</category>
      <category>rust</category>
      <category>pdf</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Kubernetes - Redirect HTTP to HTTPS with ELB and the nginx ingress controller</title>
      <dc:creator>Tom Houlé</dc:creator>
      <pubDate>Tue, 04 Apr 2017 13:44:53 +0000</pubDate>
      <link>https://forem.com/tomhoule/kubernetes---redirect-http-to-https-with-elb-and-the-nginx-ingress-controller</link>
      <guid>https://forem.com/tomhoule/kubernetes---redirect-http-to-https-with-elb-and-the-nginx-ingress-controller</guid>
      <description>&lt;p&gt;TL;DR Read the last paragraph for the most recent way of implementing HTTPS with an nginx ingress controller while letting ELB handle the certificates.&lt;/p&gt;

&lt;p&gt;In our exploratory work with Kubernetes, one of the first hurdles was configuring a proper HTTP ingress. At first, we wanted three properties from our setup:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All unencrypted http traffic is immediately redirected to https&lt;/em&gt;.&lt;br&gt;
&lt;em&gt;The ingress controller does not handle TLS certificates&lt;/em&gt;. instead we have TLS termination at the ELB level.&lt;br&gt;
&lt;em&gt;The redirection is handled by the ingress controller&lt;/em&gt;, not the individual ingress resources, since we always want HTTP traffic to be encrypted, and it limits the potential for errors.&lt;/p&gt;

&lt;p&gt;It turns out that combining those three was more complicated than expected, since &lt;a href="https://aws.amazon.com/de/premiumsupport/knowledge-center/redirect-http-https-elb/"&gt;ELB does not do HTTP redirects&lt;/a&gt;, and neither &lt;a href="https://docs.traefik.io/toml/#kubernetes-ingress-backend"&gt;trÃ¦fik&lt;/a&gt; nor &lt;a href="https://github.com/kubernetes/ingress/tree/master/controllers/nginx"&gt;nginx&lt;/a&gt; ingress controllers supported redirecting unless they handled TLS termination themselves.&lt;/p&gt;
&lt;h2&gt;
  
  
  Our solution
&lt;/h2&gt;

&lt;p&gt;The DNS (Route 53) and ELB configurations were the simplest.&lt;/p&gt;

&lt;p&gt;We first created a LoadBalancer service for our ingress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-service&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kube-system&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;service.beta.kubernetes.io/aws-load-balancer-ssl-cert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;the arn for our wildcard TLS cert&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;service.beta.kubernetes.io/aws-load-balancer-backend-protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
    &lt;span class="s"&gt;service.beta.kubernetes.io/aws-load-balancer-ssl-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;k8s-app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-ingress-lb&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When applied, the service will create the ELB load balancer for you.&lt;/p&gt;

&lt;p&gt;Our DNS configuration is very simple: a “gateway” domain that points to the load balancer. The other subdomains are just CNAME records that point to it. The “gateway” domain is updated manually as a part of the cluster creation, and we just add the CNAME records when needed. We plan on trying out &lt;a href="https://github.com/kubernetes-incubator/external-dns"&gt;external-dns&lt;/a&gt; for the “gateway” domain to automate this in the future.&lt;/p&gt;

&lt;p&gt;The last part was less ideal, since the nginx ingress controller from kubernetes (there is &lt;a href="https://github.com/nginxinc/kubernetes-ingress"&gt;another implementation&lt;/a&gt; by the nginx people) did not support redirecting to https unless it is configured to handle the https traffic itself (which we really don't want).&lt;/p&gt;

&lt;p&gt;Fortunately, the controller lets you completely override its nginx config file, so we just copied the default file from the source, which is of course not ideal, and added what we wanted, which is a 301 redirect based on the &lt;code&gt;X-Forwarded-Proto&lt;/code&gt; header set by ELB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if ( $http_x_forwarded_proto = http ) {
  rewrite ^(.*) https://$host$1 permanent;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then just put the modified config template in a ConfigMap (in our case nginx-config in the kube-system namespace) and pass it to nginx by placing this in the &lt;code&gt;container&lt;/code&gt; section of the Deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/nginx-ingress-controller&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--default-backend-service=kube-system/default-http-backend&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--publish-service=kube-system/nginx-service&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--configmap=kube-system/nginx-config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to do it &lt;em&gt;today&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;This story is a testimony to how fast things are moving in the k8s ecosystem. We came up with this solution on the nginx ingress controller version 0.9.0-beta.1, exactly two weeks ago. Now the current version is &lt;a href="https://github.com/kubernetes/ingress/blob/master/controllers/nginx/Changelog.md#09-beta3"&gt;0.9.0-beta.3&lt;/a&gt; and the &lt;code&gt;ingress.kubernetes.io/force-ssl-redirect&lt;/code&gt; annotation was added, making the same configuration achievable by adding it in each ingress resource.&lt;/p&gt;

&lt;p&gt;This option ticks 2 of our 3 boxes, and we found that the last point, having this setting centralized, was not important enough to justify the hack.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hi there, we're &lt;a href="https://www.store2be.com"&gt;store2be&lt;/a&gt;, a Berlin based startup that builds a SaaS enabled marketplace for short term retails space. If you like what we are posting you might wanna check out &lt;a href="https://tech.store2be.com"&gt;the store2be tech page&lt;/a&gt; or follow our &lt;a href="https://medium.com/store2be-tech"&gt;Medium channel&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>k8s</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
