Lessons from building enterprise facing APIs
We’ll go through what we’ve learned while building public facing APIs.
APIs are powering the digital world from ticketing to financial transactions to identity verification. There are a large number of tech giants whose sole purpose is providing APIs from things like payments to authorization (Stripe, Twilio, Google etc.).
As consumer and enterprise applications are becoming bigger, more complex and requiring dozens of 3rd party integrations, the need for creating well designed APIs has become part of a company’s core business model and product.
Developers are opinionated, and to save time they will (in 100% of the cases) will choose APIs that are:
At Turing Technologies we achieve to maintain high standards for our development and follow best practices to develop our projects.
Below we’ll go through what we’ve learned while building public facing APIs. The lessons below are equally important for public facing and APIs meant for internal consumption.
I can’t stress this enough, documentation is important and no matter how intuitive your APIs and their response are you need to document.
Our API documentation, and schemas/models are all in the same place: our GitLab Repo. This prevents us from having obsolete documentation and makes it easier for API consumers to gain context.
Reference links to alternative sources so readers can use it to gain additional context. Most importantly, like with any documentation, communicate with empathy and clarity. Go over the integration flow internally and think through the questions a developer would have while consuming your APIs.
If your company is in early stages of releasing their API(s), approach each question and request as an opportunity to improve your documentation.
Each clarification request is a gift that lets you see your APIs with a pair of fresh eyes. Our customers were very helpful in assisting us craft the response messages and structuring data in our response calls.
During our time working with developers, you would be shocked to know that 80% of them do not know or follow basic REST standards, or even know the difference between PUT vs. PATCH.
We’ve also audited projects where the developers despite having more than a decade of experience would only use POST and GET for all API calls, and use the endpoints to define the API function.
To create a new user - POST /create-user
To delete an existing user - GET /delete-user
The above approach will confuse a new developer or customer using the APIs, and would take a longer time to understand your APIs.
In the above code snippet, instead of making a new endpoint after /top-colleges, use a query parameter instead /top-colleges?format=csv
One good way to learn best practices and patterns is to read documentation of API-first companies know for their great developer experience. Stripe and Shopify are great examples of such companies, having simple and clear documentation with flowcharts and sequence diagrams. This would help you follow their approach and design patterns while designing their APIs.
For example, HubSpot’s approach to their API design becomes very clear once you go through their ERD diagram.
If you’re using GraphQL as an API standard then designing your schema, middleware or query pre-processor plays a very important role.
Approach designing an API as you develop a product. Look at it from all angles and think through all the user-cases.
APIs should be thought of as a product as much as they’re thought of as technology. There are numerous multi-billion dollar companies like Twilio and Stripe, have APIs as their main product. Other companies like Expedia earn a big share of their revenue through affiliates which consume their APIs.
Design APIs like you’d design user experience for a product because that’s what it is. APIs are designed for computers and people.
While APIs are meant to be consumed by machines however they have to be designed and documented by humans (if you consider developers as humans/people).
Your APIs should provide a great user experience by being intuitive to understand, easy to read, and by providing specific response messages.
They should also use standard patterns, such as status codes and authentication methods, so that the engineers integrating against them can work efficiently. Read REST Standards here
"message": “Something went wrong"
Good: This is better than the previous one, the error message is actionable but still requires some guesswork for the recipient.
"message": “The enrollment is not found"
Better: This is much better. It determines the exact error code so recipients don’t need to match against a specific pattern and the error message is human-readable and gives the recipient all the information they need to debug.
"message": “Enrollment with ID 'ckuxnye4w934nf7344ubd8rgo' not found",
You should be obsessive about backwards compatibility in your APIs especially if they are being consumed/used by another organization.
It’s difficult and in some cases impossible to change your customers. It can be expensive for a customer or partner to upgrade API versions, and it may not be a high priority for them. With APIs and backwards compatibility, it’s much easier to extend an API than to deprecate an older one. When it comes to backwards compatibility, addition is much easier than subtraction or modification.
Hence our goal is to put backward compatibility at high priority. Read more about Backward Compatibility here.
Our APIs are described using OpenAPI — a standard, language-agnostic interface for REST APIs. OpenAPI gives us many powers like generating API docs easily. Not only did it save us a lot of time, but also improved the developer experience. One of the best decisions we made was to auto generate the OpenAPI specs from our code. This meant that any new parameter that was added was automatically documented and easily communicated to customers and removed any human intervention (as humans create errors).
Build internal API logging infrastructure to log all API requests with metadata (excluding private information).
Anyone who wants to sell to enterprise and make the big $$$ would have to focus incessantly in developing and maintaining logs. In our case we reviewed Slack's Audit Log design. In Slack's case, every audit event logged by the Audit Logs API is comprised of an actor, an action, an entity, and a context. They all work in harmony, such that the actor takes an action on an entity within a context.
As storing and retrieving logs involves storing very large amounts of data hence in case of Stripe, users can only access request logs created within the last 15 months from their Dashboard. In case of test mode, users can only see request logs created in the last 90 days from the Dashboard.
Basic building block of Stripe's logs are Events. Events occur when the state of another API resource changes. The state of that resource at the time of the change is embedded in the event's data field. For example, a charge.succeeded event will contain a charge, and an invoice.payment_failed event will contain an invoice.
Logs will help you debug errors, identify issues and audit unexpected calls. This is also a security requirement if an API-first company wants to be compliant with SOC2's guidelines or other security standards.
We'll explain in the future on how to store logs so they can be quickly retrieved by the user, and which 3rd party service is great for that.
Softwares are systems with complex interactions, powered by various 3rd party and open source tools.
In case of our public APIs, we use rate limiting to prevent mistakes from clients or denial-of-service attacks from affecting our API availability for others.
To support an API program at scale, internal tooling and infrastructure is important. Hence invest in tools, libraries and infrastructure that will help onboard new developers, sound alarms in case of a bad actor, maintain a single source of truth, debug errors and help audit or analyze usage.
If you’re interested in tackling hard problems and work on cutting-edge SaaS products then checkout our careers page: