{"id":22796277,"url":"https://github.com/makerxstudio/graphql-core","last_synced_at":"2026-02-01T16:32:41.141Z","repository":{"id":177167804,"uuid":"530915271","full_name":"MakerXStudio/graphql-core","owner":"MakerXStudio","description":"A set of core GraphQL utilities that MakerX uses to build GraphQL APIs","archived":false,"fork":false,"pushed_at":"2025-09-23T00:58:36.000Z","size":829,"stargazers_count":1,"open_issues_count":8,"forks_count":0,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-12-14T10:56:45.353Z","etag":null,"topics":["api","graphql","library","npm","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/MakerXStudio.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":"2022-08-31T02:59:46.000Z","updated_at":"2025-09-23T00:58:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"3fe9e8cd-a6c3-4013-ae41-a3315dadaa53","html_url":"https://github.com/MakerXStudio/graphql-core","commit_stats":null,"previous_names":["makerxstudio/graphql-core"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/MakerXStudio/graphql-core","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MakerXStudio%2Fgraphql-core","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MakerXStudio%2Fgraphql-core/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MakerXStudio%2Fgraphql-core/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MakerXStudio%2Fgraphql-core/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MakerXStudio","download_url":"https://codeload.github.com/MakerXStudio/graphql-core/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MakerXStudio%2Fgraphql-core/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28982684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T16:29:42.054Z","status":"ssl_error","status_checked_at":"2026-02-01T16:29:41.428Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["api","graphql","library","npm","typescript"],"created_at":"2024-12-12T05:11:46.499Z","updated_at":"2026-02-01T16:32:41.126Z","avatar_url":"https://github.com/MakerXStudio.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# GraphQL Core\n\nA set of core GraphQL utilities that MakerX uses to build GraphQL APIs.\n\nThese utilities avoid dependencies on any particular GraphQL server or logging implementation, providing a standard set of behaviours to use across varying implementations.\n\nNote: See explanation on \\*Express peer dependency below.\n\n## Context\n\n`createContextFactory` returns a function that creates your GraphQL context using a standard (extensible) representation, including:\n\n- `logger`: a logger instance to use downstream of resolvers, usually logging some request metadata to assist correlating log entries (for example the X-Correlation-Id header value)\n- `requestInfo`: useful request info, for example to define per-request behaviour (multi-tenant apps), pass through correlation headers to downstream services etc\n- `user`: an object representing the user or system identity (see definition below, defaults to creating a `User` based on JWT claims)\n- anything else you wish to add to the context\n\n### Step 1 - Define your context + creation\n\ncontext.ts\n\n```ts\n// define the base context type, setting the logger type\ntype BaseContext = GraphQLContextBase\u003cLogger\u003e\n// define the extra stuff added to our app's context\ntype ExtraContext = {\n  services: Services\n  loaders: Loaders\n}\n// our app's context type, returned from the createContext function\nexport type GraphQLContext = BaseContext \u0026 ExtraContext\n\n// configure the createContext function\nexport const createContext = createContextFactory\u003cGraphQLContext\u003e({\n  // set the keys of the user claims (JWT payload) we want added to the request metadata passed to the requestLogger factory\n  claimsToLog: ['oid', 'aud', 'tid', 'azp', 'iss', 'scp', 'roles'],\n  // set the keys of the request info we want added to the request metadata passed to the requestLogger factory\n  requestInfoToLog: ['origin', 'requestId', 'correlationId'],\n  // use a winston child logger to add metadata to log output\n  requestLogger: (requestMetadata) =\u003e logger.child(requestMetadata),\n  // build the rest of the app context\n  augmentContext: (context): ExtraContext =\u003e {\n    const services = createServices(context)\n    const loaders = createLoaders(services)\n    return { services, loaders }\n  },\n})\n```\n\n### Step 2 - Map the context creation to implementation\n\nThese examples show how you might map implementation-specific context functions to your implementation-agnostic context creation function (from step 1).\nNote: examples assume that a JWT auth middleware has set `req.user` to the decoded token payload (claims). This is optional.\n\napp.ts\n\n```ts\n// wire up the createContext function, providing `ContextInput` for apollo-server-express implementation)\nconst server = new ApolloServer({\n  ...apolloServerConfig,\n  context: ({ req }) =\u003e createContext({ req, claims: req.user }),\n})\n```\n\nlambda.ts\n\n```ts\n// wire up the createContext function, providing `ContextInput` for apollo-server-lambda implementation\nconst server = new ApolloServer({\n  ...apolloServerConfig,\n  context: ({ event, context, express: { req } }) =\u003e\n    createContext({ req, claims: req.user, event: event as LambdaEvent, context: context as LambdaContext }),\n})\n```\n\nyoga.ts\n\n```ts\n// wire up the createContext function, providing `ContextInput` for graphql-yoga implementation\nconst graphqlServer = createServer({\n  ...yogaServerConfig,\n  context: ({ req }) =\u003e createContext({ req, claims: req.user }),\n})\n```\n\n## User\n\nBy default, if `claims` (decoded token `JwtPayload`) are available, the `GraphQLContext.user` property will be set by constructing a `User` instance.\n\nThe User class adds some handy getters over raw claims (decodedJWT payload) and provides access to the JWT (access token) for on-behalf-of downstream authentication flows. Note this may represent a user or service principal (system) identity.\n\n| Property | Description                                                                                                         |\n| -------- | ------------------------------------------------------------------------------------------------------------------- |\n| claims   | The decoded JWT payload, set via the RequestInput.user field.                                                       |\n| token    | The bearer token from the request authorization header.                                                             |\n| email    | The user's email via coalesced claim values: email, emails, preferred_username, unique_name, upn.                   |\n| name     | The user's name (via the name or given_name and family_name claims).                                                |\n| id       | The user's unique and immutable ID, useful for contextual differentiation e.g. session keys (the oid or sub claim). |\n| scopes   | The user's scopes, via the scp claim split into an array of scopes.                                                 |\n| roles    | The user's roles (via the roles claim).                                                                             |\n\n### Custom user\n\nIf you wish to customise your `GraphQLContext.user` object, provide a `createUser` function to override the default `User` creation.\n\n```ts\nconst createUser: CreateUser = async ({ claims }) =\u003e {\n  const roles = await loadRoles(claims.email)\n  return { name: claims.name, email: claims.email, roles }\n}\n\nconst graphqlServer = createServer({\n  ...config,\n  context: ({ req }) =\u003e createContext({ req, claims: req.user, createUser }),\n})\n```\n\n## Logging\n\n### logGraphQLOperation\n\nLogs a GraphQL operation in a consistent format with the option of including any additional data. Top level and result level log data with null or undefined values will be omitted for berevity.\n\nRefer to the `GraphQLLogOperationInfo` type for the definition of input.\n\nThis function can be used across implementations, e.g. in a [GraphQL Envelop plugin](https://www.envelop.dev/docs/plugins) or [ApolloServer plugin](https://github.com/MakerXStudio/graphql-apollo-server/blob/main/src/plugins/graphql-operation-logging-plugin.ts).\n\n## GraphQL subscriptions\n\nThis library includes a `subscriptions` module to provide simple setup using the [GraphQL WS](https://the-guild.dev/graphql/ws) package.\n\n1. Install subscriptions dependencies (optional peer dependencies of this package):\n   ```\n   npm i graphql-ws ws\n   ```\n1. Subscription context setup:\n\n   `createSubscriptionContextFactory` returns a function that creates an equivalent GraphQL context using input supplied to the [graphql-ws Server context callback](https://the-guild.dev/graphql/ws/docs/interfaces/server.ServerOptions#onconnect).\n\n   Example showing both normal context + subscription context creation:\n\n   ```ts\n   const augmentContext = (context: GraphQLContext) =\u003e {\n     const services = createServices(context)\n     const dataLoaders = createDataLoaders()\n     return { services, dataSource, dataLoaders }\n   }\n\n   // create a context using request based input\n   const createContext = createContextFactory\u003cGraphQLContext\u003e({\n     claimsToLog,\n     requestInfoToLog,\n     requestLogger: (requestMetadata) =\u003e logger.child(requestMetadata),\n     createUser: ({ claims, req }) =\u003e findUpdateOrCreateUser(claims, req.headers.authorization?.substring(7)),\n     augmentContext,\n   })\n\n   // create a context using graphql-ws Server#context callback input\n   const createSubscriptionContext = createSubscriptionContextFactory\u003cGraphQLContext\u003e({\n     claimsToLog\n     requestInfoToLog,\n     requestLogger: (requestMetadata) =\u003e logger.child(requestMetadata),\n     createUser: ({ claims, connectionParams }) =\u003e findUpdateOrCreateUser(claims, extractTokenFromConnectionParams(connectionParams)),\n     augmentContext,\n   })\n   ```\n\n1. Create a subscriptions server, using the ws-server cleanup function in your server lifecycle.\n\n   The `useSubscriptionsServer` function sets up:\n\n   - Auth token validation as part of establishing (or rejecting) the connection (behaviour defined by `verifyToken` and `requireAuth` args)\n   - GraphQL context creation\n   - Logging from the server `onConnect`, `onDisconnect`, `onOperation`, `onNext` and `onError` callbacks\n\n   Example for Apollo Server (`wsServerCleanup` called in the `drainServer` plugin callback):\n\n   ```ts\n    export const startApolloServer = async (app: Express, httpServer: http.Server) =\u003e {\n      logger.info('Building schema')\n      const schema = createSchema()\n\n      logger.info('Initialising subscriptions websocket server')\n      const wsServerCleanup = useSubscriptionsServer({\n        schema,\n        httpServer,\n        logger,\n        createSubscriptionContext,\n        jwtClaimsToLog: config.get('logging.userClaimsToLog'),\n        requireAuth: true,\n        verifyToken: (host, token) =\u003e verifyForHost(host, token, config.get('auth.bearer')),\n      })\n\n      logger.info('Starting apollo server')\n      const server = new ApolloServer\u003cGraphQLContext\u003e({\n        schema,\n        plugins: plugins(httpServer, wsServerCleanup),\n        introspection: true,\n        csrfPrevention: true,\n      })\n      await server.start()\n\n   ```\n\n1. For authorisation, clients can include a connection parameter named `authorization` or `Authorization` using the HTTP header format `Bearer \u003ctoken\u003e`. Note: [Apollo Sandbox](https://studio.apollographql.com/sandbox/explorer) will include an `Authorization` connection parameter when you specify an HTTP `Authorization` header via the UI.\n\n## Testing\n\nThe testing submodule exports utility functions for easily constructing ApolloClient instances for integration testing on NodeJS. The `errorPolicy` is set to `all` so that returned errors can be checked.\n\n### Setup\n\nIf you use this module, you need to install `@apollo/client`:\n\n```\nnpm install --save-dev @apollo/client\n```\n\n### Usage\n\n- `createTestClient` accepts a url and optional accessToken.\n- `createTestClientWithClientCredentials` accepts a url and client credentials config and will fetch and attach an access token to each request.\n\ntesting.ts\n\n```ts\nexport const testClient = createTestClientWithClientCredentials(process.env.INTEGRATION_TEST_URL, clientCredentialsConfig)\n\nexport const unauthenticatedClient = createTestClient(process.env.INTEGRATION_TEST_URL)\n```\n\ntweets.spec.ts\n\n```ts\ndescribe('tweets query', () =\u003e {\n  const tweetsQuery = gql`\n    query Tweets($input: TweetsWhere) {\n      tweets(input: $input) {\n        data {\n          text\n          createdAt\n        }\n      }\n    }\n  `\n\n  it('returns tweets with sensible default limit', async () =\u003e {\n    const {\n      data: { tweets },\n      errors,\n    } = await testClient.query\u003cTweetsQuery\u003e({\n      query: tweetsQuery,\n    })\n\n    expect(errors).toBeUndefined()\n    expect(tweets).toBeDefined()\n    expect(tweets?.data?.length).toBe(10)\n  })\n\n  it('guards against high limit', async () =\u003e {\n    const tooHighLimit = 101\n    await expect(async () =\u003e {\n      await testClient.query\u003cTweetsQuery, TweetsQueryVariables\u003e({\n        query: tweetsQuery,\n        variables: {\n          input: {\n            maxResults: tooHighLimit,\n          },\n        },\n      })\n    }).rejects.toThrowErrorMatchingInlineSnapshot(`\"Response not successful: Received status code 400\"`)\n  })\n\n  it('requires authorisation', async () =\u003e {\n    const { data, errors } = await unauthenticatedClient.query\u003cTweetsQuery, TweetsQueryVariables\u003e({\n      query: tweetsQuery,\n    })\n\n    expect(data.tweets).toBeNull()\n    expect(errors?.length).toBe(1)\n    expect(errors?.[0].message).toMatchInlineSnapshot(`\"User is not authorized to access Query.tweets\"`)\n  })\n})\n```\n\n## Utils\n\n- `isIntrospectionQuery`: indicates whether the query is an introspection query, based on the operation name or query content.\n\n## \\*Express peer dependency\n\nApolloServer v3 standardises on the Express request representation.\n\nGraphQL Yoga uses the NodeJS http request representation, plus adds the Express version when using an Express server.\n\nThis library therefore takes a peer dependency on Express as the standard (common) request representation.\n\nThe ApolloServer [v4 roadmap](https://github.com/apollographql/apollo-server/blob/main/ROADMAP.md#replace-9-core-maintained-bindings-with-a-stable-http-abstraction) will standardise on the NodeJS http request representation.\n\nThis library may swap to the NodeJS http representation in a future version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmakerxstudio%2Fgraphql-core","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmakerxstudio%2Fgraphql-core","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmakerxstudio%2Fgraphql-core/lists"}