AI meets GraphQL: Leveraging persisted queries for AI agents

I had the chance to attend Apollo Summit 2024 again, where I met many smart and creative people in the industry. I had the opportunity to speak with one of the Apollo engineers at the conference, and he shared a fascinating idea about how GraphQL can become an ally in this era of AI agents.

I’ll be preparing a separate post to explore this concept further, specifically how persisted queries can drive AI experiences. But for now, I want to show you how you can leverage your existing tools and some open-source alternatives to enhance your AI with rich content from your own ecosystem.

Let’s dive in!

Getting started

  1. Create a free GraphOS Studio account
  2. Install Rover CLI
  3. Create a free MongoDB Atlas account
  4. Get an OpenAPI API key from OpenAI

Clone repository and install dependencies:

git clone http://g.hub/your-repo.git
cd your-repo
npm install

Deploy a supergraph

  1. Create a new Supergraph service in GraphOS Studio and deploy it to the Apollo registry. This will allow you to combine multiple subgraphs into a single endpoint, you need to select architecture as supergraph and then deploy it.
  2. It will give you a Rover CLI command, COPY the command, and update it to match below
  3. Deploy your schema using the following command
APOLLO_KEY={YOUR_APOLLO_KEY} rover subgraph publish {YOUR_GRAPH_REF} --schema ./graphs/shopify/schema.graphql --name monolith --routing-url https://summit-graphchat.myshopify.com/admin/api/2024-10/graphql.json
 

Create Persisted Queries

Vectorize the Schema

Running the Supergraph

The Factory interfaces and concrete factory objects represent the process of creating a new pet. The Factory interfaces specify the methods for creating a new pet, while the concrete factory objects are responsible for actually creating the pets. This allows for flexibility in the creation process, as different factories can be created for different pet lines, such as different breeds of dogs or different types of birds.

Implementation

Here, we will define the interface that will serve as a blueprint for our implementations.

interface Pet {
 getName(): string;
 getAge(): number;
 getLastMeal(): string;
}

Next, we can directly implement our interface into the Dog and Cat classes.

class Dog implements Pet {
  getName(): string {
    return "duke";
  }
  
  getAge(): number {
    return 4;
  }
 
  getLastMeal(): string {
    return "kibble"
  }
}
 
class Cat implements Pet {
  getName(): string {
    return "git";
  }
  
  getAge(): number {
    return 2;
  }
 
  getLastMeal(): string {
    return "fish"
  }
}

With the Factory Method pattern, it is desired to avoid instantiating them using the new operator, and instead, a factory is defined for each type of Pet.

interface PetFactory {
  create(): Pet;
}
 
class DogFactory implements PetFactory {
  create(): Pet {
    return new Dog();
  }
}
 
class CatFactory implements PetFactory {
   create(): Pet {
    return new Cat();
  }
}

By utilizing the Factory Method, you can ensure that the objects are only created once throughout the lifespan of the program. You can then pass these objects whenever the PetFactory interface is required, keeping the object creation logic centralized and unchanging.

const dogFactory = new DogFactory();
const catFactory = new CatFactory();
const factories: PetFactory[] = [dogFactory, catFactory, dogFactory];
for(let i = 0; i < factories.length; i++){
  factories[i].create()
}

This is a classic implementation of a factory design pattern, however, I don't have a strong preference for creating abstractions to handle small tasks. Instead, I prefer to use parameters to determine which object to instantiate, rather than relying on an interface.

const enum PetType {
  DOG,
  CAT
}
 
class PetCreator {
  create(petType: PetType): Pet {
    switch (petType) {
      case PetType.DOG:
        return new Dog();
        break;
      case PetType.Cat
        return new Cat();
        break;
    }
  }
}

This solution might seem okay for now, especially if you're still building the app. But as you add more types of objects, it'll quickly become a pain to keep updating the PetType and switch cases. Trust me, you'll want to rethink this code and use a Factory method interface in the future

In conclusion, the Factory Pattern is a powerful tool for creating objects in a clean and organized way. By encapsulating the object creation logic, it makes your code more maintainable and scalable. Whether you're just starting out with object-oriented programming or you're an experienced developer, it's definitely worth taking the time to understand and implement this pattern in your projects. Happy coding!