Comparing Different API Types
Freeform API
This is the most simple and basic type of API, the "anything goes" approach. You design custom endpoints based on your specific use cases, often without any strict conventions or standards. You might have an endpoint like /get-user-info
or /send-customer-email
that directly interact with your backend.
With this approach you don't get any type safety or tooling or standardization out of the box. You end up with a dedicated endpoint for each specific use case.
Example
GET /get-user-info?userId=123&posts=true
Host: localhost
Content-Type: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{
"userId": 123,
"name": "Alice",
"email": "[email protected]",
"posts": [
{
"id": 1,
"title": "Hello, World!",
"content": "This is my first post."
}
]
}
REST API
REST (Representational State Transfer) is a standard that follows the OpenAPI specification. Resources are exposed as endpoints (e.g., /users
, /posts
). REST APIs are stateless, meaning each request is independent, making them scalable and cache-friendly. REST API requires using the correct HTTP method for fetching, creating, updating and deleting resources (GET, PUT, POST, DELETE). For example, to delete a user, you would send /users/2
and specify the DELETE
HTTP method.
With REST API, you end up making multiple requests to fetch related data, which can lead to overfetching and underfetching. You also cannot specify the fields that you want returned. For example, if you wanted to get a user, their address and their posts and the comments of each post, you would end up making 5 requests to the server.
Because of its impracticality it is rarely used in user-facing applications, being reserved for public APIs.
Example
GET /users/123
Host: localhost
Content-Type: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{
"userId": 123,
"name": "Alice",
}
GraphQL
GraphQL, developed by Facebook, is a query language where clients define exactly what data they need, all in a single request. It solves the underfetching and overfetching issues, comes with standard tooling and type safety out of the box. Clients can request exactly the fields they need and even run multiple operations in a single request. This is often the most practical type of API for a real-world user facing application.
Example
{
"query": "{
user(id: 10) {
email
posts {
id
comments {
text
}
}
}
}"
}
{
"data": {
"user": {
"email": "[email protected]",
"posts": [
{
"id": 1,
"comments": [
{ "text": "Great post!" },
{ "text": "Thanks for sharing." }
]
},
{
"id": 2,
"comments": [
{ "text": "Interesting thoughts." }
]
}
]
}
}
}
JSON:API
JSON:API follows the JSON:API specification and creates a blend between freeform API and GraphQL. It creates a standardized convention for data fetching, allowing clients to request exactly the fields they need without the overhead of a schema setup. It does not come with back-to-back type safety out of the box like GraphQL so you will need to define your own types. Tooling support may also not be as strong as that of GraphQL.
Example
GET /users?filter[slug]=user-slug&filter[email][email protected]
&include=posts.comments&fields[users]=email,posts
&fields[posts]=id,comments&fields[comments]=text
Accept: application/vnd.api+json
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"type": "users",
"id": "10",
"attributes": {
"email": "[email protected]"
},
"relationships": {
"posts": {
...
}
}
},
"included": [
{
"type": "posts",
"id": "1",
"attributes": {
"id": 1
},
"relationships": {
"comments": {
...
}
}
},
]
}
Conclusion
For most real-world applications, GraphQL is often the most practical choice as it is highly standardized, has strong back-to-back type safety out of the box, great tooling and familiarity among developers. REST API is practically never used in real-world applications because of its overfetching issues. Freeform API lacks standardization, type safety and tooling support while JSON:API is a middleground between GraphQL and freeform, offering standard way of authoring APIs and good standard tooling but lacking type safety by default and may not be as familiar among developers.