{"id":20595520,"url":"https://github.com/thefrontside/hydraphql","last_synced_at":"2025-09-09T06:39:58.223Z","repository":{"id":192903819,"uuid":"681266105","full_name":"thefrontside/HydraphQL","owner":"thefrontside","description":"Tool that DRY and then Hydrate your GraphQL schema","archived":false,"fork":false,"pushed_at":"2024-09-14T15:12:46.000Z","size":1277,"stargazers_count":4,"open_issues_count":4,"forks_count":0,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-09-06T15:24:28.023Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/thefrontside.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null}},"created_at":"2023-08-21T16:25:23.000Z","updated_at":"2024-09-14T15:12:07.000Z","dependencies_parsed_at":"2023-12-12T15:26:20.665Z","dependency_job_id":"335ba699-5add-4dcf-a17e-a41dce9707cd","html_url":"https://github.com/thefrontside/HydraphQL","commit_stats":null,"previous_names":["thefrontside/hydraphql"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/thefrontside/HydraphQL","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thefrontside%2FHydraphQL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thefrontside%2FHydraphQL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thefrontside%2FHydraphQL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thefrontside%2FHydraphQL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thefrontside","download_url":"https://codeload.github.com/thefrontside/HydraphQL/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thefrontside%2FHydraphQL/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273978867,"owners_count":25201413,"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","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-11-16T08:13:21.892Z","updated_at":"2025-09-09T06:39:58.170Z","avatar_url":"https://github.com/thefrontside.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HydraphQL\n\nHydraphQL provides functionality to organize your Schema by reducing\nrepetition and augmenting it with [GraphQL modules][graphql-modules] defining new\ntypes and how to resolve them.\n\n- [Directives API](#directives-api)\n  - [`@field`](#field)\n  - [`@implements`](#implements)\n  - [`@discriminates`](#discriminates)\n  - [`@resolve`](#resolve)\n- [Getting started](#getting-started)\n  - [GraphQL Application](#graphql-application)\n  - [Extending Schema](#extending-your-schema-with-a-custom-module)\n  - [@graphql-codegen/TypeScript](#graphql-codegentypescript)\n\n## Directives API\n\nWith GraphQL modules you can structure your graphql code base,\nbut you still have to write resolvers using TypeScript code. However,\none of the most important advantages of HydraphQL is that most of the\ntime you don't need to write any TypeScript at all. Instead, you can tell\nGraphQL what it should do just by adding hints directly to the Schema about\nwhich fields map to what. These hints are called `directives`.\n\nThe following directives will tell GraphQL how to write resolvers\nautomatically, so that you don't have to.\n\n### `@field`\n\nThe @field directive allows you to access properties on an object\nusing a given path. It allows you to specify a resolver for a field\nfrom the schema without actually writing a real resolver at all. Under\nthe hood, it's creating the resolver for you. To see this in action,\ncheck out the\n[`catalog.graphql`](https://github.com/thefrontside/playhouse/blob/main/plugins/graphql-backend-module-catalog/src/catalog/catalog.graphql)\nwhich uses the `@field` directive extensively module to retrieve\nproperties like `namespace`, `title` and others.\n\n1. Mapping `namespace.name` field from source data to `Entity#name` field:\n\n```graphql\ntype Entity {\n  name: String! @field(at: \"namespace.name\")\n}\n```\n\n2. If source path's fields contain dots `{ spec: { \"data.label\": \"...\" } }`, you can use an array:\n\n```graphql\ntype Entity {\n  label: String @field(at: [\"spec\", \"data.label\"])\n}\n```\n\n3. You can specify a default value as a fallback if the field is not found:\n\n```graphql\ntype Entity {\n  tag: String! @field(at: \"spec.tag\", default: \"N/A\")\n}\n```\n\n### `@implements`\n\nThe `@implements` directive allows you to inherit fields from another\ninterface. We created this directive to make it easier to implement\ninterfaces that inherit from other interfaces. It makes GraphQL types\nsimilar to extending types in TypeScript. In TypeScript, when a class\ninherits another class, the child class automatically inherits\nproperties and methods of the parent class. This functionality doesn't\nhave an equivalent in GraphQL. Without this directive, the `Service`\ninterface in GraphQL would need to re-implement many fields that are\ndefined on implemented interfaces which leads to lots of duplication.\n\n1. Use this directive to define a new type that\n   includes all of the properties of the parent interface.\n\n```graphql\ntype Service @implements(interface: \"Component\") {\n  endpoint: String! @field(at: \"spec.endpoint\")\n}\n```\n\nIn the output schema it is transformed into:\n\n```graphql\ntype Service implements Component \u0026 Entity \u0026 Node {\n  id: ID!\n  name: String!\n  kind: String!\n  namespace: String!\n  # ... rest `Entity` and `Component` fields ...\n\n  endpoint: String!\n}\n```\n\n### `@discriminates`\n\nThe `@discriminates` directive tells the GraphQL App that an interface\nbe discriminated by a given value to another interface or a type.\nThe value by path from `with` argument is used to determine to which\ntype the interface should be resolved.\n\n```graphql\ninterface Entity\n  @implements(interface: \"Node\")\n  @discriminates(with: \"kind\") {\n    # ...\n  }\n\ntype Component @implements(interface: \"Entity\") {\n  # ...\n}\ntype Service @implements(interface: \"Entity\") {\n  # ...\n}\n```\n\n_NOTE: In this example if we have data of `Entity` type and it has `kind` field_\n_with `Component` value, that means data will be resolved to `Component` type_\n\n#### `opaqueType`\n\nThere is a special case when your runtime data doesn't have a value\nthat can be used to discriminate the interface or there is no type\nthat matches the value. In this case, you can define `opaqueType` argument\n\n```graphql\ninterface Entity\n  @implements(interface: \"Node\")\n  @discriminates(with: \"kind\", opaqueType: \"OpaqueEntity\") {\n    # ...\n  }\n```\n\nIn this case, if the value of `kind` field doesn't match with any schema type,\nthe `OpaqueEntity` type will be used. You don't need to define this type, the GraphQL\nplugin will generate it for you.\n\nThere is another way to define opaque types for all interfaces by using `generateOpaqueTypes`\noption for GraphQL plugin.\n\n#### `aliases`\n\nBy default value from `with` argument is used to find a type as-is or converted to PascalCase.\nSometimes you need to match the value with a type that has a different name.\nIn this case, you can define `aliases` argument.\n\n```graphql\ninterface API\n  @implements(interface: \"Node\")\n  @discriminates(with: \"spec.type\", aliases: [{ value: \"grpc\", type: \"GrpcAPI\" }]) {\n    # ...\n  }\n\ntype GrpcAPI @implements(interface: \"API\") {\n  # ...\n}\n```\n\nThis means, when `spec.type` equals to `grpc`, the `API` interface will be resolved to `GrpcAPI` type.\n\n### `@resolve`\n\nThe `@resolve` directive is similar to the `@field` directive, but instead of\nresolving a field from the source data, it resolves a field from a 3rd party\nAPI. This is useful when you want to add fields to your schema that are not\navailable in the source data, but are available from another API.\n\n1. To achieve that first of all you need to create a DataLoader with `createLoader` function\n\n```ts\nimport querystring from \"querystring\";\nimport { createLoader, NodeQuery } from \"@frontside/hydraphql\";\n\nexport const loader = createLoader({\n  async ExampleCom(queries: NodeQuery[]) {\n    return Promise.all(\n      queries.map(async ({ ref, args }) =\u003e {\n        const response = await fetch(\n          `https://example.com/api/${ref}?${querystring.stringify(args)}`,\n        );\n        return response.json();\n      }),\n    );\n  },\n});\n```\n\n2. Then you can use the `@resolve` directive with specifying the loader name of your API:\n\n```graphql\ntype Project {\n  tasks: [Task!] @resolve(at: \"spec.projectId\", from: \"ExampleCom\")\n}\n```\n\n## Getting started\n\n### GraphQL Application\n\nSince HydraphQL uses GraphQL Modules `createGraphQLApp` returns a GraphQL Application\nwhich can be used with all popular GraphQL servers like `apollo-server`, `express-graphql`, `graphql-yoga` and others.\n\n1. Create an application by using `createGraphQLApp` function\n\n```ts\nimport {\n  createGraphQLApp,\n  createLoader,\n  NodeQuery,\n} from \"@frontside/hydraphql\";\nimport modules from \"./modules\";\n\nexport async function main() {\n  const application = await createGraphQLApp({ modules });\n  const loader = createLoader({\n    async MyAPI(queries: NodeQuery[]) {\n      // ...\n    },\n  });\n\n  //...\n}\n```\n\n2. [Use the application with your GraphQL server](https://the-guild.dev/graphql/modules/docs/get-started#use-your-application)\n\n_NOTE_ You can find a simple example of how to use HydraphQL with `graphql-yoga` in [`examples/graphql-yoga`](./examples/graphql-yoga) directory.\n\n### Extending your schema with a custom module\n\nTo extend your schema, you will define it using the GraphQL Schema Definition\nLanguage, and then (optionally) write resolvers to handle the various types\nwhich you defined.\n\n1. Create modules directory where you'll store all your GraphQL modules, for example in `./src/modules`\n2. Create a module directory `my-module` there\n3. Create a GraphQL schema file `my-module.graphql` in the module directory\n\n```graphql\nextend type Query {\n  hello: String!\n}\n```\n\nThis code adds a `hello` field to the global `Query` type. Next, we are going to\nwrite a module containing this schema and its resolvers.\n\n4. Create a GraphQL module file `my-module.ts` in the module directory\n\n```ts\nimport { loadFilesSync } from \"@graphql-tools/load-files\";\nimport { createModule } from \"graphql-modules\";\n\nexport const myModule = createModule({\n  id: \"my-module\",\n  dirname: __dirname,\n  typeDefs: loadFilesSync(require.resolve(\"./my-module.graphql\")),\n  resolvers: {\n    Query: {\n      hello: () =\u003e \"world\",\n    },\n  },\n});\n```\n\n## `@graphql-codegen`/TypeScript\n\nIf you use `@graphql-codegen` to generate an output schema to use it for\nvalidating frontend queries and/or TypeScript to have type checking in\nGraphQL modules resolvers, you'll need modify your `@graphql-codegen` config.\n\n1. First of all create a `schema.ts` file with `transformSchema`\n   function and pass all your GraphQL files\n\n```ts\n// ./src/schema.ts\nimport { transformSchema } from \"@frontside/hydraphql\";\nimport { printSchemaWithDirectives } from \"@graphql-tools/utils\";\nimport { MyModule } from \"./modules/my-module/my-module\";\n\nexport const schema = printSchemaWithDirectives(transformSchema([MyModule]));\n```\n\n2. Then you need to update `schema` option in your `codegen.ts`\n\n```ts\nimport { CodegenConfig } from \"@graphql-codegen/cli\";\nimport { schema } from \"./src/schema\";\n\nconst config: CodegenConfig = {\n  schema,\n  generates: {\n    /* ... */\n  },\n};\n\nexport default config;\n```\n\n[graphql-modules]: https://the-guild.dev/graphql/modules\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthefrontside%2Fhydraphql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthefrontside%2Fhydraphql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthefrontside%2Fhydraphql/lists"}