DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Building REST API Backend Easily With Ballerina Language
  • Composite Requests in Salesforce Are a Great Idea
  • Creating a Secure REST API in Node.js

Trending

  • Operational Principles, Architecture, Benefits, and Limitations of Artificial Intelligence Large Language Models
  • Mastering Fluent Bit: Installing and Configuring Fluent Bit on Kubernetes (Part 3)
  • The Smart Way to Talk to Your Database: Why Hybrid API + NL2SQL Wins
  • Breaking Bottlenecks: Applying the Theory of Constraints to Software Development
  1. DZone
  2. Data Engineering
  3. Databases
  4. RESTful Considered Harmful

RESTful Considered Harmful

RESTful APIs are common across the internet, but is this a good thing?

By 
Tomasz Nurkiewicz user avatar
Tomasz Nurkiewicz
DZone Core CORE ·
Jul. 25, 15 · Opinion
Likes (31)
Comment
Save
Tweet
Share
67.8K Views

Join the DZone community and get the full member experience.

Join For Free

I don't like RESTful principles and APIs. In recent years it is seen as universal protocol for inter-process communication, especially in distributed systems. However I see many deficiencies of REST and there are alternatives that work well for certain use cases. Obviously there is no one size fits all, I just want to emphasize that REST architecture is flawed in a number of ways.

Bloated, Human Readable Format That Demands Extra Compression

The de facto standard format for REST is JSON. At least it's way better than SOAP with its XML and envelopes. Either lots of network bandwidth is wasted (can be an issue with mobile devices or large data centers) or CPU is spent on compression and decompression. A lovely quote:


[the] internet is running in debug mode source

It's a serious issue, ask high-frequency traders how hard parsing text-based FIX is. There are plenty of well-established binary protocols that are both easy to parse and use little memory, e.g. Protocol buffers, Avro or Thrift. Moreover backward compatibility is built into them. You can technically be using Google Protocol Buffers with Spring MVC-based REST Services - but somehow very few do it this way and stick to JSON.

Of course JSON has one (literally, one) tremendous advantage - it's human readable. Well, at least as long as it's pretty printed - rarely the case. But do we really want to pay that price rather than just use binary format by default and have translator or switch when human intervention is required?

Neither Schema Nor Contract

I belong to the static typing camp. I love it when my compiler finds bugs before I even run unit tests - and that I don't need to write so many of them in the first place. Moreover especially in functional programming if code compiles, chances are it works because the contract enforced by types is so strict. When dealing with XML I am happy to have XSD validated so that I can be sure what I read conforms to contract. I can reduce the number of assertions and keep code shorter. Also the XML I produce is guaranteed to be syntactically correct. Last but not least when working with relational databases strict schema prevents me from corrupting data by inserting incorrect values. Similarly to XML, I can also trust what I read: foreign keys are correct, NOT NULL columns really are not null, etc. These are clearly the advantages of machine enforced contract. Somehow all of this is not important anymore when designing API to be produced and consumed by machines in a distributed system (and accidentally in many schema-less NoSQL databases like MongoDB).

Understandable hate of SOAP pushed us to another extreme - no contract at all. There is no widespread standard for documenting contracts and enforcing them automatically for REST APIs. Most of the APIs I find these days are just a collection of sample requests and responses, Swagger at best. I never know which fields are optional, what are the formats and constraints. One might argue that this is by design, that we don't couple ourselves to one concrete API, allowing it evolve. But I have a feeling that most of the API consumers are nevertheless coupled and will break once undocumented and unannounced change is rolled out.

Publishing URIs

Talking about documentation, purists claim that the only piece of information API should expose is the root URI, e.g.

www.example.com/api

. Everything else, including allowed methods, resources, content types and documents should be discovered via HATEOAS. And if URIs are not officially documented but instead should be discovered, it implies we should not rely on hard-coded URIs in our code but instead traverse the resource tree every time we use such API. This is idiomatic, however also slow and error-prone. Not to mention Swagger, the de facto standard for REST documentation, officially claims such approach is "not per design" - and sticks to fixed URIs.

No Support for Batching, Paging, Sorting/Searching, ...

RESTful web service does not natively support many enterprise-grade features of APIs like batching requests, paging, sorting, searching and many others. There are competing suggestions, like query parameters, request headers, etc. I remember an hour long discussion about flexible searching API we had some time ago. Should there be:


  • single SQL-like parameter (with proper escaping!), e.g. query=age>10 and (name=John or company=Foo)
  • multiple parameters for each criteria, e.g. age=10&name=John&company=Foo (but how to implement OR operator?)
  • most bizarre: stateful POST on /searches with criteria modeled with JSON-like structure, returning URL to search results (/searches/25), which can later be queried

REST is really constraining here.

CRUD by Definition

RESTful web services are CRUD-oriented, rather than business- or transaction-oriented. Countless times we had to carefully map business terms into simple create/update/delete actions. World isn't that simple and not everything can be simply described in create or update sentences. And even if it can, often RESTful endpoints are awfully awkward and artificial. Do you still remember POST to 

/searches

? Moreover not all data can be mapped into URI tree structure and often we allow denormalization, e.g. the same resource is available under multiple URIs so that it's easily accessible.

Imagine a system that publishes domain events, arbitrary state changes, e.g. 

LoanApproved

, 

EmailSent

, etc. Of course every event has its own, distinct set of attributes. How do you design a RESTful API that consumes such events? Inevitably you'll end up with 

POST /domainEvents

 that accepts arbitrary JSON, maybe with some tag like 

"type": "LoanApproved"

. So you have an endpoint that takes arbitrary JSON "BLOB" and most likely has a giant switch-like statement to interpret various types of events properly. Replace 

"type"

 with 

"method"

 and you just reinvented JSON RPC. How would you do that in an idiomatic way? Translate every domain event on the publisher side to appropriate API call? Doesn't sound very robust.

HTTP Verbs Aren't Descriptive Enough

Mapping from business terms into POST/PUT/PATCH/DELETE/HEAD is tedious and childish. Just like with URIs, world is much richer and not everything fits into these buckets. These verbs were designed to interact with documents and forms in World Wide Web. Using REST feels like degrading our business domain back into database browser. There is no logic, no domain-driven design, no rich flows. We simply manipulate objects, shift them back and forth. Of course REST don't have to and shouldn't map directly to database entities. But even if they map to your core business domain, you still have to view your domain through limited CRUD interface.

Mixing HTTP Status Codes with Business Replies

Idiomatically signaling business errors via REST should use 4xx class of status codes. However these errors were not designed to signal business cases so I constantly find myself trying to map 4xx codes to business results. CAPTCHA test failed? Let's make it 400 Bad request. Form validation error? 417 Expectation failed? Constraint violation due to duplicate? 409 Conflict? But what about optimistic lock exception? What if client has insufficient balance? What if... These error codes are designed for document retrieval, not rich business behind back-end. You end up with putting everything under the same status code or building complex translation document. We invented exceptions, faults, 

Either<T>

 - just to suddenly limit ourselves to fixed set of numeric error codes.

404 is especially problematic, because it's so prevalent. When using RESTful API you can never tell whether 404 is a business circumstance or just a typo in URL. Sounds like a recipe for distributed debugging hell. Oh, and did I mention there is no standard way of encoding errors?

Temporal Coupling

RESTful APIs are really popular among microservice fanboys. But let's go back to basics. RESTful APIs are based on HTTP, which is a request-response protocol built on top of TCP/IP. TCP/IP builds a connection abstraction on top of packet-oriented IP protocol. IP is barely capable of delivering messages from one machine to another. By building all these levels of abstractions we forgot that web is really asynchronous. We believe that by using contract-less lax JSON we are no longer coupling systems together. But there is another dimension of coupling: temporal dependency. If one system needs to notify another about some event, do they really need to exist at the same time. In some cases, typically implemented with GET, request-response makes sense. But in most cases what we really want is a fire-and-forget, at-least-once semantics. Message-driven distributed architectures proved to be much more robust and fault-tolerant. Two systems no longer need to see each other and live at the same time. Moreover one system will no longer break another one if it's producing too many requests. Simply put a persistent queue in between two systems. This approach has numerous advantages:


  • producers and consumers can be restarted at any time without data loss or quality of service degraded
  • scalability is easier to achieve, no need for complex load balancing
  • we can dispatch messages to multiple systems at once
  • debugging might be easier with wire tap pattern

RESTful also has no notion of one-way requests. POST with no body is as close as you can get. This is problematic, because many business cases are naturally fitting the fire-and-forget semantics. When I'm publishing domain events I don't care about response, but REST services are too tightly coupled to HTTP protocol.

Only typical request-response interactions (like retrieving a user by ID) aren't a good fit for queues though. Temporal queues with correlation IDs are awkward and introduce a lot of latency.

No Standards

There are no standards but only good, sometimes contradicting practices. We can't even agree whether resources should be singular or plural, not to mention paging parameters, error handling, HATEOAS... You will find people arguing how RESTful is your service, according to Richardson Maturity Model. Lack of standards mean that everyone can name their service RESTful. It might be a live example from Roy Fielding's dissertation, it might be a 

<form>

 POST handler - as long as they use HTTP, they are REST. This also means you never now how to properly interact with any API. Which headers you should use, how to decode the response, how content type is negotiated (headers? extension in URL?) and which types are supported.

Backward Compatibility

RESTful services have few flawed ways of handling backward compatibility:

  • only adding fields, never removing or changing. I witnessed a situation where someone fixed an innocent typo in an object that happened to be directly encoded as JSON. This crashed another system
  • Content type with versioning - painful and requires maintaining multiple versions
  • HTTP redirects if resources are renamed - only works if clients are RESTful enough, avoiding hard-coded URLs entirely

Each technique above has its own issues, RESTful services simply aren't designed to be evolving.

Alternatives

Few questions I want you to ask yourself before jumping onto JSON over HTTP a.k.a. REST hippie-style of integration:

  • is performance a concern? Then choose more compact format that doesn't require costly compression
  • do you really need blocking request-response? If not, consider message queue or store, like Kafka
  • are you doing continuous deployment or auto-scaling? Then pick technology that doesn't couple client and server temporarily and is connection-less, see above
  • are your interactions complex or maybe you are extracting existing API/interface from module to distributed service? Then choose technology closer to classic RPC
  • do you need exactly-once semantics? If so, nothing will help you, sorry

PS: Obligatory appendix: "Considered Harmful" Essays Considered Harmful.


REST Web Protocols Database

Published at DZone with permission of Tomasz Nurkiewicz, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Building REST API Backend Easily With Ballerina Language
  • Composite Requests in Salesforce Are a Great Idea
  • Creating a Secure REST API in Node.js

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: