{"id":13564239,"url":"https://github.com/AEB-labs/graphql-weaver","last_synced_at":"2025-04-03T21:30:34.818Z","repository":{"id":25202485,"uuid":"103269225","full_name":"AEB-labs/graphql-weaver","owner":"AEB-labs","description":"A tool to combine, link and transform GraphQL schemas","archived":true,"fork":false,"pushed_at":"2022-09-20T08:52:07.000Z","size":682,"stargazers_count":240,"open_issues_count":23,"forks_count":20,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-03-17T19:49:02.546Z","etag":null,"topics":["graphql","graphql-schema","typescript"],"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/AEB-labs.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":"2017-09-12T12:42:51.000Z","updated_at":"2024-06-15T22:21:30.000Z","dependencies_parsed_at":"2022-08-07T11:15:25.784Z","dependency_job_id":null,"html_url":"https://github.com/AEB-labs/graphql-weaver","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AEB-labs%2Fgraphql-weaver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AEB-labs%2Fgraphql-weaver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AEB-labs%2Fgraphql-weaver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AEB-labs%2Fgraphql-weaver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AEB-labs","download_url":"https://codeload.github.com/AEB-labs/graphql-weaver/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247082858,"owners_count":20880731,"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","graphql-schema","typescript"],"created_at":"2024-08-01T13:01:28.463Z","updated_at":"2025-04-03T21:30:33.931Z","avatar_url":"https://github.com/AEB-labs.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","Libraries"],"sub_categories":["JavaScript Libraries"],"readme":"# NO LONGER MAINTAINED\n\nWe no longer use this library at AEB, so unfortunately, we are not able to maintain it properly. The repository is therefore archived.\n\n# graphql-weaver\n\n[![npm version](https://badge.fury.io/js/graphql-weaver.svg)](https://npmjs.org/graphql-weaver) [![Build Status](https://github.com/AEB-labs/graphql-weaver/workflows/Node%20CI/badge.svg)](https://github.com/AEB-labs/graphql-weaver/actions?workflow=Node+CI)\n\nA tool to combine, link and transform GraphQL schemas\n\nUse graphql-weaver if you have multiple GraphQL servers and want to combine them into one API. Features like namespacing, links and custom transformation modules allow you to augment the API as you like.\n\n**[Try it online in Apollo Launchpad](https://launchpad.graphql.com/0m5x0k495)**\n\n## How to use\n\n```bash\nnpm install --save graphql-weaver\n```\n\nBasic usage:\n\n```typescript\nconst schema: GraphQLSchema = await weaveSchemas({\n    endpoints: [{\n        namespace: 'model',\n        typePrefix: 'Model',\n        url: 'http://localhost:8080/graphql' // url to a GraphQL endpoint\n    }, {\n        namespace: 'local',\n        schema: new GraphQLSchema(/* ... */) // use schema instance directly\n    }]\n})\n```\n\nA *woven schema* is an executable GraphQL schema built from several *endpoints*. For each endpoint, you can either specify a URL to a GraphQL server, pass an executable GraphQL schema instance, or implement the [`GraphQLClient`](src/graphql-client/graphql-client.ts) interface yourself.\n\nIn its basic configuration, `weaveSchemas` merges the query, mutation and subscription fields of all endpoints. To avoid name collisions, you can specify the `namespace` and `typePrefix` properties like seen above. The `typePrefix` will be prepended to all types; `namespace` causes the fields of this endpoint to be wrapped in a field, to be queried via `{ model { aFieldOfModel } }`.\n\n### Links\n\nIn the spirit of GraphQL, this tool allows you to create links between objects of different endpoints. Suppose you have a music recommendation service and a music library service. You can make the whole properties of a song available in the recommendation API without the recommendation service knowing all song properties.\n\n```typescript\nconst schema: GraphQLSchema = await weaveSchemas({\n    endpoints: [{\n        namespace: 'library',\n        url: 'http://example.com/library/graphql'\n    }, {\n        namespace: 'recommendations',\n        url: 'http://example.com/recommendations/graphql',\n        fieldMetadata: {\n            'Recommendation.song': { // Field song in type Recommendation\n                link: {\n                    field: 'library.Song', // field Song in namespace library\n                    argument: 'id', // argument of library.Song\n                    batchMode: false,\n                }\n            }\n        }\n     }]\n});\n```\nThis assumes the library schema has a field `Song` with argument `id`, and the recommendations schema has a type `Recommendation` with a field `song` which contains the song id. Then, you can query the recommendations with all song information like this:\n\n```graphql\nquery {\n    recommendations {\n        myRecommendations {\n            recommendedAt\n            song {\n                id\n                artist\n                title\n                year\n            }\n        }\n    }\n}\n```\n\nIf there are many recommendations, this is ineficcient because all songs are queried independently.  If the library schema supports querying multiple songs at once, you can set `batchMode` to `true`. If the library schema may return the songs in a different order than the ids its get, you need to set `keyField` too.\n\n```typescript\nconst schema: GraphQLSchema = await weaveSchemas({\n    endpoints: [{\n        namespace: 'library',\n        url: 'http://example.com/library/graphql'\n    }, {\n        namespace: 'recommendations',\n        url: 'http://example.com/recommendations/graphql',\n        fieldMetadata: {\n            'Recommendation.song': {\n                link: {\n                    field: 'library.allSongs',\n                    argument: 'filter.ids', // allSongs has an argument filter with an array field ids\n                    batchMode: true,\n                    keyField: 'id' // the name of a field in Song type that contains the id\n                }\n            }\n        }\n     }]\n});\n```\n\nIn the case where you want to perfom a link from a single id to a list of many related items, include `linkFieldName` as an alias for the linked list of items, and the `oneToMany` flag. Since we are making a single request for a GraphQL list of items, `batchMode` is not required and should be set to `false`. \n```typescript\nconst schema: GraphQLSchema = await weaveSchemas({\n    endpoints: [{\n        namespace: 'library',\n        url: 'http://example.com/library/graphql'\n    }, {\n        namespace: 'recommendations',\n        url: 'http://example.com/recommendations/graphql',\n        fieldMetadata: {\n            'Artist.name': { // Field name in type Artist\n                link: {\n                    field: 'library.allSongs', // field allSongs in namespace library\n                    argument: 'filter.artistName', // argument of library.Song\n                    batchMode: false, // batchMode must be set to false\n                    oneToMany: true,\n                    linkFieldName: 'songsByThisArtist' // the \"virtual\" field that will be populated\n                }\n            }\n        }\n     }]\n});\n```\nThis assumes the library schema has a field `allSongs` which accepts a `filter` argument, where one of the filter properties is `artistName`. This also assumes that the recommendations schema has a type `Artist` with a field `name` which contains the artists name. This configuration will add new virtual field `songsByThisArtist` on the `Artist` type which will allow you to query an Artist along with their songs like this:\n\n```graphql\nquery {\n    recommendations {\n        myFavoriteArtists {\n            name\n            genre\n            songsByThisArtist {\n                id\n                title\n                year\n            }\n        }\n    }\n}\n```\n### Joins\n\nWhat if you want to sort the recommendations by the song age, or filter by artist? The recommendation service currently does not know about these fields, so it does not offer an API to sort or order by any of them. Using graphql-weaver, this problem is easily solved:\n\n```typescript\nconst schema: GraphQLSchema = await weaveSchemas({\n    endpoints: [{\n        namespace: 'library',\n        url: 'http://example.com/library/graphql'\n    }, {\n        namespace: 'recommendations',\n        url: 'http://example.com/recommendations/graphql',\n        fieldMetadata: {\n            'Recommendation.song': {\n                link: {\n                    field: 'library.allSongs',\n                    argument: 'filter.ids',\n                    batchMode: true, // is now required\n                    keyField: 'id' // this one too\n                }\n            },\n            'Query.myRecommendations': { // Field myRecommendations on type Query\n                join: {\n                    linkField: 'song', // The field name song in the type Recommendation\n                }\n            }\n        }\n     }]\n});\n```\n\nThis assumes that the library service offers a way to filter and sort songs via the `orderBy` and `filter` arguments. Using it is simple:\n\n```graphql\nquery {\n    recommendations {\n        myRecommendations(filter: { song: { artist: \"Ed Sheeran\" } }, orderBy: song_year_DESC) {\n            recommendedAt\n            song {\n                id\n                artist\n                title\n                year\n            }\n        }\n    }\n}\n```\n\nA note on efficiency: The list of recommendations should be relatively small (not more than a few hundred), as all recommendations need to be fetched so that their ids can be sent to the library for filtering and sorting.\n\n### Custom transformations\n\nAll four presented features (namespaces, type prefixes, links and joins) are implemented as independent modules. If you need something else, you can just write your own module:\n\n```typescript\nclass MyModule implements PipelineModule {\n    transformExtendedSchema(schema: ExtendedSchema): ExtendedSchema {\n        // do something with the schema\n        return schema;\n    }\n    transformQuery(query: Query): Query {\n        // do something with the query\n        return query;\n    }\n}\n\nconst schema: GraphQLSchema = weaveSchemas({\n    endpoints: [{\n        namespace: 'library',\n        url: 'http://example.com/library/graphql',\n        \n    }],\n    pipelineConfig: {\n        transformPreMergePipeline(modules: PipelineModule[], context: PreMergeModuleContext): PipelineModule[] {\n            // These modules are executed for each endpoint\n            return [\n                ...modules,\n                new MyModule()\n            ]\n        },\n        transformPostMergePipeline(modules: PipelineModule[], context: PostMergeModuleContext): PipelineModule[] {\n            // These modules are executed once for the merged schema\n            return [\n                ...modules,\n                new MyModule()\n            ]\n        }\n    }\n});\n```\n\nFor a simple module, see [`TypePrefixModule`](src/pipeline/type-prefixes.ts). The section *Architecture* below gives an overview over the pipeline architecture.\n\nTo simplify modifications to a schema, graphql-weaver ships [`graphql-transformer`](https://github.com/AEB-labs/graphql-transformer) (and [`transformExtendedSchema`](src/extended-schema/extended-schema-transformer.ts)). You can change types and fields as you like with a simple function:\n\n```typescript\nconst transformedSchema = transformSchema(originalSchema, {\n    transformField(field: GraphQLNamedFieldConfig\u003cany, any\u003e, context) {\n        // Rename a field in a type\n        if (context.oldOuterType.name == 'MyType') {\n            return {\n                ...field,\n                name: field.name + 'ButCooler'\n            }\n        }\n        return field;\n    },\n\n    transformObjectType(type: GraphQLObjectTypeConfig\u003cany, any\u003e) {\n        if (type.name == 'MyType') {\n            return {\n                ...type,\n                name: 'MyCoolType'\n            };\n        }\n        return type;\n    },\n\n    transformFields(fields: GraphQLFieldConfigMap\u003cany, any\u003e, context) {\n        // You can even copy types on the fly and transform the copies\n        const type2 = context.copyType(context.oldOuterType, {\n            transformObjectType(typeConfig: GraphQLObjectTypeConfig\u003cany, any\u003e) {\n                return {\n                    ...typeConfig,\n                    name: typeConfig.name + '2'\n                };\n            }\n        });\n\n        // This just adds a reflexive field \"self\" to all types, but its type does not have\n        // the \"self\" field (because it is a copy from the original type, see above)\n        // it also won't have the \"cool\" rename applied because the top-level transformers are not applied\n        return {\n            ...fields,\n            self: {\n                type: type2,\n                resolve: (source: any) =\u003e source\n            }\n        }\n    }\n});\n```\n\nFor more information, refer to the [graphql-transformer](https://github.com/AEB-labs/graphql-transformer) project.\n\n### Schema error handling\n\nBy default, `weaveSchemas` throws a `WeavingError` if an endpoint schema could not be fetched or the link and join features\nare configured incorrectly. You can change this behavior with the `errorHandling` property:\n\n```typescript\nweaveSchemas({\n    endpoints: [ /* ... */ ],\n    errorHandling: WeavingErrorHandlingMode.CONTINUE_AND_REPORT_IN_SCHEMA\n})\n```\n\nThere are four modes available:\n\n* `THROW` is the default behavior which throws all schema errors\n* `CONTINUE` ignores errors. If the endpoint schema cannot be created at all, it will be missing in the result config.\n    If a join or link feature is malconfigured, this one configuration will be skipped.\n    Use `weaveSchemasExt` to retrieve a list of errors.\n* `CONTINUE_AND_REPORT_IN_SCHEMA` behaves like `CONTINUE`, but errors are additionally displayed to the user via a\n    special _errors field on the root query type.\n* `CONTINUE_AND_ADD_PLACEHOLDERS` is like `CONTINUE_AND_REPORT_IN_SCHEMA`, but namespaced endpoints that completely fail\n    are also replaced by an object with a field _error. \n    \n### Runtime error handling\n\nSince version 0.11, graphql-weaver propagates runtime errors of endpoints transparently and in the most intuitive way.\n\n* If a user requested fields from two endpoints of which one failed and one succeeded, the result data of the successful\n    endpoint will be returned and the error of the failing one will be propagated.\n* If the endpoint reported errors for some fields but returned data for other fields, graphql-weaver will pass this\n    through exactly. The errors will be reported at the correct fields in the woven schema.\n* If the error reported by the endpoint included source location information, graphql-weaver will try to map this these\n    source location to the woven schema. If the location can not be mapped, the field's location in the woven schema\n    will be used, so if you get a location, you can be sure it is a location in your request to graphql-weaver.\n    \nFor the most part, this means everything should work as expected. However, the mapping is more complex as it might\nseem, so if you find an error mapping that looks wrong, please open an issue. \n\n## Contributing\n\nAfter cloning the repository, run\n\n```bash\nnpm install\nnpm start\n```\n\nTo run the test suite, run\n\n```bash\nnpm test\n```\n\nTo debug/run the application (or tests) in WebStorm, right-click on `graphql-weaver.js` (or `graphql-weaver-tests.js`, respectively) and choose *Debug*/*Run*.\n\n### Release workflow\n\n* For **normal development**, create a branch from master, commit and create a merge request to master. \n* To **fix a bug in a previous release**, find the *release-* branch for the corresponding version, increase the *patch* level in `package.json` and push the changes. Once the tests pass, manually trigger the *deploy* stage in Gitlab. You can also release a *-rc.1* version before the actual release for prior testing in dependent modules.\n* To prepare a **new feature release** (currently, this means a new minor version), create a `release-0.x` branch from master. Set the version to `0.x-rc.1`, push and manually trigger the *deploy* stage in Gitlab. Test the changes in dependent modules. Once everything is ok, change the version to `0.x` and deploy again. Finally, merge the release branch into *master*. Do not delete the release branch as it is used for hotfixes.\n\n## Architecture\n\ngraphql-weaver takes a set of GraphQL endpoints, transforms them through pipelines, merges them, transforms the merged schema again and exposes that as its *woven schema*.\n\n```\n           +------+  +------+  +------+\nEndpoints  |Schema|  |Schema|  |Schema|\n           +------+  +------+  +------+\n\n           +------+  +------+  +------+\n            X    X    X    X    X    X\nPipelines    X  X      X  X      X  X\n              XX        XX        XX\n\n               +                  +\nMerge           +----------------+\n\n                     +------+\n                      X    X\nPipeline               X  X\n                        XX\n\n                     +------+\nServer               |Schema|\n                     +------+\n\n```\n\nThe *merge* in the middle simply merges all the fields of the Query/Mutation types. All the other features, like type prefixing, field namespacing, even resolvers, is implemented by pipeline modules.\n\nYou'll find the list of modules in `src/pipeline/pipeline.ts`. For a description of each module, please refer to the TypeDoc comments.\n\n## Module structure\n\n* `graphql` - general utilities for working with GraphQL schemas and queries\n* `extended-schema` - an implementation of storing and exposing metadata on fields, the concept being [discussed on GitHub](https://github.com/facebook/graphql/issues/300)\n* `graphql-client` - GraphQL client library, with local and http implementations\n* `pipeline` - the core, being framework and modules for graphql-weaver's features\n* `config` - configuration parameter types for `weaveSchemas`\n* `utils` - utilities unrelated to GraphQL\n* `typings` - typings for thirdparty modules\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAEB-labs%2Fgraphql-weaver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAEB-labs%2Fgraphql-weaver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAEB-labs%2Fgraphql-weaver/lists"}