Repository: django-json-api/django-rest-framework-json-api

Issue:
JSON API 1.1: return included array even if nothing is included
## Description of feature request

Currently, when there are no included resources, there is no included key. However, as of JSON API 1.1 [this is required](https://jsonapi.org/format/1.1/#fetching-includes) and in older versions it is certainly allowed. So whenever a user has requested includes with the query parameter `include` an `included` key needs to be part of the result. But only then. If there is no `include` query parameter, no `include` should be added.

## Checklist

- [x] Raised initially as discussion #1092
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be in the form of third party libraries where feasible.)
- [x] I have reduced the issue to the simplest possible case.



Discussion:
#1153 has the enhancement
Hey @sliverc I have issued the PR #1153
Kindly review and merge it.
Thanks
Hey @sliverc ! I’d like to work on this. 
Plan: add "included": [] only when ?include= is present; update tests to cover requested-include / no-include cases.
@harshalkalewar Sure. You are very welcome to work on this.



Patch:
diff --git a/AUTHORS b/AUTHORS
index d421d5e6..1cfc9206 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -15,6 +15,7 @@ David Guillot, for Contexte <dguillot@contexte.com>
 David Vogt <david.vogt@adfinis-sygroup.ch>
 Felix Viernickel <felix@gedankenspieler.org>
 Greg Aker <greg@gregaker.net>
+Harshal Kalewar <kalewarh@gmail.com>
 Humayun Ahmad <humayunahbh@gmail.com>
 Jamie Bliss <astronouth7303@gmail.com>
 Jason Housley <housleyjk@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe7c9e14..2ec34d46 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 Note that in line with [Django REST framework policy](https://www.django-rest-framework.org/topics/release-notes/),
 any parts of the framework not mentioned in the documentation should generally be considered private API, and may be subject to change.
 
+## [Unreleased]
+
+### Fixed
+
+* Ensured that an empty `included` array is returned in responses when the `include` query parameter is present but no related resources exist.
+
 ## [8.0.0] - 2025-07-24
 
 ### Added
diff --git a/rest_framework_json_api/renderers.py b/rest_framework_json_api/renderers.py
index b670338f..f418b080 100644
--- a/rest_framework_json_api/renderers.py
+++ b/rest_framework_json_api/renderers.py
@@ -652,7 +652,7 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
                 if not included_cache[obj_type]:
                     del included_cache[obj_type]
 
-        if included_cache:
+        if included_resources:
             render_data["included"] = list()
             for included_type in sorted(included_cache.keys()):
                 for included_id in sorted(included_cache[included_type].keys()):


Test patch:
diff --git a/example/tests/integration/test_includes.py b/example/tests/integration/test_includes.py
index d32ff7e3..238c3076 100644
--- a/example/tests/integration/test_includes.py
+++ b/example/tests/integration/test_includes.py
@@ -36,7 +36,9 @@ def test_missing_field_not_included(author_bio_factory, author_factory, client):
     # First author does not have a bio
     author = author_factory(bio=None)
     response = client.get(reverse("author-detail", args=[author.pk]) + "?include=bio")
-    assert "included" not in response.json()
+    content = response.json()
+    assert "included" in content
+    assert content["included"] == []
     # Second author does
     author = author_factory()
     response = client.get(reverse("author-detail", args=[author.pk]) + "?include=bio")
@@ -204,3 +206,47 @@ def test_meta_object_added_to_included_resources(single_entry, client):
     )
     assert response.json()["included"][0].get("meta")
     assert response.json()["included"][1].get("meta")
+
+
+def test_included_array_empty_when_requested_but_no_data(blog_factory, client):
+    blog = blog_factory()
+    response = client.get(
+        reverse("blog-detail", kwargs={"pk": blog.pk}) + "?include=tags"
+    )
+    content = response.json()
+
+    assert "included" in content
+    assert content["included"] == []
+
+
+def test_included_array_populated_when_related_data_exists(
+    blog_factory, tagged_item_factory, client
+):
+    blog = blog_factory()
+    tag = tagged_item_factory(tag="django")
+    blog.tags.add(tag)
+
+    response = client.get(
+        reverse("blog-detail", kwargs={"pk": blog.pk}) + "?include=tags"
+    )
+    included = response.json()["included"]
+
+    assert included, "Expected included array to be populated"
+    assert [x.get("type") for x in included] == [
+        "taggedItems"
+    ], "Included types incorrect"
+    assert included[0]["attributes"]["tag"] == "django"
+
+
+def test_included_array_present_via_jsonapimeta_defaults(
+    single_entry, comment_factory, author_factory, client
+):
+    author = author_factory()
+    comment_factory(entry=single_entry, author=author)
+
+    response = client.get(reverse("entry-detail", kwargs={"pk": single_entry.pk}))
+
+    included = response.json()["included"]
+
+    assert included, "Expected included array due to JSONAPIMeta defaults"
+    assert any(resource["type"] == "comments" for resource in included)
diff --git a/example/tests/test_filters.py b/example/tests/test_filters.py
index 8e45ded1..ff7bf263 100644
--- a/example/tests/test_filters.py
+++ b/example/tests/test_filters.py
@@ -530,7 +530,8 @@ def test_search_keywords(self):
                     },
                     "meta": {"bodyFormat": "text"},
                 }
-            ]
+            ],
+            "included": [],
         }
         assert response.json() == expected_result