Blending JSON-RPC with JSON:API for Enhanced APIs

API DesignJSON-RPCError ManagementData StructuringFeature Integration

Throughout my career, I have strongly advocated for REST APIs. However, with experience comes the realization that they may not be the ideal solution for every scenario. In recent years, my focus has shifted towards JSON-RPC, and I've grown to appreciate its straightforwardness. This protocol, while not flawless, offers a solid foundation for API development. An intriguing idea I've been exploring is how to merge the strengths of JSON:API with those of JSON-RPC into a cohesive, user-friendly specification. This post discusses key aspects from JSON:API that I integrate into JSON-RPC APIs to enhance consistency and usability.

Errors

Efficient error handling is crucial for any API. Poor error management in APIs can lead to confusion and inefficiency, making troubleshooting a challenging task. JSON:API addresses this aspect effectively, a practice I've adopted in my JSON-RPC APIs to ensure clarity and ease of error resolution.

{
	"jsonrpc": "2.0",
	"id": "abc123",
	"error": {
		"code": -32600,
		"message": "Invalid Request",
		"data": [
			{
				"status": "422",
				"source": {
					"pointer": "/jsonrpc"
				},
				"title": "Invalid member",
				"detail": "The jsonrpc field is required."
			},
			{
				"status": "422",
				"source": {
					"pointer": "/method"
				},
				"title": "Invalid member",
				"detail": "The method field is required."
			}
		]
	}
}

Data

A primary step in my approach involves encapsulating data within a data property. This structure clearly separates data from metadata, simplifying the addition of metadata without altering the response format. I find this method, common in JSON:API, equally beneficial in JSON-RPC APIs.

{
    "jsonrpc": "2.0",
    "id": "abc123",
    "result": {
        "data": {
            "type": "articles",
            "id": "1",
            "attributes": {},
            "relationships": {}
        },
        "page": {
            "cursor": {
                "self: "...",
                "prev": "...",
                "next": "..."
            }
        }
    }
}

Sparse Fieldsets

Another key practice is ensuring that responses contain only the requested fields. This approach, a hallmark of JSON:API, is highly practical in JSON-RPC APIs. It allows for efficient data retrieval without necessitating multiple requests.

{
	"jsonrpc": "2.0",
	"id": "abc123",
	"params": {
		"fields": {
			"articles": ["title"]
		}
	}
}

Filters

Similarly, I ensure that responses conform to specified filters. This JSON:API feature, when applied to JSON-RPC APIs, streamlines the process of fetching relevant data, eliminating the need for multiple queries.

{
	"jsonrpc": "2.0",
	"id": "abc123",
	"params": {
		"filters": {
			"articles": [
				{
					"attribute": "title",
					"operator": "contains",
					"value": "JSON"
				},
				{
					"attribute": "created_at",
					"operator": "gte",
					"value": "2024-02-19"
				}
			]
		}
	}
}

Relationships

Including relationships in responses is another strategy I consistently employ. This technique, effective in JSON:API, proves equally valuable in JSON-RPC APIs, facilitating the retrieval of related resources without additional requests.

{
	"jsonrpc": "2.0",
	"id": "abc123",
	"params": {
		"relationships": ["author"]
	}
}

Sorting

Lastly, I prioritize responses according to requested sorting criteria. This practice, derived from JSON:API, enhances JSON-RPC APIs by ensuring data is presented in a user-specified order, further reducing the need for multiple requests.

{
	"jsonrpc": "2.0",
	"id": "abc123",
	"params": {
		"sorts": [
			{
				"attribute": "created_at",
				"direction": "asc"
			}
		]
	}
}

Conclusion

I hope this post offers insights into refining JSON-RPC APIs. Often perceived as a basic protocol, JSON-RPC is actually quite potent. By incorporating elements from JSON:API, known for its comprehensive API design, the process of creating robust APIs becomes more streamlined, with many intricate details efficiently managed by the combined strengths of both specifications.