POST only? Let’s end this absurd API design debate

Debunking the “POST only” API myth, explaining why it stems from a misunderstanding of API design principles, and clarifies the appropriate use cases for RESTful and RPC architectural styles.

Image description

Recently, a discussion about whether to design APIs using “POST only” caught my attention. After delving into this debate, I found that not only is the issue people are arguing about meaningless, but it also exposes many developers’ misunderstanding of the essence of API design. Today, let’s dive deep into the core ideas of API design and see why this debate shouldn’t exist in the first place.

Those developers who advocate using “POST only” to replace RESTful API specifications clearly haven’t grasped the most important point of API design. Their arguments usually include:

  1. Simplifying design: One method can handle everything
  2. Security: POST parameters don’t appear in the URL
  3. Flexibility: POST can send any data structure

At first glance, these arguments seem to make some sense. But in reality, this view confuses the choice of HTTP methods with API design styles, two different levels of issues. POST is just one method of the HTTP protocol, while REST is a style of API design.

Before discussing specific API styles, we need to understand what the core purpose of API design is. A good API should be:

  1. Clear and understandable: Other developers (including your future self) should be able to intuitively understand the purpose of each endpoint
  2. Consistent: Follow certain specifications to reduce learning costs
  3. Extensible: Able to easily perform version control and functional expansion
  4. Efficient: Consider efficiency in terms of performance and resource utilization

RESTful API is just one of many API design styles, focusing on resources and operations on resources. Let’s take a simple blog system as an example to see how RESTful API is designed:

  • Get all articles:
GET /api/articles
Enter fullscreen mode
Exit fullscreen mode
  • Get a specific article:
GET /api/articles/{id}
Enter fullscreen mode
Exit fullscreen mode
  • Create a new article:
POST /api/articles
Content-Type: application/json { "title": "REST vs RPC", "content": "This is the article content...", "authorId": 12345
}
Enter fullscreen mode
Exit fullscreen mode
  • Update an article:
PUT /api/articles/{id}
Content-Type: application/json { "title": "Updated title", "content": "Updated content..."
}
Enter fullscreen mode
Exit fullscreen mode
  • Delete an article:
DELETE /api/articles/{id}
Enter fullscreen mode
Exit fullscreen mode

In this example, we can see:

  • The API is designed around the “article” resource
  • Different HTTP methods are used to represent different operations
  • The URL structure is clear, indicating the resource being operated on
    This design approach makes the API more intuitive and self-explanatory, making it easy for developers to understand the function of each endpoint.

The goal of RPC (Remote Procedure Call) style API design is to make remote service calls look as simple as calling local functions.

Interestingly, those advocating for “POST only” may not realize that they are actually describing the RPC style.

Compared to RESTful APIs, RPC focuses more on the operation itself rather than the resource. This is why RPC-style APIs typically use a “verb + noun” form, such as getProduct(productId) or createUser(userData).

In many RPC implementations, all operations are usually sent via POST requests to the same endpoint, with the specific operation and parameters specified in the request body. This is why the idea of “POST only” is actually closer to RPC than REST.

For example, an RPC-style “get product” request based on HTTP might look like this:

POST /api/rpc/getProduct
Content-Type: application/json { "productId": 12345
}
Enter fullscreen mode
Exit fullscreen mode

Modern RPC frameworks, such as gRPC, provide more powerful and efficient implementations. Let’s use this as an example to demonstrate the RPC style:

First, we define the service and message format (using Protocol Buffers):

syntax = "proto3"; package blog; service BlogService { rpc GetArticle (GetArticleRequest) returns (Article) {} rpc CreateArticle (CreateArticleRequest) returns (Article) {}
} message GetArticleRequest { int32 article_id = 1;
} message Article { int32 id = 1; string title = 2; string content = 3; int32 author_id = 4;
} message CreateArticleRequest { string title = 1; string content = 2; int32 author_id = 3;
}
Enter fullscreen mode
Exit fullscreen mode

Then, using this service in a Node.js client is as simple as calling a local function:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader'); const PROTO_PATH = __dirname + '/blog.proto'; const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true,
});
const blog_proto = grpc.loadPackageDefinition(packageDefinition).blog; // Convert gRPC method to Promise
function promisify(client, methodName) { return (request) => { return new Promise((resolve, reject) => { client[methodName](request, (error, response) => { if (error) reject(error); else resolve(response); }); }); };
} async function main() { const client = new blog_proto.BlogService('localhost:50051', grpc.credentials.createInsecure()); const getArticle = promisify(client, 'getArticle'); const createArticle = promisify(client, 'createArticle'); try { // Get article const article = await getArticle({ article_id: 12345 }); console.log('Retrieved article:', article.title); // Create article const newArticle = await createArticle({ title: 'gRPC vs REST', content: 'gRPC is awesome for internal services...', author_id: 67890, }); console.log('Created new article with id:', newArticle.id); } catch (error) { console.error('Error:', error); }
} main();
Enter fullscreen mode
Exit fullscreen mode

In this RPC-style example, we can see:

  1. The service definition clearly lists all available operations (in this simplified example, GetArticle and CreateArticle).
  2. Each operation has clearly defined request and response types.
  3. The client code looks like calling a local asynchronous function, using await to wait for the result, which further hides the complexity of network communication.
  4. There’s no need to manually construct HTTP requests or parse JSON responses.

Although the underlying layer may still use HTTP/2 as the transport protocol, RPC frameworks (such as gRPC) provide developers with an abstraction layer that makes remote calls look and feel like local function calls.

Therefore, we can see that most of the debates about “POST only” and RESTful APIs should essentially be discussions about these two API styles: REST and RPC. However, the key is to recognize that these two styles each have their applicable scenarios, and the choice should be based on the specific needs of the project, not personal preference.

Now that we understand the differences between REST and RPC, let’s look at their respective applicable scenarios:

  1. REST is suitable for:
    • Resource-oriented applications (such as content management systems, blog platforms, e-commerce websites)
    • Scenarios that require good cache support (GET requests are naturally cacheable)
    • Projects that want to leverage HTTP semantics (such as using appropriate status codes)
    • Public-facing APIs that require good discoverability and self-description
  2. RPC is suitable for:
    • Action-oriented applications (such as complex data processing operations, workflow control)
    • Systems that require high performance and low latency (such as real-time trading systems)
    • Communication between internal microservices (which may require more flexible parameter passing)
    • When operations cannot be simply mapped to CRUD (Create, Read, Update, Delete) operations

The choice of style should be based on your specific needs. In some cases, you might even use a mix of these two styles within the same system to meet the needs of different parts.

  1. The core of API design lies in clarity, consistency, extensibility, and efficiency, not in sticking to a particular method or style.
  2. Both RESTful and RPC are mature API design paradigms. The choice between them should be based on project requirements, not personal preference.
  3. If you decide to use “POST only”, then please design your API according to the RPC style. Like RESTful, it’s a commonly used API specification in the industry, but it should be used in appropriate scenarios.
  4. Don’t be confused by superficial technical details. What really matters is whether your API can effectively serve your users and business needs.
  5. When designing APIs, spend more time thinking about your resource model, business logic, and user needs, rather than obsessing over which HTTP method to use.

Let’s shift our attention away from these pointless arguments and focus on designing truly excellent APIs. After all, technology is for solving problems, not creating them.

Try Logto Cloud for free


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top