The Inspiration or Why Rust?

It seems that developers adore Rust. It was voted the most loved language for the seventh year in a row on the Stack Overflow Developer Survey. An astonishing 86.73% of developers claim they loved the language and want to continue using it.
While Rust is growing in popularity, it is still not as widely adopted as many of its peers, with only 9.09% of developers stating they are using it.

Asked why developers have stopped using Rust, the most common response is that the respondent's company does not use it, suggesting an adoption issue.
Other common reasons are the learning curve, a lack of necessary libraries, and a lack of integrated development environment (IDE) support.

That seems to explain the low utilization of RUst. Developers are either feeling like the up-front cost of starting a project in Rust is too high or that Rust would not enable them to do what they want efficiently. While Rust offers great speed and efficiency it does not have a big enough ecosystem to enable its usage in many cases.

One such use-case may be web development, specifically backend development for retailers. Square enables retailers to quickly build and scale their online stores and centrally manage large amounts physical locations. They can integrate their web presence and internal service by using the Square API and its many endpoints. As a boost to their productivity there are many programming languages that have supported Square SDKs. These languages include:

  • Java
  • .NET
  • Node.js
  • PHP
  • Python
  • Ruby

What most of these languages have in common is that they are interpreted. This is by no means necessarily a bad thing. Being evaluated at runtime makes these languages very flexible and often the ideal choice for web development. Yet, their interpreted nature has two significant downsides. They are not particularly fast, and they can be unreliable. In the space of finance and commerce, speed and reliability are extremely important. You would not want to wake up in the middle of the night to a pager alert stating that one of you vital services has failed, losing revenue with every minute of downtime, or worse, losing valuable transaction data that could mean the loss of millions of dollars.

A solution to this is using a language that is compiled, checked at compiletime, and inherently safe. If this sounds like a marketing pitch for Rust, that is because it is. Rust answers all of these questions while also speeding up all of your services to handle requests efficiently and safely.

To make Rust a viable choice for developing services that interact and depend on the Square API there needs to be an easy way of interacting with the API without building everything from scratch. A Rust Square SDK if you will.

When embarking on this project, I discovered that there existed a small library named square-rs. This library offered some basic functionality of the Square Payments endpoint. While being limited in what it could do, this library was a perfect candidate for turning into a fully fledged Square SDK as it laid out the fundamental patterns of how to communicate with the API endpoints.

With the goal of this project now decided, building a Rust Square SDK, it was time to start the journey.

What it does

Oxidized Square allows developers to idiomatically interact with the Square APIs many endpoints without needing to worry about implementation details. Every endpoint that is supported by Oxidized Square (currently ten and growing) requested. Any objects that are returned by those endpoints are already defined in the libraries objects module, allowing developers to focus on what they want to communicate to and from the API, rather than the format.

For example, creating a new booking would look like this:

use square_ox::{
    response::{SquareResponse, ResponseError},
    client::SquareClient,
    api::bookings::BookingsPost,
    builder::Builder,
};

let booking = Builder::from(BookingsPost::default())
    .start_at("2022-10-11T16:30:00Z".to_string())
    .customer_id("some_id".to_string())
    .add_appointment_segment(AppointmentSegment {
        duration_minutes: 60.00,
        team_member_id: "some_id".to_string(),
        any_team_member_id: None,
        intermission_minutes: None,
        resource_ids: None,
        service_variation_id: "some_id".to_string(),
        service_variation_version:  1655427266071,
    })
    .build()
    .await;

let square_reply = SquareClient::new("some_token").bookings().create(input).await;

Or, retrieving the inventoried count for an item could look like this:

use square_ox::{
     response::{SquareResponse, ResponseError},
     client::SquareClient,
 };

let count = SquareClient::new("some_token")
    .inventory()
    .retrieve_count(
     "some_obj_id".to_string(),
     Some("some_loc_id".to_string())
    )
    .await;

How I built it

square-ox is a fork of Kyle Cotton's square-rs library. When initially searching for a Square API library written in Rust Kyle Cotton's cargo crate was the first one that came up. At the time the library only encompassed one endpoint method; that of creating a Payment. Yet, the library laid the groundwork of how the crate would be structured and took the first extremely important steps. The impact of Kyle Cotton's preliminary work on this project cannot be understated.

After forking the repository I first got to work understanding the way Kyle designed the crate. After understanding the implementation enough that I felt comfortable making changes I began implementing endpoints, methods, objects, and enums. As the codebase grew, the need to redesign certain areas became apparent. The original library was not capable of facilitating the complexity of the growing project. By refactoring some implementations to be more flexible and by changing how certain features worked, I was slowly able to grow the size of supported endpoints. This process was slow , to begin with, but as I became comfortable debugging Rust and API errors, the speed at which I moved increased. By no means would I claim that I am now either an expert at Rust or the Square API, but I have learned a lot about them both.

Challenges I ran into

The most significant challenges were:

  1. Debugging API serialization errors returned by serde_json
  2. Implementing all objects for each the endpoint
  3. Designing the public interfaces to be as easy-to-use and simple as possible
  4. Making the code as efficient as possible by reducing memory usage wherever possible

The first challenge was one of the most difficult to deal with in the beginning. My experience working with Rust to make API requests, to serialize, and deserialize were very limited in the beginning. I am still rather new to Rust programming, so learning how to debug JSON parsing errors or how to even make the code return the right errors was a challenge. Eventually, as the project progressed this improved, and my skills in this area bettered significantly.

In the beginning, I very much underestimated how much work it would be to define each of the Square API's objects in Rust.

Accomplishments that I am proud of

When there is talk about Rust its steep learning curve will often come up. While at times may be overstated it still is very true. Rust can be very unconventional in the restrictions it puts on the programmer. One of Rust's main goals is to almost completely do away with unsafe or undefined behavior. This can make working with it complicated and confusing at times. Yet, once you figure out how to work with Rust and get whatever you are trying to implement to work, you feel like a genius. In that sense, I almost do not want to take any ownership of many of the aspects of Square-ox. Working with Rust makes you feel like a genius, but it truly seems that this genius is intrinsic to Rust itself.

If I have to mention one thing that I am proud of, it is using Rust's rich type and trait to develop a generic Builder. This struct allows developers to make calls to the Square API that are checked in the validity of their content. This way the application will not even allow you to send requests with missing parameters and thus developers do not need to decipher cryptic API error responses. In a sense, the Builder and the various objects that implement it are their own little API documentation. The part that I am most proud of is making this Builder a generic one instead of having a dedicated Builder for each object. While this is maybe trivial to most, to me this means a lot.

What I learned

This project taught me a lot. It taught me about much about the fundamentals of developing useful and usable code. It is often not enough to build something with a use case if the implementation is not great. While I would call Square-ox is far from great, it was quite handy when developing some demo websites showcasing Rust microservices that interact with the Square API. I learned a lot about the nitty-gritty of working on an open-source project. I was able to get a sense of the intricacies of Rust and how amazing it really is. This project taught me a lot about API in general and how to interface with one through Rust. I believe this project has led me to have a much deeper understanding of both Rust and APIs than I had before.

What's next for Oxidized Square

Next up I would like to add Square's remaining endpoints to the library and keep incorporating any future ones. I also plan to improve the usability of the library and make communication with the Square API even more convenient for developers. I hope to be able to get more people contributing to the library as my knowledge and skills are still very limited, and I am sure there is much that could be imported by others.

Built With

Share this project:

Updates