{"id":13847303,"url":"https://github.com/equalogic/typeorm-relations-graphql","last_synced_at":"2025-07-29T16:33:28.074Z","repository":{"id":37007133,"uuid":"249107855","full_name":"equalogic/typeorm-relations-graphql","owner":"equalogic","description":"Automatically join TypeORM relations based on fields selected in a GraphQL query.","archived":false,"fork":false,"pushed_at":"2024-07-13T23:40:36.000Z","size":4321,"stargazers_count":17,"open_issues_count":8,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-07-14T09:31:18.725Z","etag":null,"topics":["graphql","typeorm"],"latest_commit_sha":null,"homepage":"","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/equalogic.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-22T03:46:21.000Z","updated_at":"2024-07-16T02:13:10.921Z","dependencies_parsed_at":"2023-12-21T02:30:29.721Z","dependency_job_id":"29954479-4e4e-492b-8386-38dd3ef54506","html_url":"https://github.com/equalogic/typeorm-relations-graphql","commit_stats":{"total_commits":732,"total_committers":6,"mean_commits":122.0,"dds":"0.46584699453551914","last_synced_commit":"cb93c17a6adeb14a54b6ab3b63782ef6a2c6f520"},"previous_names":["equalogic/typeorm-graphql-joiner"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/equalogic%2Ftypeorm-relations-graphql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/equalogic%2Ftypeorm-relations-graphql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/equalogic%2Ftypeorm-relations-graphql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/equalogic%2Ftypeorm-relations-graphql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/equalogic","download_url":"https://codeload.github.com/equalogic/typeorm-relations-graphql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228030027,"owners_count":17858432,"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":["graphql","typeorm"],"created_at":"2024-08-04T18:01:16.113Z","updated_at":"2024-12-04T02:13:34.966Z","avatar_url":"https://github.com/equalogic.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/equalogic/typeorm-relations-graphql/raw/master/resources/logo@720w.png\" width=\"720\" height=\"420\"\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://npmjs.com/package/typeorm-relations-graphql\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/typeorm-relations-graphql\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://npmjs.com/package/typeorm-relations-graphql\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dy/typeorm-relations-graphql\"\u003e\n  \u003c/a\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n\u003c/div\u003e\n\nAutomatically determine the entity relationships that must be `JOIN`ed in a TypeORM query to satisfy nested object\nfields selected by a client in a GraphQL query.\n\nCan be used as a potentially higher performance alternative to the [DataLoader pattern](https://github.com/graphql/dataloader).\n\n---\n\n## Installation\n\n```\nnpm i typeorm-relations-graphql\n```\n\nThis library is written in TypeScript, so type definitions are included in the box.\n\nYour project must also install the following as peer dependencies (you should have them already):\n\n- [typeorm](https://typeorm.io/) v0.3.x\n- [graphql](https://www.npmjs.com/package/graphql) v14.x or higher\n\nNote: typeorm v0.3.0 [changed](https://typeorm.io/changelog#030httpsgithubcomtypeormtypeormpull8616-2022-03-17) the way\nrelations and data sources work. If you are still using typeorm v0.2.x, please install\n[typeorm-graphql-joiner@^1](https://github.com/equalogic/typeorm-relations-graphql/blob/1.x/README.md) and read the usage\ninstructions for that version.\n\n---\n\n## Introduction\n\nWhen your GraphQL server is backed by TypeORM entities, you may have object relationships like the following example:\n\n```\n{\n  // Product entity\n  \"product\": {\n    \"id\": \"1234\",\n    \"name\": \"Some product\",\n    // nested Owner entity\n    \"owner\": {\n      \"id\": \"4321\",\n      \"name\": \"Some owner\"\n    }\n  }\n}\n```\n\nLet's say `product` corresponds to a `Product` entity in TypeORM, and it has a\n[many-to-one relationship](https://typeorm.io/#/many-to-one-one-to-many-relations) to an `Owner` entity defined on the\n`product.owner` property. In your database, you have a table for each of these entities.\n\nNow you want to expose `Product` as an object in your GraphQL schema with the same relationship. You could simply\nresolve `product.owner` using a database query to fetch the related `Owner` object, but if you create a GraphQL resolver\nthat returns a list of _n_ products then your server will need to perform _n_ + 1 database queries to fully resolve it.\nThis problem multiplies exponentially as your schema grows more complex and you have levels of nested relationships.\n\n**typeorm-relations-graphql** can help here by optimizing these relationships into SQL `JOIN`s. Instead of fetching the\n`product` and then each `owner` individually, it enables you to fetch the `product` with all requested relationships\nin a single database query by making use of TypeORM's `relations` [option](https://typeorm.io/#/find-options) on `find`\nmethods.\n\nSo in this simple example, instead of your resolvers producing this SQL:\n\n```sql\nSELECT * FROM product;\nSELECT * FROM owner WHERE product_id = :x;\nSELECT * FROM owner WHERE product_id = :y;\nSELECT * FROM owner WHERE product_id = :z;\n```\n\nYou can optimize it to:\n\n```sql\nSELECT * FROM product LEFT JOIN owner ON product.id = owner.product_id;\n```\n\nThe value of this optimization increases as you have greater levels of nesting, of course.\n\nYou could join these relations manually (or eagerly) with TypeORM, but then you are likely to end up overfetching -\nretrieving relations that were not requested by the client and producing SQL that is more expensive than necessary.\n**typeorm-relations-graphql** only joins relations that were requested in the client's GQL query.\n\nYou could also use a [DataLoader](https://github.com/slaypni/type-graphql-dataloader) to batch requests, but this will\nusually still result in more database queries than are produced by joining relations. Beware however that large joins\nwith many levels of nesting can be bad for performance, too, so you may need to mix approaches.\n\n---\n\n## Usage\n\nFirst, create a `GraphRelationBuilder` instance, passing in a TypeORM `DataSource` object (which provides access to\nentity metadata):\n\n```ts\nimport { GraphRelationBuilder } from 'typeorm-relations-graphql';\nimport { dataSource } from './datasource';\n\nconst graphRelationBuilder = new GraphRelationBuilder(dataSource);\n```\n\nInside a GraphQL query resolver (where you have a `GraphQLResolveInfo` object available) you can use\n`GraphRelationBuilder` to determine the relations you need to join to fulfill the query.\n\nThe `build` and `buildForQuery` methods of `GraphRelationBuilder` return a `RelationMap` instance. This is a class\nprovided by the [typeorm-relations package](https://www.npmjs.com/package/typeorm-relations) and contains methods that\nyou can use to manipulate the relations before passing them to TypeORM. Read the\n[typeorm-relations documentation](https://github.com/equalogic/typeorm-relations#readme) to learn more about it.\n\n### GraphRelationBuilder\n\n#### `buildForQuery(entity: Constructor\u003cEntity\u003e, info: GraphQLResolveInfo): RelationMap\u003cEntity\u003e`\n\nBuilds a `RelationMap` for an entity class by mapping from the root of the GraphQL query.\n\nThe `entity` passed in should be an entity class constructor (not an instance of the entity).\n\nFor example, if you have a `products` query in your GQL schema which returns a list of `Product` entities (where the\n`Product` entity and `Product` GQL object type are equivalent), you can simply map `Product` relations in this way:\n\n```ts\nimport { GraphQLResolveInfo } from 'graphql';\nimport { dataSource } from './datasource';\n\n// Example resolver function for a \"products\" query in your GQL schema\nfunction products(source: any, args: any, context: any, info: GraphQLResolveInfo): Promise\u003cProduct[]\u003e {\n  const graphRelationBuilder = new GraphRelationBuilder(dataSource);\n\n  const productRelationMap = graphRelationBuilder.buildForQuery(Product, info);\n\n  return dataSource.getRepository(Product).find({\n    relations: productRelationMap.toFindOptionsRelations(),\n  });\n}\n```\n\nIn this example if your `Product` entity has an `owner` property that relates to another entity, and the `owner` field\nis selected by the client's GraphQL query, then calling `toFindOptionsRelations()` will produce:\n\n```\n{\n  owner: true\n}\n```\n\nOr, if `owner` contains an additional relationship to an `address` entity which is also selected by the client, you can\nget a nested structure like:\n\n```\n{\n  owner: {\n    address: true\n  }\n}\n```\n\n#### `buildForQuery(entity: Constructor\u003cEntity\u003e, info: GraphQLResolveInfo, path: string): RelationMap\u003cEntity\u003e`\n\nIn some cases you may need to map relations to entity fields where the GQL object type for the entity is not the root\nnode in the query. A common example of this is in a mutation which returns a payload object containing the modified\nobject rather than the object directly. In this case you can pass a `path` string as the last argument to\n`buildForQuery`:\n\n```ts\nimport { GraphQLResolveInfo } from 'graphql';\nimport { dataSource } from './datasource';\n\n// Example resolver function for a \"createProduct\" mutation in your GQL schema\nasync function createProduct(\n  source: any,\n  args: any,\n  context: any,\n  info: GraphQLResolveInfo,\n): Promise\u003cCreateProductPayload\u003e {\n  const graphRelationBuilder = new GraphRelationBuilder(dataSource);\n\n  // Create the new product\n  const product: Product = await dataSource.getRepository(Product).save(\n    dataSource.getRepository(Product).create({\n      name: 'New Product',\n    }),\n  );\n\n  // Create payload and re-fetch the new product to retrieve all requested relations\n  const payload: CreateProductPayload = {\n    success: true,\n    product: await dataSource.getRepository(Product).findOneOrFail({\n      where: { id: product.id },\n      relations: graphRelationBuilder.buildForQuery(Product, info, 'product').toFindOptionsRelations(),\n    }),\n  };\n\n  return payload;\n}\n```\n\nA GraphQL query for this mutation might look like:\n\n```graphql\nmutation {\n  createProduct {\n    success\n    product {\n      id\n      name\n      owner {\n        id\n        name\n      }\n    }\n  }\n}\n```\n\nThe `Product` entity here exists below the root level of the object being resolved (`createProduct`), at a field called\n`product`. So the path `'product'` must be given to `buildForQuery`.\n\nDotted path notation can be used when the entity is at an even lower level in the node tree. For example, the path\n`'product.owner'` could be used to map the `Owner` entity in this example.\n\n#### `build(entity: Constructor\u003cEntity\u003e, baseNode: SelectionNode, fragments?: Record\u003cstring, FragmentDefinitionNode\u003e): GraphRelationBuilder\u003cEntity\u003e`\n\nThis method works like `buildForQuery` (and is called by it internally), but it can operate on an arbitrary\n`SelectionNode` rather than requiring an entire `GraphQLResolveInfo` object.\n\nIf your GQL for the selection may contain named fragments, the definition of those fragments must be passed through.\nThe required data can be retrieved from the `fragments` property on the top level `GraphQLResolveInfo` object.\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fequalogic%2Ftypeorm-relations-graphql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fequalogic%2Ftypeorm-relations-graphql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fequalogic%2Ftypeorm-relations-graphql/lists"}