{"id":13550494,"url":"https://github.com/deeprjs/deepr","last_synced_at":"2025-04-05T09:04:54.187Z","repository":{"id":47802136,"uuid":"198026986","full_name":"deeprjs/deepr","owner":"deeprjs","description":"A lightweight alternative to GraphQL","archived":false,"fork":false,"pushed_at":"2023-01-03T01:20:44.000Z","size":1729,"stargazers_count":330,"open_issues_count":0,"forks_count":15,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-03-29T08:11:12.685Z","etag":null,"topics":["api","graphql","rpc"],"latest_commit_sha":null,"homepage":"https://deepr.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deeprjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-21T07:34:56.000Z","updated_at":"2025-03-08T01:18:12.000Z","dependencies_parsed_at":"2023-02-01T04:46:23.884Z","dependency_job_id":null,"html_url":"https://github.com/deeprjs/deepr","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deeprjs%2Fdeepr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deeprjs%2Fdeepr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deeprjs%2Fdeepr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deeprjs%2Fdeepr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deeprjs","download_url":"https://codeload.github.com/deeprjs/deepr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247312068,"owners_count":20918344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","graphql","rpc"],"created_at":"2024-08-01T12:01:33.972Z","updated_at":"2025-04-05T09:04:54.091Z","avatar_url":"https://github.com/deeprjs.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n\t\u003cimg src=\"branding/deepr-logo-with-tagline.svg\" width=\"350\" alt=\"Deepr — A specification for invoking remote methods, deeply!\"\u003e\n\t\u003cbr\u003e\n\u003c/h1\u003e\n\n## Why?\n\n[GraphQL](https://graphql.org/) introduced a powerful idea — the ability to invoke multiple methods in a single call, and more importantly, the ability to invoke methods based on the results of other methods. However, we feel that the design of GraphQL is not quite right. Some crucial points are missing and some features should be removed or implemented at different layers of the stack.\n\nFirst of all, with GraphQL it is not possible to invoke methods on collections. When we specify a query for a collection, it is executed on the elements of the collection, but not on the collection itself. To solve this issue, it is necessary to introduce some additional models, as Relay does with the [Connections](https://facebook.github.io/relay/graphql/connections.htm). We think that such a solution adds complexity and confusion.\n\nFor example, take the following query:\n\n```graphql\n{\n  movies(genre: \"comedy\") {\n    averageRating\n  }\n}\n```\n\nDoes `averageRating` refer to the movie collection or to a movie element? If we are not familiar with the schema, it is difficult to say.\n\nAnother issue is the GraphQL execution model. Having queries executed in parallel seems like a good idea at first, but it has unfortunate consequences for the developer experience. Since the execution order of nested mutations is unpredictable, it is [not recommended](https://github.com/graphql/graphql-js/issues/221#issuecomment-157481861) to do something like this:\n\n```graphql\n{\n  movie(id: 123) {\n    update(rating: 8.3)\n  }\n  allMovies {\n    averageRating\n  }\n}\n```\n\nParallel execution is an optimization matter and we believe it should be under the control of the developer.\n\nThen, there is the way the execution is handled. With GraphQL, it is required to implement resolvers for each operation. This resolver layer seems a little cumbersome to us. When the business layer is implemented in an object-oriented way, why not just directly invoke the methods of the objects? Some would say it is good practice to add an API layer on top of the business layer. Well, we can agree with that. But in any case, we believe that the query execution should not require an additional layer. If some developers want to add an API layer, it is up to them to do so.\n\nAnother point is the type system. Providing schemas and types is certainly an important feature, but we believe it should not be included in the core specifications. A fine type system (such as those provided by TypeScript or Flow) should be optional and implemented orthogonally as an extension. Or even better, if types are specified deeper in the backend stack (i.e., in the business layer), an additional type system may not be necessary.\n\nFinally, let's question the very nature of GraphQL: the fact that it is a language. Do we really need another language, though? The GraphQL language makes queries prettier, but is it worth it? Adding a new language to the stack is no small matter, as it merely adds complexity when connecting it to an actual programming language — both on the frontend and backend sides.\n\nWe love the main idea behind GraphQL, especially the ability to compose method calls, but we think there may be a better way to achieve this goal. That is why we wrote Deepr.\n\n## Feature comparison\n\n|                     | Deepr | GraphQL |  REST  |\n| ------------------- | :---: | :-----: | :----: |\n| Root queries        |  ✅   |   ✅    |   ✅   |\n| Deep queries        |  ✅   |   ✅    |        |\n| Sequential queries  |  ✅   |         |        |\n| Parallel queries    |  ✅   |   ✅    | ✅ (2) |\n| Aliases             |  ✅   |   ✅    |        |\n| Unnesting           |  ✅   |         |        |\n| Root mutations      |  ✅   |   ✅    |   ✅   |\n| Deep mutations      |  ✅   |         |        |\n| Collections         |  ✅   |   ✅    |   ✅   |\n| Collection items    |  ✅   |         |   ✅   |\n| Collection slices   |  ✅   |         |        |\n| Source values       |  ✅   |         |        |\n| No additional layer |  ✅   |         |        |\n| Type system         |  (1)  |   ✅    |        |\n| Introspection       |  (3)  |   ✅    |        |\n| No extra language   |  ✅   |         |   ✅   |\n| Subscriptions       |  (1)  |   ✅    |        |\n\n1. We believe these features should be implemented at another layer of the stack.\n2. REST parallel queries are possible with HTTP/2.\n3. Introspection might come later in the form of an extension.\n\n## Guide\n\nDeepr does not specify the use of a particular language. So, although the following examples are written in JSON, keep in mind that any language could be used.\n\n\u003e Note: To fully appreciate this guide, it is recommended to have a minimum knowledge of [GraphQL](https://graphql.org/).\n\n### Simple queries\n\nLet's start with a simple query:\n\n```json\n{\n  \"movie\": {\n    \"title\": true,\n    \"year\": true\n  }\n}\n```\n\nHere we are querying an object called `movie` in the top-level context (the \"root\").\n\nThen, inside the context of `movie`, we are getting `title` and `year` fields.\n\nThe response will be:\n\n```json\n{\n  \"movie\": {\n    \"title\": \"Inception\",\n    \"year\": 2010\n  }\n}\n```\n\nSo far, it looks like GraphQL. Since we are using JSON objects, the only significant difference is that we must specify a value for the keys `title` and `year`. Specifying `true` means that we want to return the corresponding field.\n\n### Collections\n\nInstead of querying a single movie, let's query a collection of movies:\n\n```json\n{\n  \"movies\": {\n    \"count\": true\n  }\n}\n```\n\nNothing surprising here, we are just querying the `count` field on the `movies` collection. The query will return:\n\n```json\n{\n  \"movies\": {\n    \"count\": 2\n  }\n}\n```\n\n### Collection items\n\nNow, you might ask yourself, how can I reach the items of the `movies` collection? That is easy:\n\n```json\n{\n  \"movies\": {\n    \"[]\": [],\n    \"title\": true,\n    \"year\": true\n  }\n}\n```\n\nBy using the special key `[]`, we specify that the context of the query is the **items** of the collection rather than the collection itself. We get the following response:\n\n```json\n{\n  \"movies\": [\n    {\n      \"title\": \"Inception\",\n      \"year\": 2010\n    },\n    {\n      \"title\": \"The Matrix\",\n      \"year\": 1999\n    }\n  ]\n}\n```\n\nThe value associated with the special key `[]` can be an empty array, an array of one or two numbers, or a simple number.\n\nWith an empty array (such as in the previous example), we get all the items of a collection.\n\nWith an array of numbers, we get a slice of a collection in a similar way to the [`slice()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) method in JavaScript. For example, to get the first two items of the `movies` collection, we can use the following query:\n\n```json\n{\n  \"movies\": {\n    \"[]\": [0, 2],\n    \"title\": true,\n    \"year\": true\n  }\n}\n```\n\nWe get the following response:\n\n```json\n{\n  \"movies\": [\n    {\n      \"title\": \"Inception\",\n      \"year\": 2010\n    },\n    {\n      \"title\": \"The Matrix\",\n      \"year\": 1999\n    }\n  ]\n}\n```\n\nTo get the last two items of a collection, we can use a negative index:\n\n```json\n{\n  \"movies\": {\n    \"[]\": [-2],\n    \"title\": true,\n    \"year\": true\n  }\n}\n```\n\nFinally, to get a particular item in a collection, we can use a simple number. For example, to get the first item of the `movies` collection, we can write:\n\n```json\n{\n  \"movies\": {\n    \"[]\": 0,\n    \"title\": true,\n    \"year\": true\n  }\n}\n```\n\nNote that in this case the item is returned directly, and it is not embedded in an array like in the previous examples:\n\n```json\n{\n  \"movies\": {\n    \"title\": \"Inception\",\n    \"year\": 2010\n  }\n}\n```\n\nNow, let's see how to query both a collection and its items:\n\n```json\n{\n  \"movies\": {\n    \"count\": true,\n    \"=\u003eitems\": {\n      \"[]\": [],\n      \"title\": true,\n      \"year\": true\n    }\n  }\n}\n```\n\nUsing the key `\"=\u003eitems\"` means that we take the current context (the collection of movies) and we put it under a new key called `items` (more explanation on this topic later). As a result, we get the following:\n\n```json\n{\n  \"movies\": {\n    \"count\": 2,\n    \"items\": [\n      {\n        \"title\": \"Inception\",\n        \"year\": 2010\n      },\n      {\n        \"title\": \"The Matrix\",\n        \"year\": 1999\n      }\n    ]\n  }\n}\n```\n\n### Method invocation\n\nSo far, we have seen how to query fields. Let's now see how to invoke methods:\n\n```json\n{\n  \"getMovie\": {\n    \"()\": [{\"id\": \"abc123\"}],\n    \"title\": true\n  }\n}\n```\n\nThe special key `()` indicates that we want to invoke the `getMovie` method with the parameters specified in the corresponding array.\n\nWe get the following result:\n\n```json\n{\n  \"getMovie\": {\n    \"title\": \"Inception\"\n  }\n}\n```\n\n### Keys\n\nWe have seen previously some examples involving the arrow symbol `=\u003e`; let's now go into the details of this powerful feature.\n\nObject keys are made of two parts, a \"source\" and a \"target\", separated by the arrow symbol `=\u003e`.\n\n- The \"source\" is the method or the field name, evaluated in the current context.\n- The \"target\" is the place where to put the result of the evaluation in the response.\n\nSource, target, or both can be omitted, producing slightly different results. Let's check the five possible variants.\n\n#### `\"key\"` variant (mirror)\n\nIf there is no arrow symbol it means that source and target are the same.\nThis is the most frequent use-case, when the response structure mirrors exactly the query structure.\n\n```json\n{\n  \"movie\": {\n    \"title\": true\n  }\n}\n```\n\n\u003e Note: The key `title` could be expressed by `title=\u003etitle`; it would work too.\n\nNot surprisingly, this will return something like this:\n\n```json\n{\n  \"movie\": {\n    \"title\": \"Inception\"\n  }\n}\n```\n\n#### `\"sourceKey=\u003etargetKey\"` variant (alias)\n\nIf source and target are different, the result of the evaluation of `sourceKey` will appear under a key called `targetKey` in the response.\n\nFor example `createdAt=\u003edate` key means the `createdAt` field value will appear under a key called `date` in the response.\n\nYou can think about it as a way to create aliases, similarly to the GraphQL's [aliasing feature](https://graphql.org/learn/queries/#aliases).\n\nBy using aliases, it is possible to execute a method more than once with different parameters, avoiding name collisions inside the result.\n\nFor example, in the following query, we first call the `getMovies` method and assign the result to `actionMovies`, then we call the same `getMovies` method, with different parameters, and assign the result to `dramaMovies`.\n\n```json\n{\n  \"getMovies=\u003eactionMovies\": {\n    \"()\": [{\"filter\": {\"genre\": \"action\"}}],\n    \"=\u003e\": {\n      \"[]\": [],\n      \"title\": true\n    }\n  },\n  \"getMovies=\u003edramaMovies\": {\n    \"()\": [{\"filter\": {\"genre\": \"drama\"}}],\n    \"=\u003e\": {\n      \"[]\": [],\n      \"title\": true\n    }\n  }\n}\n```\n\nDoing this we get both `actionMovies` and `dramaMovies` results in the response, like this:\n\n```json\n{\n  \"actionMovies\": [\n    {\n      \"title\": \"Inception\"\n    },\n    {\n      \"title\": \"The Matrix\"\n    }\n  ],\n  \"dramaMovies\": [\n    {\n      \"title\": \"Forrest Gump\"\n    }\n  ]\n}\n```\n\n#### `\"=\u003etargetKey\"` variant (nest)\n\nIf the source is omitted, it means the current context will be re-used in the response as it is, without any processing.\n\nFor example, in the following query, `=\u003eitems` means we take the current context and put it inside an object whose key is `items`. Basically, we are nesting the current context one level deeper, under a new key.\n\n```json\n{\n  \"movies\": {\n    \"count\": true,\n    \"=\u003eitems\": {\n      \"[]\": [],\n      \"title\": true\n    }\n  }\n}\n```\n\nDoing this, we can query both a collection and its items to produce results such as:\n\n```json\n{\n  \"movies\": {\n    \"count\": 2,\n    \"items\": [\n      {\n        \"title\": \"Inception\"\n      },\n      {\n        \"title\": \"The Matrix\"\n      }\n    ]\n  }\n}\n```\n\n#### `\"sourceKey=\u003e\"` variant (unnest)\n\nIf the target is omitted, it means that the evaluation of a method (or field) does not generate a new object. We call this feature \"Unnesting\".\n\nFor example, if we are only interested in the title of the movie we found, we can do this:\n\n```json\n{\n  \"movie\": {\n    \"title=\u003e\": true\n  }\n}\n```\n\nBecause we use the key `\"title=\u003e\"` instead of `\"title\"`, the key `title` is absent from the response:\n\n```json\n{\n  \"movie\": \"Inception\"\n}\n```\n\n#### `\"=\u003e\"` variant (return)\n\nLastly, we can remove both the source and the target from the key expression, leaving alone the arrow symbol `=\u003e`.\n\nIn this case, we do not process the current context (no source) and we are not creating new keys in the response (no target).\n\nThe `=\u003e` can be interpreted as a way to introduce the result of a function call.\n\nIn the following query, we retrieve a movie by its `id`, and we return `title` and `year` fields in the response.\n\n```json\n{\n  \"getMovie\": {\n    \"()\": [\"cjrts72gy00ik01rv6eins4se\"],\n    \"=\u003e\": {\"title\": true, \"year\": true}\n  }\n}\n```\n\nNote that the following query is exactly the same:\n\n```json\n{\n  \"getMovie\": {\n    \"()\": [\"cjrts72gy00ik01rv6eins4se\"],\n    \"title\": true,\n    \"year\": true\n  }\n}\n```\n\nBoth queries will produce the following response:\n\n```json\n{\n  \"getMovie\": {\n    \"title\": \"Inception\",\n    \"year\": 2010\n  }\n}\n```\n\nThis feature is particularly useful to access the items of a collection. For example:\n\n```json\n{\n  \"getMovies\": {\n    \"()\": [{\"filter\": {\"country\": \"USA\"}}],\n    \"=\u003e\": {\n      \"[]\": [],\n      \"title\": true\n    }\n  }\n}\n```\n\nWill output:\n\n```json\n{\n  \"getMovies\": [\n    {\"title\": \"Inception\"},\n    {\"title\": \"The Matrix\"},\n    {\"title\": \"Forest Gump\"}\n  ]\n}\n```\n\n### Values\n\nQuery objects are evaluated in a recursive way, and for every key the related value can be either:\n\n- the boolean `true`\n- an object\n- an array\n\nLet's see how Deepr handles these three types of values.\n\n#### Boolean `true`\n\nThe boolean `true` means the value of a field will be included as is in the response.\n\nIf we query a single movie this way:\n\n```json\n{\n  \"movie\": {\n    \"title\": true,\n    \"year\": true\n  }\n}\n```\n\nWe get the following result:\n\n```json\n{\n  \"movie\": {\n    \"title\": \"Inception\",\n    \"year\": 2010\n  }\n}\n```\n\n#### Object\n\nWhen the value is an object, the execution continues recursively:\n\n```json\n{\n  \"movie\": {\n    \"director\": {\n      \"name\": true\n    }\n  }\n}\n```\n\nAs expected, this will produce:\n\n```json\n{\n  \"movie\": {\n    \"director\": {\n      \"name\": \"George Lucas\"\n    }\n  }\n}\n```\n\n#### Array\n\nFinally, by using an array, we can specify a sequence of subqueries to be executed in order. For example:\n\n```json\n{\n  \"movies\": [\n    {\n      \"getByTitle=\u003e\": {\n        \"()\": [\"Inception\"],\n        \"title\": true\n      }\n    },\n    {\n      \"getByTitle=\u003e\": {\n        \"()\": [\"The Matrix\"],\n        \"title\": true\n      }\n    }\n  ]\n}\n```\n\nWill return:\n\n```json\n{\n  \"movies\": [{\"title\": \"Inception\"}, {\"title\": \"The Matrix\"}]\n}\n```\n\n### Fault-tolerant queries\n\nIf you add a question mark (`?`) after the name of a key, then no error will be thrown in case a field or a method is missing during the execution of a query.\n\nFor example, the following query will succeed even if the movie has no director:\n\n```json\n{\n  \"movie\": {\n    \"title\": true,\n    \"director?\": {\n      \"fullName\": true\n    }\n  }\n}\n```\n\nRather than throwing an error, this will just return:\n\n```json\n{\n  \"movie\": {\n    \"title\": \"Inception\"\n  }\n}\n```\n\n### Chained queries\n\nNow, let's put into practice what we have just seen to compose a more complex query:\n\n```json\n{\n  \"movies\": {\n    \"filter=\u003e\": {\n      \"()\": [{\"country\": \"USA\"}],\n      \"sort=\u003e\": {\n        \"()\": [{\"by\": \"year\"}],\n        \"skip=\u003e\": {\n          \"()\": [5],\n          \"limit=\u003e\": {\n            \"()\": [10],\n            \"=\u003e\": {\n              \"[]\": [],\n              \"title\": true,\n              \"year\": true\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\nDespite the fact that we have nested several method calls, this will just return:\n\n```json\n{\n  \"movies\": [\n    {\n      \"title\": \"The Matrix\",\n      \"year\": 1999\n    },\n    {\n      \"title\": \"Inception\",\n      \"year\": 2010\n    }\n  ]\n}\n```\n\n### Mutations\n\nSo far we have invoked methods that read data without producing any side effects on the server. Let's fix that by executing some simple CRUD operations.\n\n#### Create\n\nHere is how we could create a record:\n\n```json\n{\n  \"movies=\u003e\": {\n    \"create=\u003emovie\": {\n      \"()\": [{\"title\": \"Avatar\", \"country\": \"USA\"}],\n      \"=\u003e\": {\"id\": true}\n    }\n  }\n}\n```\n\nUnlike GraphQL, Deepr does not differentiate queries and mutations. So, performing a mutation is just a matter of calling a method.\n\nThe query above will return:\n\n```json\n{\n  \"movie\": {\n    \"id\": \"cjrts72gy00ik01rv6eins4se\"\n  }\n}\n```\n\n#### Read\n\nNow that we have added a movie, let's retrieve it:\n\n```json\n{\n  \"movies=\u003e\": {\n    \"get=\u003emovie\": {\n      \"()\": [{\"id\": \"cjrts72gy00ik01rv6eins4se\"}],\n      \"=\u003e\": {\"id\": true, \"title\": true, \"country\": true}\n    }\n  }\n}\n```\n\nThis will return:\n\n```json\n{\n  \"movie\": {\n    \"id\": \"cjrts72gy00ik01rv6eins4se\",\n    \"title\": \"Avatar\",\n    \"country\": \"USA\"\n  }\n}\n```\n\n#### Update\n\nTo modify a record, we can do this with:\n\n```json\n{\n  \"movies=\u003e\": {\n    \"get=\u003emovie\": {\n      \"()\": [{\"id\": \"cjrts72gy00ik01rv6eins4se\"}],\n      \"update=\u003e\": {\n        \"()\": [{\"rating\": 8.1}],\n        \"=\u003e\": {\"id\": true}\n      }\n    }\n  }\n}\n```\n\nNote how we use the key `\"update=\u003e\"` instead of `\"update\"` to avoid creating an unnecessary `\"update\"` key in the response:\n\n```json\n{\n  \"movie\": {\n    \"id\": \"cjrts72gy00ik01rv6eins4se\"\n  }\n}\n```\n\n#### Delete\n\nFinally, here is how we can delete a record:\n\n```json\n{\n  \"movies=\u003e\": {\n    \"get=\u003emovie\": {\n      \"()\": [{\"id\": \"cjrts72gy00ik01rv6eins4se\"}],\n      \"delete=\u003e\": {\n        \"()\": [],\n        \"id\": true\n      }\n    }\n  }\n}\n```\n\nThis will produce the following result:\n\n```json\n{\n  \"movie\": {\n    \"id\": \"cjrts72gy00ik01rv6eins4se\"\n  }\n}\n```\n\n### Source values\n\nSometimes it is useful to execute a query from a source value. To do so, we can use the \"\u003c=\" key.\n\nUsing this feature, the previous example for creating a movie could be written as follows:\n\n```json\n{\n  \"\u003c=\": {\"_type\": \"Movie\", \"title\": \"Avatar\", \"country\": \"USA\"},\n  \"save=\u003emovie\": {\n    \"()\": [],\n    \"id\": true\n  }\n}\n```\n\nAs before, this will return:\n\n```json\n{\n  \"movie\": {\n    \"id\": \"cjrts72gy00ik01rv6eins4se\"\n  }\n}\n```\n\n### Parallel queries\n\nContrary to GraphQL, the default execution model of Deepr is sequential. So when a query is composed of several subqueries, the subqueries are executed one by one from top to bottom.\n\nTo specify that some subqueries should be executed in parallel, you can use the special key `||` and list each subquery in an array.\n\nFor example, the following query will execute the `getMovie()` method two times in a concurrent way:\n\n```json\n{\n  \"||\": [\n    {\n      \"getMovie\": {\n        \"()\": [{\"id\": \"abc123\"}],\n        \"title\": true\n      }\n    },\n    {\n      \"getMovie\": {\n        \"()\": [{\"id\": \"def456\"}],\n        \"title\": true\n      }\n    }\n  ]\n}\n```\n\nThe result is returned as follows:\n\n```json\n[\n  {\n    \"getMovie\": {\n      \"title\": \"Inception\"\n    }\n  },\n  {\n    \"getMovie\": {\n      \"title\": \"The Matrix\"\n    }\n  }\n]\n```\n\n### Relations\n\nThis guide would not be complete without demonstrating another common use case: the ability to query relationships between collections. It is actually pretty straightforward. Here is how we can fetch some movies with their related actors:\n\n```json\n{\n  \"getMovies=\u003emovies\": {\n    \"()\": [{\"filter\": {\"country\": \"USA\"}}],\n    \"=\u003e\": {\n      \"[]\": [],\n      \"title\": true,\n      \"year\": true,\n      \"getActors=\u003eactors\": {\n        \"()\": [{\"sort\": {\"by\": \"popularity\"}, \"limit\": 2}],\n        \"=\u003e\": {\n          \"[]\": [],\n          \"fullName\": true,\n          \"photoURL\": true\n        }\n      }\n    }\n  }\n}\n```\n\nThis will return:\n\n```json\n{\n  \"movies\": [\n    {\n      \"title\": \"Inception\",\n      \"year\": 2010,\n      \"actors\": [\n        {\n          \"fullName\": \"Leonardo DiCaprio\",\n          \"photoURL\": \"https://www.imdb.com/name/nm0000138/mediaviewer/rm487490304\"\n        },\n        {\n          \"fullName\": \"Joseph Gordon-Levitt\",\n          \"photoURL\": \"https://www.imdb.com/name/nm0330687/mediaviewer/rm1175888384\"\n        }\n      ]\n    },\n    {\n      \"title\": \"The Matrix\",\n      \"year\": 1999,\n      \"actors\": [\n        {\n          \"fullName\": \"Keanu Reeves\",\n          \"photoURL\": \"https://www.imdb.com/name/nm0000206/mediaviewer/rm3751520256\"\n        },\n        {\n          \"fullName\": \"Laurence Fishburne\",\n          \"photoURL\": \"https://www.imdb.com/name/nm0000401/mediaviewer/rm1925683200\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n### Subscriptions\n\nWe do not believe that [subscriptions](https://facebook.github.io/graphql/draft/#sec-Subscription) should be included in the core specifications of Deepr. We acknowledge it is an important feature, though, and it might be added later in the form of an extension.\n\n## Runtimes\n\nTo execute a Deepr query, you need a runtime.\n\n### Reference runtime\n\nHere is the reference runtime implemented in TypeScript:\n\n- JavaScript/TypeScript: https://github.com/deeprjs/deepr/tree/master/packages/runtime\n\n### Community runtimes\n\nThe community is free to implement alternative runtimes.\n\nHere is the first runtime implemented by the community:\n\n- PHP: https://github.com/stefanak-michal/deepr-php\n\n## Specifications\n\nAlthough pretty stable, Deepr is a work in progress, and formal specifications still have to be written.\n\n## Logo\n\nSubmarine by Andrejs Kirma from the Noun Project.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeeprjs%2Fdeepr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeeprjs%2Fdeepr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeeprjs%2Fdeepr/lists"}