How to Run Apollo GraphQL in a QWIK Endpoint
Published: 2023-07-09 12:28:21

---
title: How to Run Apollo GraphQL in a QWIK Endpoint
published: true
description: Having a single process running for both the QWIK and the Apollo GQL endpoint might be useful
tags: 
# cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/isj9zmg1suklj28pbyxe.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-07-09 11:54 +0000
---

![QWIK + Apollo server](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/isj9zmg1suklj28pbyxe.jpg)

## Short version

- [code](https://github.com/gparlakov/qwik-endpoint-apollo-graphql) Github

## Long version

Highlights:

- starting with settin up QWIK and Apollo server
- we'll add a couple of routes
  - one to expose the graphql as an endpoint
  - one to consume it
- in the endpoint we'll use code inspired by the expressMiddlware for Apollo server 4 and update it so it works with the QWIK `RequestHandler` interface
- on the consuming side
  - fetch will need a slighly different URL when working on the server (SSR) vs the client so we'll use the `onRequest` and `routeLoader` to provide the URL from QWIK down to the `gqlCall` function

### 0. Setting up QWIK

- start by setting up [QWIK](https://qwik.builder.io/docs/getting-started/)
- `npm create qwik@latest`
  - we used the empty app template
- branch in [GitHub](https://github.com/gparlakov/qwik-endpoint-apollo-graphql/tree/step-0-init-qwik)

### 1. Set up routes

- one route for the graphql endpoint
  - so create a file
- one for a page that will consume that GQL
- branch in [GitHub](https://github.com/gparlakov/qwik-endpoint-apollo-graphql/tree/step-1-endpoint-and-page-init)

### 2. Setting up Apollo

- Apollo [getting started docs](https://www.apollographql.com/docs/apollo-server/getting-started)
- `npm install @apollo/server graphql`
- Typescript and node types already installed and tsconfig configured by qwik so we can skip that
- create the `./graphql/index.ts` file
- start with the schema

  ```ts
    import { ApolloServer } from '@apollo/server';
    import { startStandaloneServer } from '@apollo/server/standalone';

    // A schema is a collection of type definitions (hence "typeDefs")
    // that together define the "shape" of queries that are executed against
    // your data.
    const typeDefs = `#graphql
    # Comments in GraphQL strings (such as this one) start with the hash (#) symbol.

    # This "Book" type defines the queryable fields for every book in our data source.
    type Book {
        title: String
        author: String
    }

    # The "Query" type is special: it lists all of the available queries that
    # clients can execute, along with the return type for each. In this
    # case, the "books" query returns an array of zero or more Books (defined above).
    type Query {
        books: [Book]
    }
    `;
  ```

- add a resolver and the server

  ```ts
  import { ApolloServer } from '@apollo/server';

  // A schema is a collection of type definitions (hence "typeDefs")
  // that together define the "shape" of queries that are executed against
  // your data.
  const typeDefs = `#graphql
    # Comments in GraphQL strings (such as this one) start with the hash (#) symbol.

    # This "Book" type defines the queryable fields for every book in our data source.
    type Book {
      title: String
      author: String
    }

    # The "Query" type is special: it lists all of the available queries that
    # clients can execute, along with the return type for each. In this
    # case, the "books" query returns an array of zero or more Books (defined above).
    type Query {
      books: [Book]
    }
  `;


  const books = [
      {
        title: 'The Awakening',
        author: 'Kate Chopin',
      },
      {
        title: 'City of Glass',
        author: 'Paul Auster',
      },
    ];


  // Resolvers define how to fetch the types defined in your schema.
  // This resolver retrieves books from the "books" array above.
  const resolvers = {
      Query: {
          books: () => books,
      },
  };

  // The ApolloServer constructor requires two parameters: your schema
  // definition and your set of resolvers.
  const server = new ApolloServer({
      typeDefs,
      resolvers,
  });

  export const serverStarted = server.start();
  ```

- on the last line we export the serverStarted which we'll use in the next step to expose the GraphQL API and UI
- branch in [GitHub](https://github.com/gparlakov/qwik-endpoint-apollo-graphql/tree/step-2-setup-apollo)

### 3. Starting the server in an endpoint

- we have the endpoint in the `src/routes/graphql/index.ts` file
- the following is inspired and using the code from expressMiddleware `@apollo/server/src/express4/index.ts`

  ```ts
  import { ContextFunction, BaseContext, HeaderMap, HTTPGraphQLRequest } from '@apollo/server';
  import { RequestHandler } from '@builder.io/qwik-city';
  import { serverStarted, server } from '~/graphql';

  export const onRequest: RequestHandler = async ({
    parseBody,
    method,
    next,
    send,
    request,
    query,
    getWritableStream,
  }) => {
    await serverStarted;

    // from expressMiddleware node_modules/@apollo/server/src/express4/index.ts
    server.assertStarted('QWIK Endpoint');

    // This `any` is safe because the overload above shows that context can
    // only be left out if you're using BaseContext as your context, and {} is a
    // valid BaseContext.
    const defaultContext: ContextFunction<[{}], BaseContext> = async () => ({});

    return parseBody().then((body) => {
      const headers = new HeaderMap(request.headers);

      const httpGraphQLRequest: HTTPGraphQLRequest = {
        method: method.toUpperCase(),
        headers,
        search: query.toString() ?? '',
        body: body ?? {},
      };

      return server
        .executeHTTPGraphQLRequest({
          httpGraphQLRequest,
          context: () => defaultContext({}),
        })
        .then(async (httpGraphQLResponse) => {
          if (httpGraphQLResponse.body.kind === 'complete') {
            const response = new Response(httpGraphQLResponse.body.string, {
              status: 200,
              headers: [...httpGraphQLResponse.headers.entries()],
            });

            send(response);
            return;
          }

          const writableStream = getWritableStream();
          const writer = writableStream.getWriter();
          const encoder = new TextEncoder();

          for await (const chunk of httpGraphQLResponse.body.asyncIterator) {
            writer.write(encoder.encode(chunk));
          }
          writer.close();
        })
        .catch((e) => {
          console.error(e);
          next();
        });
    });
  };

  ```

- this basically let's the Apollo server have the body and headers
- and then relays the response back to the client
  - in one go - if the `kind === complete`
  - otherwise - in a stream  
![GraphQL Sandbox - dev-only by default](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/za3a0k1by85kzoypa4er.png)

- branch in [GitHub](https://github.com/gparlakov/qwik-endpoint-apollo-graphql/tree/step-3-start-apollo-in-the-endpoint)

### 4. Consume from a page

- in the `src/routes/page/index.tsx` we have the placeholder of a page
- in the `src/routes/page/gql-call.ts` let's create a function to make a gql call

- ```ts
    export function gqlCall<T>(
      url: string,
      body: string,
      controller?: AbortController
    ): Promise<{ data?: T; errors?: unknown[] }> {
      return fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
        },
        body: JSON.stringify({ query: body }),
        signal: controller?.signal,
      })
        .then((r) => {
          if (r.status === 200) {
            const value = r.json();
            return value;
          }

          return r.body
            ?.getReader()
            .read()
            .then((v) => {
              throw new Error(`An error occurred: , /n ${r.statusText} /n${String.fromCodePoint(...(v.value ?? []))}`);
            });
        })
        .catch((e) => console.error(e));
    }

  ```

  - it adds the necessary minimum headers, reads the body, and has a minimal error handling

- now we can make a call in the page's component using the Qwik [Resource](https://qwik.builder.io/api/qwik/#resource) primitive/component

    ```ts
    import { Resource, component$, useResource$, useSignal, useTask$ } from '@builder.io/qwik';
    import { gqlCall } from './gql-call';

    const titlesQuery = `#gql
    query BookTitlesQuery {
        books {
            title
        }
    }
    `

    export default component$(() => {

        const reload = useSignal(0)
        const res = useResource$(({track}) => {
            track(reload)
            return gqlCall<{ books: { title: string }[] }>(JSON.stringify({ query: titlesQuery }));
        })

        return (
            <>
                <button onClick$={() => reload.value +=1}>:reload:</button>
                <Resource
                    value={res}
                    onResolved={({ data }) => data != null && Array.isArray(data?.books)
                        ? <>{data.books.map(b => <div> {b.title}</div>)}</>
                        : <div>{JSON.stringify(data)}</div>
                    }
                />
            </>
        );
    });

    ```

- branch in [GitHub](https://github.com/gparlakov/qwik-endpoint-apollo-graphql/tree/step-4-consume-from-qwik-page)

### 5. Import the GraphQL schema

> In a Real-world® scenario we would probably read that off of an environment variable:
>
> - `const typeDefsFile = process.env.GRAPHQL_SCHEMA`

To consume the GraphQL schema from a file we need a couple of changes:

- starting with the external file  `src/graphql/schema.graphql`
  - for this demo we'll just copy over the book schema from `index.ts`
- then in our `src/graphql/index.ts` we can

```ts
...
import typeDefs from './schema.graphql?raw';
....
```

- so at runtime `typeDefs` will hold the raw contents of the schema.graphql file allowing for the Apollo server to be built
- branch in [GitHub](https://github.com/gparlakov/qwik-endpoint-apollo-graphql/tree/step-5-external-graphql-schema)

## Summary

Thus we have a single process that runs both the QWIK ssr and client code as well as the GraphQL server. We can use the endpoint /graphql by the app or externally. 

Cheers.

## 🧞‍🙏 Thanks for reading!
Give some 💖 or 🦄  if you liked this post.
These help other people find this post and encourage me to write more. Thanks!

Check out my projects:
- [SCuri](https://npmjs.com/packages/scuri) — Unit test boilerplate automation (with Enterprise support option too)
- [ngx-forms-typed](https://npmjs.com/packages/ngx-forms-typed) — Angular form, only strong typed!
- [ngx-show-form-control](https://npmjs.com/packages/ngx-show-form-control) — Visualize/Edit any FormControl/Group


<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/bHQk8Cu"><img src="https://cdn.buymeacoffee.com/buttons/bmc-new-btn-logo.svg" alt="Buy me a coffee"><span style="margin-left:15px"></span></a>