Skip to content

RESTDataSource in Apollo GraphQL

When building a GraphQL server with Apollo, you might come across scenarios where you need to integrate with external REST micro services. Apollo provides a convenient way to handle such integrations

What is RESTDataSource?

RESTDataSource is a class provided by Apollo that helps simplify making HTTP requests to RESTful services. It abstracts away many of the common HTTP request tasks and provides methods to interact with the API endpoints.

Let’s start by creating a Services class that extends RESTDataSource. This class will encapsulate the logic for making requests to your RESTful services.

// Services.ts
import { RESTDataSource } from "apollo-datasource-rest";

export class Services extends RESTDataSource {
  override baseURL = process.env.API_URL + "/api";

  async saveListing(body: SaveListingPayload): Promise<{ processed: boolean }> {
    return this.post(`listings`, body);
  }
}

In this example, we’ve defined a method, saveListing that use the post method from RESTDataSource to send POST requests to the respective endpoint.

Integrating Services with Apollo Server

Now that we have our Services class set up, let’s integrate it with your Apollo GraphQL server.

Update the dataSources configuration in your Apollo server setup:

export interface ApolloContext extends BaseContext {
  authorization?: string;
  jwt?: jwt.JwtPayload | null | string;
  dataSources: {
++   services: Services;
  };
}
const server = new ApolloServer({
  resolvers,
  typeDefs,
  introspection: true,
  plugins: [authenticationMiddleware(["LoginMutation", "Login"])],
  context: async (req, res) => ({
    req,
    res,
    authorization: req?.headers?.authorization,
    jwt: decode(req?.headers?.authorization),
    dataSources: {
++    services: new Services(),
    },
  }),
});

Using the RESTDataSource Methods

With the Services class integrated into your Apollo server, you can now use its methods in your resolvers.

For example, let’s assume you have a resolver that needs to save a listing:

// resolvers/saveListing.ts
import { ApolloContext } from "@/pages/api/graphql";
import { ApolloError } from "apollo-server";
import { MutationSaveListingArgs } from "__generated__/graphql";

export async function saveListing(
  _: unknown,
  { input }: MutationSaveListingArgs,
  { dataSources: { services }, jwt }: ApolloContext
): Promise<ApolloError | SaveListingResponse> {
  try {
    return await services.saveListing({
      ...input,
      user: { uuid: (jwt as { uuid: string })?.uuid },
    });
  } catch (error) {
    throw new ApolloError("Failed to save listing.", "SAVE_LISTING_ERROR");
  }
}

In this example, the saveListing resolver uses the services.saveListing method to send the necessary data to the RESTful service and receives the response.

This approach allows you to encapsulate the HTTP request logic, making your resolvers cleaner and more focused on your business logic. With the RESTDataSource, you have a powerful tool to streamline your GraphQL server’s interaction with RESTful APIs.

Happy Hacking !