Sending Telemetry from GraphQL under NodeJS without Spamming

I am running a GraphQL server hosted with NodeJS. Under the hood, it is an Express server with Apollo Middleware. I am collecting different information along with errors. The way it works is that I have an Elastic Search server fronted by a simple REST API facade. The endpoint on the REST accepts a single telemetry payload or a collection of them. The latter is recommended. When I use the REST facade for the web, I am collecting all call to the telemetry and batch the request every 5 seconds or when the browser send a beacon (when leaving the page). It reduces the load on the server by limiting the number of HTTP requests. For example, if there is 24 different telemetry within a few seconds, it performs a single HTTP request with 24 entries.

Telemetry Information flow

Under NodeJS, I could do something similar with a timer, but while reading how the DataLoader library handles batching I thought I could code a similar pattern. Instead of leveraging on time, I could batch every telemetry on a single NodeJS event loop. In the NodeJS world, this is called a “tick”. There are two ways to accomplish the batching, and I leaned on setImmediate.

NodeJS Event Loop

The idea is that NodeJS runs in an infinite loop. It is possible to mention to the system to prepare the execution later, on the next loop with setImmediate which execute when the “poll phase” is completed. setImmediate is different from the setTimeout because it does not rely on a time threshold. Often, libraries use process.nextTick. it processes the task after the event loop. I avoided using process.nextTime because in some situation it can cause an infinite loop with recursivity. setImmediate is enough to delay the execution. In fact, after using it for more than two weeks, every telemetry collected within a single GraphQL request are batched together which is perfect in my case.

The code in the NodeJS server is short. There is a class that consists of a boolean field that indicates if we have batched information. By default, the value is false until we invoke the first time the function to send the telemetry. When the flag is true, we keep calling the code that will add into an array all the telemetry but we do not call the function performing the HTTP request to the API; we wait that the setImmediate function callback is executed. When this one is executed and returned with a successful HTTP code, we copy the content of the array, flush the data from the list of telemetry, send the information and turn back the flag to false. Ready for the next round of batching. While the code is sending the telemetries, other telemetries can be collected. The data is added to the array to be sent. In case of failure, the data is added back to the next batch.

public send(data: TelemetryPayload): void {
    const dateToSend = { ...data };
    this.listDataToSend.push(dateToSend);
    if (!this.isTransmittingQueuedPayload) {
        this.isTransmittingQueuedPayload = true;
        setImmediate(() => {
            this.send(); // Perform HTTP requests with all the this.listDataToSend
        });
    }
}

Overall, the code is pretty clean and under one hundred lines of code. It reduces drastically the number of HTTP requests while being easy to read once we get the setImmediate detail clarified.

My Other GraphQL Blog Posts

How to Collect HTTP Request Telemetry with GraphQL and RESTDataSource?

I have been working in a GraphQL solution at Netflix for few months now. I am always keen to have insight about what is going on. While I created from the ground-up the GraphQL server with NodeJS I had in mind to find a way to give me, the software engineer, some insights by providing some direct feedback in the bash console running the server but also to send the data to an ElasticSearch cluster. Before rendering or sending the telemetry, we need to collect it. In this article, I’ll write how I proceeded to get information on individual HTTP calls that the GraphQL query invoke to retrieve the information while resolving a graph of objects.

The solution is oriented toward the library RESTDataSource but it is exactly the same principle with Axios or other HTTP request library. The idea is to subscribe to a global event that is invoked when a request starts and when a response comes back. By having a hook at the beginning and the end, it is possible to collect the time elapsed without having to code something on every request.

RESTDataSource Override

In the case of RESTDataSource, it is a matter of overriding the willSendRequest function. It takes a request parameter. We will use the request to add in the HTTP’s header a unique identifier that will give the response function a reference to the originator. The second function to override is didReceiveResponse. This one has two parameters: the request and the response.

The willSendRequest function performs three actions. The first one is to generate a GUID that will serve as a unique identifier. It is added into the HTTP header. Then, the second action is to add in the GraphQL’s context a collection of HTTP requests. I created a type that will keep track of the time, the total bytes received, the URL, query string, the starting time and also the unique request identifier (GUID). The unique identifier is needed for the second function.

export interface HttpStatsEndpoints {
    requestUuid: string;
    url: string;
    urlSearchParams: URLSearchParams;
    elapsedTimeMs: number;
    totalBytes: number;
}

The didReceiveResponse function gets a response, but also the request object. Because we have the request, we can peek at the GUID and extract from the GraphQL context the information and subtract the actual time from the time saved when the request started. The number of bytes and the elapsed time is saved in the context until read by the GraphQL Extension.

public didReceiveResponse(response: Response, request: Request): Promise<any> {
    return super.didReceiveResponse(response, request).then(result => {
        const requestId = request.headers.get(HEADER_REQUEST_UUID);
        const startTime = request.headers.get(HEADER_START_TIME);
        const httpRequest = this.context.stats.httpRequests.find(d => d.requestUuid === requestId);
        if (httpRequest !== undefined &amp;&amp; startTime !== null) {
            const totalNanoSecondsElapsed = process.hrtime();
            const totalMilliSecondsElaspsed = this.convertNodeHrTimeToMs(totalNanoSecondsElapsed);
            httpRequest.elapsedTimeMs = totalMilliSecondsElaspsed - Number(startTime);
            httpRequest.totalBytes = JSON.stringify(result).length;
        }
        return result;
    });
}

GraphQL Extension

At this point, when all requests are completed and the GraphQL is ready to send a response back, a custom extension can come into play. I covered the detail of a custom GraphQL Extension in a previous post concerning telemetry and how to display it on the console. The idea is the same, this time we can read the GraphQL context and while looping through the telemetry display the bytes and time taken for each request.

Here are some of my GraphQL post of this series

Validating Consumer of GraphQL with Jenkins

I will demonstrate a way to configure a continuous environment that gives insight on if a change on the GraphQL server can impact negatively a consumer. The solution assumes that both code bases are under the same organization, not under the same repository and that Jenkins is used.

I like to start with an image of what I am describing. So here is the solution.

The diagram shows on the top-right the GraphQL engineer who push a change in the repository. The normal continuous integration (CI) kicks a build when it receives a new code. So far, nothing changes from a typical workflow. However, the third step is where it changes. The idea is to create a new Jenkins job that is independent of the GraphQL server and independent of the consumer application. The independence of this job keep both existing builds untouched and the whole ecosystem tidy. The Jenkins jobs wait for the GraphQL job to complete. It is possible to configure the option under Jenkins Build Triggers.

Jenkins’ Trigger

When the GraphQL changes, the Jenkins job fetch the consumer code and fetch the GraphQL code. Do not forget we assumed that we had access to both source code. However, if the GraphQL is not internal, you can always download the GraphQL unified schema and accomplish the same end results. In fact, the next step is to run the GraphQL server script that builds the latest schema (stitches the schema). Then, it runs a tool that validates the consumer code. The tool looks for gql tags inside TSX and TS file (TypeScript) and analyzes all queries against the schema. If a breaking change occurs, the build fails and an email is sent to the engineers to act before it reaches deployment.

The Jenkins’ execute Shell script install NPM because it must run the tool which is from an NPM library. It gets the server because we do not have a graph unified (stitched) schema. Then, it runs the graphql-inspector tool.

npm install
git clone ssh://git@youServer/yourGraphQLServer.git graphql_repo
cd graphql_repo
npm install
npm run generateunifiedschema
cd ..
./node_modules/.bin/graphql-inspector validate ./src/**/*.graphql ./graphql/unifiedSchema.graphql

A special mention, the graphql-inspector does not work well (in my experience) with analyzing .ts and.tsx files. However, it worked perfectly when I moved the GraphQL query into .graphql files.

Most of the time, breaking change should not occur. GraphQL allows additional change and to deprecate fields slowly instead of working with several versions. Nonetheless, having additional awareness is something beneficial and by having the tooling configured to automatically verify potential breakdown we reduce the stress of introducing consequences in production.

My GraphQL Articles

A Single GraphQL’s Schema for Internal and External Users

Imagine the scenario that you have a GraphQL schema that is consumed only by engineers of your organization. However, some engineers work on external facing applications while some other works on internal applications. The difference is subtle, but some information must remain confidential. While it might be more restrictive than most GraphQL that are fully public, there is the benefit that the schema is private and we can leverage that detail.

Internal vs External

In the scenario described, the GraphQL server is deployed into an Amazon VPC — an Amazon Virtual Private Cloud. The environment is secured and the authorization is granted to only a limited to specific security groups.

Internal vs External Environment

The challenge is not to limit the schema definition into an internal and external, but to have the external users not able to craft a request to access unexpected information. Because the GraphQL server is behind the VPC, it is not directly accessible by the user. The external application can communicate to the GraphQL server and every front-ends request to fetch data are proxied by the web server application. The proxy is important. It means that the browser of external users is not directly connected to the GraphQL server. Instead, the browser performs an AJAX call to the respective web server and this one, on the behalf of the user will conduct the GraphQL query. The proxy is conducted with the ProxyPass instruction of Apache.

Internal applications do not have many constraints but keeping the same pattern of proxying is a good habit. It simplifies CORS because the browser performs HTTP requests to the same server, and only underneath it communicate to other servers. It also simplifies the security by having a central point of communication (the web server) to communicate with backend secured services.

GraphQL Single Schema

An obvious solution is to have two schemas: one internal and one external. The solution is the only choice if you need to expose the GraphQL schema externally without exposing the definition of internal fields and entities. However, because I had the constraint of not exposing GraphQL outside, I could simplify the maintainability by having a single schema. The problem with many schemas is that it does not scale very well. First, when adding a field that is good externally, you need to add it twice. Then, when it is time to modify or remove, you need to keep in synchronization the different schemas. Any little boilerplate cause the engineering experience to be a burden but also is prone to errors.

In an ideal world, a single schema exists and we flag the field or entity to be only available internally. That world can exist with the power of GraphQL directive.

The GraphQL Directive Idea

GraphQL allows enhancing the graph’s schema with annotation. Let’s start with the end result which should talk more than any explanation.

type MyTypeA {    
    fieldA: EntityTypeA
    fieldB: EntityTypeA @internalData
}

The idea is to add “@internalData” to every field that must be only visible to internal usage. The annotation marks field but also can mark a whole type.

type MyTypeA @internalData {    
    fieldA: EntityTypeA
    fieldB: EntityTypeA
}

The idea is to have a single schema that had an indication that the field is added into a request will have some consequence. Because it is a single graph, the field appears in the interactive Graphql Playground and is a valid field to request; even externally. However, when invoked, GraphQL at runtime will be able to read the directive and perform a logic. In our case, the logic will be to verify the source of the request and figure out if the request is internal or not. In the case of an internal request, the data will be part of the response. If the source is external, an exception will occur and the field will be undefined.

How to build a GraphQL Directive?

The directive is two parts: one is in the GraphQL language (design time) and one is the logic to perform at runtime.

In any .graphql file, you need to specify the directive to let know GraphQL about its existence. I created a file with the name of the directive and added this single line. The directive indicates that it can be applied to a type (OBJECT) or to a field (FIELD_DEFINITION). The directive could also have arguments. For example, we could have a more advanced need to specify which role can access which field.

directive @internalData on OBJECT | FIELD_DEFINITION

The second part if to handle the directive. When merging all the resolvers and type definitions you can also specify the collection of directives. What you need to pass is a key-value pair with the directive name and the class of the directive (not the object). It means that you do not instantiate (new) the class, but only give a reference to the class.

const schemas = makeExecutableSchema({
    typeDefs: allSchemas,
    resolvers: allResolvers,
    schemaDirectives: {
        internalData: InternalDataDirective,
        }
});

The class must inherit SchemaDirectiveVisitor. Then, because we have specified that it can be applied to a field and a type, we need to override two functions: visitFieldDefinition and visitObject.

export class InternalDataDirective extends SchemaDirectiveVisitor {
    private static readonly INTERNAL_APP = ["app1", "app2", "app3"];

    public visitObject(object: GraphQLObjectType): GraphQLObjectType | void | null {
        this.ensureFieldsWrapped(object);
    }

    public visitFieldDefinition(
        field: GraphQLField<any, any>,
        details: {
            objectType: GraphQLObjectType | GraphQLInterfaceType;
        }
    ): GraphQLField<any, any> | void | null {
        this.checkField(field);
    }

    private ensureFieldsWrapped(objectType: GraphQLObjectType | GraphQLInterfaceType) {
        if ((objectType as any).__scopeWrapped) {
            return;
        } else {
            (objectType as any).__scopeWrapped = true;
        }
        const fields = objectType.getFields();

        Object.keys(fields).forEach(fieldName => {
            const field = fields[fieldName];
            this.checkField(field);
        });
    }

    private checkField(field: GraphQLField<any, any>): void {
        const { resolve = defaultFieldResolver } = field;
        field.description = `&#x1f510; Internal Field. Only available for: ${InternalDataDirective.INTERNAL_APP.join(", ")}.`;
        field.resolve = async function(
            source: any,
            args: any,
            context: GraphQLCustomResolversContext,
            graphQLResolveInfo: GraphQLResolveInfo
        ) {
            if (
                context.req.appOrigin === undefined ||
                !InternalDataDirective.INTERNAL_APP.includes(context.req.appOrigin)
            ) {
                throw new Error(
                    `The field [${field.name}] has an internal scope and does not allow access for the application [${
                        context.req.appOrigin
                    }]`
                );
            }

            const result = await resolve.apply(this, [source, args, context, graphQLResolveInfo]);
            return result;
        };
    }
}

The directive converges the two entry points (field and object) into a single function. The two functions are called once when the class is instantiated by the GraphQL code at the startup of the server. It means that you cannot have custom logic in the visit functions. The dynamic aspect appends because we wrap the resolve of the field. It means that the actual resolution is executed but the code specified in “checkField” is also performed at runtime. In the code excerpt, we see that it checks for a list of accepted internal applications. If the field has the directive, it goes into the directive’s resolver and checks if the origin if from the list of accepted internal application. If not, it throws an error.

A little detail, it is possible to inject a description from the directive that is set on the initialization of this one. In my case, I specify that the field is private and mention which application can access it. If a software engineer needs an application to be on the list, it requires a code change. This is not something that happens often and because a code change is required it involves a pull request where many people will have a look.

Example of how it looks from the GraphQL Interactive Playground. The engineer who build the query knows that it is an internal field as well as under which application the response will return a value

Conclusion

The more I work with different organizations, code bases, and technologies, the more I lean toward simplicity. There is so many changes, so many ways to get very deep into subjects and so little time. Getting into complex solution often cause the maintainability a nightmare or make some people very dependent. The solution of a directive in GraphQL took less than 150 lines of code and can scale toward the entire graph of objects without having a dependency on a system to manage many schemas. The security of the information is preserved, the engineers that consume the graph are aware when building the query (description) and while executing the query (error), and the engineers building the graph can add the tag to the fields or types which take a few seconds without having to worry about the detail of the implementation.

My Other GrapHL Blog Posts

The authorization and authentication in GraphQL

Implementing a service that covers many systems might be frightful in term of data exposition. While wandering the Internet for resources about GraphQL and security, we often see cases where the security is not paramount — it is easier to ignore security. The reality is that most corporation that desire to bring GraphQL will also need to secure the information. This article sheds light on how I approached the security in my implementation of GraphQL at Netflix for Open Connect.

Prelude

In an ideal world, we have the GraphQL server getting the authorization from the corporate security infrastructure and the GraphQL delegate downstream to the actual data source the responsibility of returning an error. For example, if the data source is a REST API, the token of the user is used for the call, hence can return an HTTP code 401 and GraphQL will handle the error. However, in maybe the GraphQL exposes some internal services that were secured only by the premise that it was under a VPS (virtual private server). Meaning that not validation is actually performed. In that case, some custom code is required by the executioner of the service: GraphQL. Another case could be that the security is by a specific group of entity (organization, user, etc) meaning that you do not want user A to access user B information. Again, a strong security model would perform the validation at the source (Rest, SQL views, etc) but in the real world, it is not always the case. To mitigate the possibility of security issues among the sea of services that was cover in my scenario, the security was placed in GraphQL. Meanwhile, further modification in the data sources could be planned and put in place without compromising the delivery of the GraphQL server.

Exploring the possibilities

One strength of GraphQL is the flexibility. The flexibility nature remains true for security and it opens many doors to where to secure the service. As mentioned earlier, the NodeJS server that host Apollo is behind Apache. The reason is that at Netflix, we have many tools integrated within Apache to secure the user like single-sign-on, OpenID Connect and OAuth 2.0. The Apache module is great for authentication but not for authorizing. It does check if the user is one that can access the GraphQL but does not discriminate on which information the user can consult.

Flow of the request from user to services that contain the information

Apache gives information about the user and provides the additional HTTP headers to NodeJS. The first stop is a custom NodeJS Express middleware. The middleware is a piece of code executed in each request. The middleware check if the user is a Netflix employee with a specific grant right. If that is the case, it marks a particular field in an enhanced request object to signal the user as “authorized for full access.” The idea is to avoid every future validation that can be performance costly. This shortcut works well because the Apache module can be trusted with its information. It might not work well in your organization, thus do your homework. The next stop is at the GraphQL context. In my case, I have no validation to do at that level because I did the check in the NodeJS Express middleware. However, if you are not using NodeJS, it would be the place to do HTTP request checks. However, I instantiate a secure object at that level that contains functions that check particular lists of objects. The lists of objects are specific ids of secured entities that the user has access. Then, the context performs a query on specific backend services to fetch what objects ids the users can access. The list goes in the request. The idea is to have before the user reaches the resolver a well-defined list of authorized entities and ids.

It is possible to perform at the resolver checks, but the problem is that if you are not querying for the field that contains the ids that the user can access that you will not have the value available. For example, if a user can only access the data of the organization that he/she belongs and that the user requests for the organization by id for its name then you could block. But, if the user request for a sub-entity, for example a contact and then in the query’s tree the name of the organization, without the organization id, then the resolver cannot check if the organization’s data belong or not to the authorized ids.

Finally, the place I found the best to handle authorization is at the data loaders level where every HTTP requests to service are performed. Upon reception of a query, the data is examined to check if the payload contains information from the entities we want secure. If the response contains an entity that does not belong to the user, an exception is thrown and GraphQL bubble up the exception to the resolver who initiated the request. GraphQL handles the exception properly and your message is given to the user.

Conclusion

The solution is not ideal because it requires the user to have an additional call, per service, to have a list of entities and ids. I opted to have the GraphQL cache all the entities-ids per user in a server cache (not a request cache) for few minutes. The solution has a flaw that the request is still performed to the service. The reason is the lack of transparency from an entity B on entity A before getting the data. Nonetheless, it secures because the response does not go beyond NodeJS, it is not cached either. These loops are required because of weakness at the leaf of the architecture: the service that has access to the data. As a reminder, even if you are building an internal service that is secured by a network, it is always better to not rely on that infrastructure and to perform database checks. The future is never known, infrastructure change, security change, a potential consumer of the information evolve, and we never know when something will be exposed. For future resiliency and for an optimal defense: always authorize at the source.

My Other GraphQL Articles

How to consume GraphQL in TypeScript and React

In this article I’ll cover two different approach concerning rendering information coming from a GraphQL server in React while being strongly typed.

Approach 1: The normal fetch

The first approach is the one I prefer because it is not intrusive. It is very similar of to do a normal REST Ajax call. It can be swapped from an existing fetch command that is using not GraphQL without having to change much. The approach is frontend framework agnostic, meaning that you can use it outside React. Furthermore, this approach works well in a middleware if you are using Redux, but can be directly used in you custom “service” layer or when a component is mounted — you choose.

The first step is to get some NPM packages. In this approach, I am using two packages related to GraphQL: the Apollo-boost and the graphql-tag.

import ApolloClient, { ApolloQueryResult } from "apollo-boost";
import { Proto } from "autogenerated/octoolstypes";
import gql from "graphql-tag";
import React from "react";

You then need to create an ApolloClient which is the library that will perform the query. See the ApolloClient as an HTTP request library, similar to Axios. It lets you specify the URL to perform the POST HTTP request, but also custom headers. For example, I am specifying a bearer token to have the request authenticated.

this.client = new ApolloClient({
    uri: "/api/graphql",
    headers: {
        authorization: `Bearer ${this.props.token}`
    }
});

The next step is to have the query executed. This is where you can swap your existing fetching code with the ApolloClient.

(async () => {
    try {
        const result: ApolloQueryResult<Proto.Query> = await this.client.query<Proto.Query>({
            query: gql`
                query Proto {
                    org(orgId:orgIdParam) {
                        name
                    }
                }
            `
        });
        if (result.data.org &amp;&amp; result.data.org.name) {
             // Do something with result.data.org.name
        }
    } catch (error) {
        console.error(error.networkError.bodyText);
    }
})();

That’s it. Very simple, easy to understand because it is similar to most fetching pattern. The ApolloQueryResult takes a generic type which is the format of the gql. It means that when building for the first time the query that you might not want to explicitly specify the type because it does not exist. As discussed in a previous article, there is tool that will scan the gql query and generate the type for you.

Approach 2: Strongly bound to React

The second approach is my less favorite. I favor the first one because I like separating my view from my logic. It is easier to unit test, and the separation of concern make the code easier to understand and refactor. Nonetheless, the option is available and worth exploring.

The second approach use an Apollo component that will wrap the application. It is similar to the React-Redux provider. It takes a single property which is the client, that is the same as provided in the first approach.

public render(): JSX.Element {
    return (
        <ApolloProvider client={this.client}>
            <ConnectedRouter history={history}>
                <AppRouted />
            </ConnectedRouter>
        </ApolloProvider>
    );
}

The difference is that because we have the client connected in the ApolloProvider that any React component underneath has access to the possibility of querying by creating a Query component.

<Query<Proto2.Query>
    query={gql`
        query Proto2 {
            org(orgId: orgIdParam {
                name
            }
        }
    `}
>
    {queryResult => {
        if (queryResult.loading) {
            return "Loading";
        }
        if (queryResult.error) {
            return "Error";
        }
        if (queryResult.data === undefined || queryResult.data.org === null) {
            return <p>None</p>;
        }

        return (
            <div>
                <h5>Name:</h5>
                <p>{queryResult.data.org.name}</p>
            </div>
        );
    }}
</Query>

Once again, the Query is strongly typed by using the gql defined in the query parameter of the query component. Until the type is generated, which mean that the gql query has been picked up by the script that build the TypeScript file, the query cannot be explicitly typed. However, once it done, any future change will catch on. This approach has the query executed every time the container of the Query component is rendered which mean that future optimization, like having a caching policy specified in the ApolloClient might be a good thing.

Wrap-up

There are more than two ways to bind GraphQL data into your React application while having the benefice of being strongly Typed. The first approach is more agnostic, the second embrace React with a component to handle a query. As long as you are comfortable, either does the job. I recommend using the former approach because it is flexible without having your fetching code dependent on React but otherwise, the end result of a strongly typed code is the same.

GraphQL Posts

GraphQL Extension to Collect HTTP and Resolvers Telemetry

The more my graph was increasing, the more resolvers I built. The more resolvers I had, the more data loaders I created, the more HTTP endpoints I had to call and so on. The complexity of the code was rising as well as the possibility to have a catastrophic amount of HTTP requests to be performed by GraphQL. The amount of log by using the console was not only polluting the source code by having a line in each resolver and HTTP method but also couldn’t make it easy to collect the data for future analysis or to emergency cancellation of a GraphQL query if the amount of HTTP rise beyond a reasonable threshold.

The idea I had was to see if there is a way to hook into GraphQL to collect the data at a higher level instead of being at each function. I am using the Apollo library and the answer is yes! There is an experimental feature named “extension”. The documentation is dry and does not explain much but with some breakpoint and Visual Studio Code, I was able to build a simple extension in a few hours. Here is the result.

A GraphQL Request with its HTTP requests

The first requirement is to create the extension. It must inherit the GraphQLExtension class which is generic. The generic type needs to be your context type. Having access to the context is primordiale because it gives access to your user information. I assume that once authenticated the user is set in the context. Also, it gives you the possibility to inject statistic into the context throughout the exploration of the graph. In my case, I am using the context to collect information at my data service layer where all the HTTP requests are performed.

export class DevConsole<TContext extends ApolloConfigurationContext> implements GraphQLExtension<TContext>

There is two importants function that you can override. The first one is “requestDidStart” which is invoked when a GraphQL request is made. It means that when a context is created and it that case a “request” is the GraphQL request and not all HTTP request performed by GraphQL during that GraphQL’s request. The function return a function. The return function is invoked when the request is completed. It is the place to output into the console all your statistic or to send the information to your telemetry system.

public requestDidStart(o: {
    request: Pick<Request, "url" | "method" | "headers">;
    queryString?: string;
    parsedQuery?: DocumentNode;
    operationName?: string;
    variables?: { [key: string]: any };
    persistedQueryHit?: boolean;
    persistedQueryRegister?: boolean;
    context: TContext;
    requestContext: GraphQLRequestContext<TContext>;
}) {
    console.log("New GraphQL requested started");
    return () => {
        const yourContextData = o.context
        console.log("Your data can be sent here");
    };
}

The second function is named “willResolveField” and is executed at every node of your graph. You can know the parent field and your field.

willResolveField(source: any, args: { [argName: string]: any }, context: TContext, info: GraphQLResolveInfo) {
    // Info has the field name and parentType.name
    return (error: Error | null, result: any) => {
        // ...
    };
}

The next step is to plug the extension into Apollo GraphQL. The property “extensions” of ApolloServerConfig takes an array of extension.

const serverConfig: ApolloServerConfig = {
    // ...,
    extensions: [() => new DevConsole()]
};

I am using the RESTDataSource of Apollo as well to perform all HTTP request. The abstract class has the GraphQL’s context. If you override the function willSendRequest, you can then have a global hook into all HTTP request. In that function, I am populating a property of my context that I named “stats” where I am storing each URL and parameters.

public willSendRequest(request: RequestOptions) {
    const stats = this.context.stats;
    // push data in the stats object
}

The documentation could be better in term of defining all the possibility. Nevertheless, with some breakpoints and a little bit of time I was quite satisfied to see what can be done. You can do much more than what proposed, for instance, you can calculate the payload size and get more insight into what is not efficient. For example, I realized in the process of creating another exception that was throwing an exception when a maximum threshold of HTTP request was reached was still letting GraphQL continue its traversal causing the exception to occurs on every future node. The solution was to clean up the response of redundant error using the overload function willSendResponse.

Bonus: Getting the size of the payload

There is a lot of possibility to get a clear view of what is happening behind the scene for maintainers and developers of the GraphQL service. The detail does not matter directly for the consumer who care about getting the data fast and accurately in the format requested and not about the complexity. However, the complexity if not well exposed to the team who own the service can be costly in term of resources. It is very easy to fall into the trap of assuming that everything goes as expected in a world where every GraphQL request is unique and can surface a scenario not well covered.

Previous GraphQL Posts

How to automatically generate TypeScript for consumer of your GraphQL

One strength of GraphQL is that it has types. In an ideal world, the consumer of the GraphQL would receive the payload strongly typed. The reason that it is not the case is natural. The data that is moving from the server to the client who invoked the query receives a JSON format that does not have any notion of type. However, the client who call the GraphQL know exactly which fields he wants to consume and GraphQL maps each queried fields to a type.

Objectives

The idea of this article is to let the user who call the GraphQL server to get a tailored object that is only for the field requested but also strongly typed for the field needed. It means that if the user request field A, B, and C of an object that has A, B,C, D, E, that the type that will be automatically generated will be only with A, B, C. While it might look that the generated type is not representing the real object, it reflects exactly the data that can be used. There is no need to have a generated object that you could not use.

Constraints

Many articles discuss about introspection and how types can be generated by having the consumer connect directly to GraphQL server to fetch the schema. I had the constraint that I could not do it for security reason, hence I had to expose the schema by the server into another way and have the consumer uses the generated merged schemas.

Server Steps

On the server side, one step is required: exposing the schemas into a unified schema (file) that can be shared. By generating the type every time the server is compiled, the schema remains in synchronization. Hence, we do not lose the freshness of being connected directly to GraphQL.

I created a script in the package.json that execute a TypeScript script.

"generateunifiedschema": "ts-node src/scripts/mergeAllGraphqlSchemas.ts",

The code in the TypeScript file is simple — really simple. Here is the whole code:

import fs from "fs";
import { fileLoader, mergeTypes } from "merge-graphql-schemas";
import path from "path";
const documentTypeDefs = fileLoader("src/**/*.graphql");
const mergedSchema = mergeTypes(documentTypeDefs);
const typesPath = "output_types";
const schemaPath = path.join(typesPath, "allSchema.graphql");
fs.writeFileSync(schemaPath, mergedSchema);

It uses the library merge-graphql-schemas and search for all individual schema that is spread inside the project. The result goes outside in a folder. That’s it. In my case, that generated file is available internal at Netflix but not exposed outside. It means that every developers that consume the GraphQL service can download the schema, create query in their TypeScript code and use the unified schema to generate their code.

Client Steps

Each consumer will have to do few steps. First, they need to download the unified schemas. Second, they need to write their GraphQL query and use gql. The gql allows to have a library that will scan the TypeScript file for gql (GraphQL Query) and will generate the appropriate TypeScript definition for the response data.

Download Unified Schema

The download step is a matter of using curl. I create a simple bash file:

curl -X GET \
  http://retracted/allSchema.graphql \
  -H 'accept: text/plain, */*' \
  --silent \
  --output allSchema.graphql

Query the GraphQL

The next step is to write the client code to query the data. There is many ways to do it, I’ll demonstrate one but will not go in detail in this article.

async () => {
            try {
                const result: ApolloQueryResult<Proto.Query> = await this.client.query<Proto.Query>({
                    query: gql`
                        query Proto {
                            org(orgId: 0) {
                                name
                            }
                        }
                    `
                });
                if (result.data.org &amp;&amp; result.data.org.name) {
                    this.setState({
                        longName: result.data.org.name
                    });
                }
            } catch (error) {
                console.error(error.networkError.bodyText);
            }
        }

At that point, your may wonder what is “Proto.Query” type and how come the result is strongly typed. The “Proto.Query” is the type that will be generated from the query specified in the client.query. The gql contains a query name which is translated into a TypeScript’s namespace. It is important to name each query differently because of potential collisions. In my case, the org entity has only a single field, but later I might request more which will generate another org. A good way is to not call it “Proto” but something more relevant. For example, “Financial” or “Inventory” depending on the usage of the entity. Still, how do we generate the object.

Generate the TypeScript type

The generation is done by using a library named graphql-code-generator which scan the TypeScript file for gql and uses the unified schema (or could work by directly connecting to the GraphQL) and output in a specified folder the type. It means that the first time you are writing the gql, you should not strongly type the ApolloQueryResult, then will have access to the type. Then, every change in the type will mend the existing type which is a great experience. For example, removing in the gql a field that is being used in TypeScript will make the code not compile. The “graphql-code-generator” library has a bin that you can use in your package.json to read the codegen.yml file with your custom configuration. I added two scripts. One that analyzes the TypeScript files and generates and another one that I use while developing who constantly check in the background to generate types.

"generatetypes": "gql-gen --config graphql/codegen.yml",
"generatetypes:watch": "gql-gen --config graphql/codegen.yml --watch"

The codegen.yml has the schema and document configuration. At first, I was confusing about these two terms. The documentation is dry on the differentiation. The schema configuration is about where to get the GraphQL schema, this is the downloaded unified GraphQL schema from the server. The document is which TypeScript file to analyze (could be JavaScript file as well).

schema: "./graphql/**/*.graphql"
documents: "./src/**/*.+(tsx|ts)"
overwrite: true
generates:
  ./src/autogenerated/octoolstypes.ts:
    config: {}
    plugins:
      - "typescript-common"
      - "typescript-client"
require: ['ts-node/register']

It uses few plugins, hence you need to get the NPM packages accordingly.

"graphql-code-generator": "0.16.0",
"graphql-codegen-add": "0.16.0",
"graphql-codegen-typescript-client": "0.16.0",
"graphql-codegen-typescript-common": "0.16.0",
"graphql-codegen-typescript-server": "0.16.0"

The graphqlcodegen will check and generate the code. Unfortunately, it is slow. Even on a small code base, the process can take 10-15 seconds. Nonetheless, you have your TypeScript type generated for you and it will adapt with the backend (GraphQL server) changes.

Conclusion

Having the type generating automatically from the backend is fascinating and is efficient. If your business domain is rich in quantity of entities (interfaces) and if many of them have several fields and complex interconnection than having the type generated instead of typing it manually is a relief.

Related GraphQL Articles

Configuring Apollo Playground and API on two different URL

By default, Apollo GraphQL middleware install the server in Express under the same URL. If you use the URL in the browser (GET), the playground loads. If you use the URL as a POST the data is returned. The website and the API are both using the same URL. It is problematic if you want the playground to use a particular authentication system (e.g., web-based) and the API to use an other (e.g., token approach). In this article, I will present a simple way to have the web playground and the API over two different URLs.

The idea is to start two Apollo middleware and route them with different options. The playground can be even simpler that what I am presenting. I am adding a few playground settings. Because I am using TypeScript, all the fields are marked to be required, hence I am explicitly specifying the value for each. The “request.credentials” is required with the authentication mechanism under which I am developer the GraphQL.

app.use(playgroundEndpoint, authorizeUserMiddleware);
const playgroundServer = new ApolloServer({
 schema: schemas,
        engine: true,
        context: (context: GraphQLCustomResolversContext) => {
            const requestTyped = (context as any).req as IUserRequest;
            const newContext: GraphQLCustomContext = {
                loaders: new DataLoaders(requestTyped)
            };
            return newContext;
        },
        playground: {
            settings: {
                "general.betaUpdates": false,
                "editor.theme": "dark",
                "editor.reuseHeaders": true,
                "tracing.hideTracingResponse": false,
                "editor.fontSize": 14,
                "editor.fontFamily": "'Source Code Pro', 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace",
                "request.credentials": "same-origin"
            }
        }
});
playgroundServer.applyMiddleware({ app, path: playgroundEndpoint, cors: true });

The second configuration is for the API. The arrangement is slightly more simple. The reason is that the middleware needs to disable the playground and nothing else.

app.use(apiEndpoint, authorizeUserMiddleware);
const apiServer = new ApolloServer({
    schema: schemas,
    context: (context: GraphQLCustomResolversContext) => {
        const requestTyped = (context as any).req as IUserRequest;
        const newContext: GraphQLCustomContext = {
            loaders: new DataLoaders(requestTyped)
        };
        return newContext;
    },
    playground: false,
    introspection: false
});
apiServer.applyMiddleware({ app, path: apiEndpoint, cors: true });

In both cases, I set a custom “authorizeUserMiddleware” that read the user credential and figure out what the user can do or not. The important parameters to disable are the playground, the introspection

The code that is hooking Apollo middleware into Express server next were you define the Express variable. From there, you need to start the server and both URLs will be served.

Related GraphQL Post

GraphQL Resolvers with Apollo

In this article, we will discuss about two topics that concern resolvers and performance. So far, and by default, everytime a resolver is invoked, this one execute its actions which is mostly be to fetch the data. The problem is that in a graph there is potentially redundant information that will be fetched several time causing performance issue.

For example, a query on an object that is cyclic with cyclic information will cause duplication of call. Imagine querying for obj1->obj2->obj1->obj2.

The problem becomes gargantuan with an array of object. Imagine that you have a single query for each type that is in a big array, you would perform many hundred or thousand of requests while in practice you probably would have use a special endpoint that return a batch of all the information.

The good news is that GraphQL has the concept of resolving at many levels. It is possible to resolve at the root level, which mean directly at the query level. But, alos at any edge which is great for an edge into an array of object or a heavy object that require special need. How, it is possible to resolve at the field level which can also be interesting in the case of a particular field that needs to be tackled differently of its general type.

Three different resolvers: Query, Edges and Fields

The two concepts we will investigate is “look-ahead” and “batching”. The look ahead is the idea of looking in the query schema and performing chirurgical analysis of what is requested. Batching is the of collecting all the desired data to fetch and fetch it once we are done traversing the tree. It means that if in the graph we have several times the same entity to query that we will only do it once — at the end. From these two summaries, it is clear that one is to optimize the query in term of figuring out which would be the best while the second is to avoid redundant calls. The former can help for avoiding calling several endpoints by redirecting the logic into a single endpoint while the latter removes querying the same element.

Look-ahead

A parent children is the common scenario. Imagine a parent who has many children. GraphQL by default will call the resolver for the parent and then will call a single resolver by children. If you have the resolver of the parent fetching the parent data (1 HTTP request) and then one fetch at each child (1 HTTP request multiplied by the number of children) it can become not performant. Even if the GraphQL is connected directly to a database, it would not be performant on a big list of children. The database scenario is often easier to visualize. Instead of making several SELECT statement with a WHERE clause that specify a single child ID, we would do a SELECT statement with a IN clause that specify the array of IDs. That way, it would return a single query response with many rows. In REST, if you have an endpoint that allows the parent to expand the children, you can use that endpoint instead of the one that only return the immediate parent attribute.

In GraphQL, you can peek at what is being queried. The look-ahead notion is the exploration of what the user specified in the query. The information is available in the fourth parameter of the query. The parameter’s type is “GraphQLResolveInfo”. You can use a NPM package named “graphQLFields” that will give you an easy way to access the information.

const fields = graphQLFields(graphQLResolveInfo);

Once you have extracted all the fields, you can check if the children node is being requested. If not, you can fetch the parent information without the additional payload (SELECT directly the ID without further data from children).

 if (fields.sites !== undefined){
    // Perform a more exhaustive query that will save us many small request
}

There is still one issue with the look-ahead: the children resolver is still called and will still perform the request. How can we notify the children that we already have everything we need in a clean way? This is where batching come in.

Batching

Batching is doing two things: cache and batch many ids. The whole idea is that instead of calling directly your SQL or REST endpoints, you call the DataLoader. It is a layer of abstraction that will check if we already have a promise for the key requested. If so, it returns the existing promise. The promise can be already resolved which would be very fast. The DataLoader library is a NPM package that has its own TypeScript definition file which is convenient if you are writing your code in TypeScript.

Naturally, the DataLoader is taking an array of the key. Even if you want to request for a single element, the DataLoader will presume that you query for a collection. I will not go in this article about pattern that you can use other than mentioning that you could look at the number of ids passed in the DataLoader and take a smart decision about how to fetch the data. Worth mentioning, the load function of the DataLoader that is needed to get the information from the cache or the code inside the data loader (to fetch) can be invoked multiple times. The DataLoader will coalesce all singular loads which occur within a single tick and then call your batch loading function.

An effective way to work with DataLoader is to have a single DataLoader by way to query the information. For example, if you query a “parent” entity by id, you would have a DataLoader for “parent” by “id”. You will have one for “parent” by “name” and one for “child” by “id”, etc. The separation might sound redundant but a single GraphQL query does not ask for many entities in a different way, hence does not duplicate much.

A good way to keep everything tidy up is to define a class into which we can inject the current user’s request. It gives all the security information like any authentication bearer token that the fetching code might need. The class trickle down the context information (user’s HTTP request) by having the request passed in its constructor parameter down to the service that will fetch the data. In the following code, you can see the pattern.

export class DataLoaders {
    private dataSources: GraphQLCustomDataSources;
    public getParentByParentId: DataLoader<number, Parent>;
    public getChildByChildId: DataLoader<number, Child>;
    public getChildrenByParentId: DataLoader<number, Child[]>;

    public constructor(requestTyped: IUserRequest) {
        this.dataSources = {
            sourceApi1: new Api1HttpService(requestTyped),
            sourceApi2: new Api2HttpService(requestTyped)
        };

        this.getParentByParentId = new DataLoader<number, Cache[]>(parentIds => {
            const proms: Promise<Parent[]>[] = [];
            for (let i = 0; i < parentIds.length; i++) {
                proms.push(this.dataSource.sourceApi1.getParent(parentIds[i]));
            }
            return Promise.all(proms);
        });

        // And so on for each DataLoader...

    }
}

The code above is a short version of what it can be with two entities: Parent and Child. In reality, you would have way more DataLoader and might want to breakdown each detail into a separated file and use the DataLoaders class as a facade to all the logic. The goal here is to have a single point of initialization to get the HTTP request passed down to the implementation of the data source.

Still, there is an issue. We are caching the DataLoader of the Parent entity, not the Child entity. It means that when the GraphQL traverse and invokes the children resolver, that this one will call the DataLoader that request the child information by child id, not by parent ID. There are many patterns. You could invoke the parent DataLoader and check if the data is already present. You can also have the parent DataLoader primes the child DataLoader. Priming the data means to set in another cache the data. The following code can be added to the DataLoader previously built. Now, the GraphQL invokes the DataLoader of the parent, get the data and populate the parent’s cache. Because it has the information about the children, it loops the collection and primes the child’s DataLoader as well. The traversal continues and the child’s resolver calls the child’s DataLoader that has a promise resolved with the child data.

children.forEach(c => {
      this.getChildByChildId.prime(c.id, c);
});

From there, you instantiate the class once in the Apollo’s server configuration. The instantiation will occur at every request, hence no data is mixed between users.

async function apolloServerConfig() {
    const serverConfig: ApolloServerConfig = {
        schema: schemas,
        context: (context: GraphQLCustomResolversContext) => {
             const newContext: GraphQLCustomContext = {
                loaders: new DataLoaders(requestTyped)
            };
            return newContext;
        },
    // ...
    }
}

Summary

The DataLoader library is useful to cache data during a single request when GraphQL is traversing the tree. A parent node can look-ahead and load in batch information reducing the number of future requests. The DataLoader library cache the result for each DataLoader. In the code presented, the DataLoader was filling up the parent loader which might not be useful in the situation but by priming the child’s DataLoader jettisoned all costly subsequent in the child’s resolver.

Related GraphQL Articles