API Design: Best Practices and Pitfalls
Most developers have struggled working with poorly built or documented APIs.
The common scenario goes like this: your product manager decides to leverage a third-party tool to speed up development, but the team suffers due to a bad API design.
How do you know that an API is poorly designed?
Common signs include mixing endpoint resources like user and product records, incorrectly using HTTP methods like POST instead of GET, and excluding documentation of valid parameters.
Correctly implementing these features is crucial to creating a great developer UX and an awesome API product.
Let’s discuss API-first design and some of the best practices and pitfalls you should watch for when designing your API.
Understanding API-first development
Good API design is thoughtful API design.
To build great APIs, you need to understand what your services aim to accomplish and possible pain points preventing this. You can achieve this with an API-first approach.
With API-first development, you remove end-user applications. And focus on delivering value through tooling that’s easy to understand and use.
API-first development has many definitions, so let’s clear up what it means.
API stands for Application Program Interface. An API is code that allows two or more systems to talk to each other.
To perform API-first development, we have to ignore these systems.
API-first development is the process of designing an API as a standalone product, without considerations for specific end-users or systems.
By focusing on exposing resources or handling methods without a clear use case, you can build a better-defined and more flexible system. This makes it reusable and allows plug-and-play behavior for building new features when you determine a new business case.
Most importantly, the API is the main focus of this development approach. It allows developers to follow best practices, write API documentation, and provide end-users with the best developer experience.
Choosing the right API design methodology
There are a couple of questions to answer before you begin designing your API. Will the team take an inside-out or outside-in design approach? Should we use a RESTful or GraphQL API? The answers to these questions will dictate how you approach your design.
Inside-out vs. outside-in API design
The API design you choose depends on how you plan to use your API. If your main focus is creating a UI for users, you may pick the outside-in approach. In this approach, you design your UI first, then build your API to support the planned interface.
Outside-in takes the opposite approach by focusing on the schema, and then the API before designing the UI. Both methods work, but if you anticipate supporting more than one UI or client, the inside-out design is a better fit for your team.
RESTful vs. GraphQL APIs
Two of the most popular API types are REST and GraphQL. REST was an upgrade over the previous industry standard, SOAP, but has recently faced competition from GraphQL.
REST is a set of standards that describes how to expose resources from a server to a client.
It has remained dominant because it is stateless, meaning a request made to a REST API is not reliant on previous requests. REST uses descriptive HTTP methods, and groups server data into resources that are easy to query and understand.
GraphQL is an API query language that allows developers to build flexible, resource-specific queries instead of relying on prebuilt endpoints. Another benefit is that it allows requests to include the shape of the return object, so it doesn’t return unnecessary fields.
Both declarative queries and requesting return object shapes make GraphQL response objects lighter and faster. GraphQL has downfalls though, as it doesn’t support rate limiting, only provides 200 http status codes on request responses, and has a steeper learning curve for developers.
As is common in software engineering, there is no wrong answer for which type of API you choose. There is just a better answer. Mobile-first apps focusing on speed and partial records likely use GraphQL. If you work on a desktop app built around full sets of records, you’d likely use a REST API.
What are the core API design principles?
API design can be an art, but it shouldn’t be. An excellent piece of advice I got when starting API design was, “If you are creative, you aren’t building your endpoints correctly.” A creative API is a bad API.
Let’s look at additional API design best practices.
Following expectations
Software engineers thrive on repeatable, expected results. If every API call was idempotent, our jobs would be much simpler. Flaky endpoints and endpoints that function in unique ways lead to reportable bugs, which lead to issue tickets.
Avoid a backlog of issues and support tickets by adhering to API expectations in the following ways:
- Consistency across endpoints: Don’t change patterns for endpoint structure, authentication, or features like query params, pagination, or filtering across your product.
- Proper use of HTTP methods: Use methods how they are expected to be used (don’t write with GETs and don’t read with POSTs / PUTs).
- Security through authentication and authorization: Make use of Oauth and RBAC and keep resource group access consistent.
- Scalability and performance optimization: Benchmark endpoint response times and modify calls to prevent timeouts. Use features such as required query parameters or pagination to work on smaller data collections.
All of these principles greatly improve the developer experience by ensuring what the developer thinks will happen, happens.
Building to be easy-to-read
Good APIs are simple and easy to understand. Your API endpoints should form sentences with an action, subject, and context for the subject. You should also provide comprehensive and easy-to-use documentation for your users.
Many teams add API notes to a GitHub page and call it a day. Don’t be like these teams.
Use tools built to create robust, interactive, and informative API documentation that will make your API stand out and last longer. Here are some principles for easy-to-read APIs:
- Simplicity and clarity: If you can remove a layer of abstraction for a URI or avoid an extra parameter, do it.
- Resource-oriented architecture: Functionality should be built around grouping resources.
- Intuitive and human-readable URIs: Good URIs should read like a sentence. If you can’t translate it into a simple sentence, it’s too complicated.
- Robust error handling: You control the error codes and messages returned to the user for specific error cases. Give them proper status codes and context to fix their code.
- Versioning for backward compatibility: Prepend your API with path elements like “api/vX” to allow developers to track what features they can access and avoid breaking changes.
- Comprehensive and up-to-date documentation: Write API docs that follow best practices such as including examples and overexplaining.
Thoughtful upfront design will make your API much easier to modify and update as you expand functionality. Easy-to-read endpoints and strong documentation improve your API and provide a great developer experience.
Common pitfalls in API design and how to avoid them
Just as there are core principles of good API design, there are also common pitfalls all developers face. These pitfalls center around two issues: over-engineering endpoints and security missteps.
Doing too much with your endpoints
The key to building complex APIs well is to make simple components. Engineers tend to get clever with their endpoints and over-engineer them to offer extra flexibility. Don’t build endpoint features for the future if they’re not asked for in the present.
What does over-engineering look like? It could be as simple as offering too many query parameters for a resource. If you have a half dozen or more query params, then your endpoint is too general.
Over-engineering can also take the form of deeply nested URIs and overly complex schema. If your URI has more than two resource groups, the relationship between objects is too complex and your endpoint is over-engineered.
API security misalignments
Security misalignments include:
- Inconsistent authentication
- Inconsistent authorization
- Missing common auth process tools
Oauth is a widely used protocol for authentication and authorizing the use of your app. If you are allowed to implement OAuth flow, then you should in order to simplify and strengthen your API’s security.
The other common security misalignment is inconsistent authentication. This could take the form of requiring different methods for authentication from endpoint to endpoint. Some endpoints require a key in a header, others use Oauth with tokens, and a final method includes the API key in the URI (do not do this as it exposes the key to anyone on your network).
Discuss the best authorization method with your team and apply it to each endpoint.
Testing and iterating on your API design
As you design and develop your API, you’ll need to test that your code works as expected. When code changes, you need to update your users on the iteration of your software and create feedback channels for users to report issues with your code.
Tips for testing your API
Software development testing takes time and a lot of thought. Product managers may be tempted to cut corners when testing their APIs, but I highly advise against this. Missed bugs in testing become outsized issues once they hit production.
Two crucial API testing methods are contract testing and automated testing.
Let’s use a shipping company as an example to clarify the differences between these testing types and how they fit together.
A shipping company would likely have specific-sized packages for transporting goods. Some packages may be envelopes while others are boxes. The size and package type matter when shipping goods.
Contract testing verifies the size and package type of your API response. With contract testing, you don’t care about what’s in the package. You care if it has:
- A specific data format
- Specific data fields
- A specific message format
If our company has rules for the contents of the packages, we need to verify that the shipping request complies with those rules. For certain shipping requests with inputs, we need to check that the outgoing package is what is expected.
This is where automated testing comes in. With automated testing, we create test code to model requests with certain inputs and verify they have the correct response outputs. These tests are crucial for the health of our API as it grows since they verify that changes to any part of the API do not break existing functionality.
Iterating on your API’s design
APIs are living code. I have yet to see an API that ships bug-free and meets all the demands of the consuming developers. Because this code is subject to change, we need processes for updating our code.
A major component of good API iteration is API versioning. To version an API you prepend something like “/api/v1” to your URIs. Your initial API would have a V1 designation, but you’ll have to create new API versions as you update existing resources to accept new parameters or return new values.
Make sure to use changelogs to document when changes happen. Changelogs are a developer’s diary, documenting when certain bugs were fixed, when new functionality was added, and when minor, major, and, breaking changes occurred.
Gathering feedback for improving your API
Your end-users aren’t the ideal way to test your API, but they are the most likely to find bugs. Even after a thoughtful design, build, and testing process, things will still break. Users will request additional functionality. Your code will need to change.
It’s important to streamline the feedback process for your product manager and engineering lead. A community or feedback board will make this process clearer.
This resource will help your team in multiple ways. For one, it will create a list of potential issues for your software’s features. And it will also provide a forum for developers to discuss and solve their own problems. Getting feedback and proactive solutions from your users will save your team time and reduce bug backlogs.
Documenting your API
API-first development and good API design come down to planning and meeting expectations. Proper tooling increases developer productivity but also produces great resources for end-users. When you are looking for a great documentation solution, you need a tool that is robust and built with developers in mind.
ReadMe offers multiple solutions for creating robust API documentation that can grow with your product. Whether you need easy-to-read and write API documentation, a changelog platform to keep track of API updates, or even a forum for users to share issues and diagnose problems, ReadMe has you covered. Check out our solutions today.