Data Federation with the New Stargate GraphQL Schema-First API

This post will show you how to use the new DataStax Stargate GraphQL API and Apollo data federation support now available for GraphQL to build and federate different schemas in Apache CassandraTM or Astra DB without ever having to touch Cassandra Query Language (CQL).

If you’re looking for an easy way to build and federate different schemas in your Apache CassandraTM or Astra DB. We’d like to show you how. In this post, we will introduce a couple of new features in Stargate that will make it easier than ever to build and federate different schemas in Cassandra or Astra DB.

One is the new schema-first Stargate GraphQL API. This API allows you to provide a GraphQL schema that will be automatically mapped to your Cassandra database or Astra DB. And, now with Apollo data federation support for GraphQL, it’s never been easier to reference entities from other data sets to provide more robust querying capabilities.

The Stargate GraphQL API and support for data federation were introduced in Stargate open-source 1.0.23 and are now available in Astra DB. Below, we offer an example of a data federation in action to give you a glimpse of the possibilities now available, one that is easy to duplicate and doesn’t require any knowledge of Cassandra Query Language (CQL). Let’s get started!

Setting up our GraphQL schema

In the URL below, you can see that we now have a GraphQL admin endpoint, a new feature that allows us to deploy our own GraphQL schemas:

Figure 1: GraphQL’s admin endpoint.

In this example, we’ll deploy a new GraphQL schema, and Stargate will create all the tables for us. We’ll create a product entity with a product code, and a name.

The Stargate GraphQL schema-first API is also able to support indexes. So we’ll add a category field declaring that we want a CQL index on that. For simplicity, we’ll have a single category per product:

mutation {
deploySchema(
keyspace: “catalog”
schema: “””
type Product @key @cql_input {
sku: String! @cql_column(partitionKey: true)
name: String
category: String @cql_index
}

Next, we’ll tell Stargate that we want to generate a GraphQL operation to create new products that will be converted into a CQL INSERT statement under the hood:

type Mutation {
insertProduct(product: ProductInput): Product
}

And, here’s what it all looks like in our GraphQL playground:

Figure 2: Setting up a new GraphQL schema in the GraphQL playground.

Adding a couple of queries and deploying our schema

You may notice in the figure above that GraphQL also supports custom conditions on fields. This combined with the ability to add indexes allows us to do some pretty neat things. For example, indexing the category field will allow us to use it in a query. Here, when we pass a category, we’ll get back a list of products. And in our schema, with the partitionKey in the sku field set to true, we can set up a query to fetch all products by a list of SKUs:

type Query {
productsBySkus(
skus: [String] @cql_where(field: “sku”, predicate: IN)
): [Product]
productsByCategory(
category: String
): [Product]
}

We use the @cql_where directive to indicate that the argument targets the sku field (this is only needed when the names differ) and uses the IN predicate (if not specified, it defaults to EQ). Here’s what we get when we deploy our new schema:

Figure 3: Deploying our new schema in GraphQL.

Using our newly deployed schema to run our queries

If you’re familiar with the legacy GraphQL API, the CQL-first API, you’ll notice that it’s the same URL in our example. Once we deploy a custom schema, it replaces the CQL-first schema for that particular keyspace.

To run our queries, we’ll first need to add a few products to our Cassandra database as shown in the screenshot below:

mutation {
p1: insertProduct(
product: {
name: “Mechanical keyboard”
sku: “kbd123”
category: “Electronics”
}
) {
sku
}
p2: insertProduct(
product: {
name: “Nintendo Switch”
sku: “swtch42”
category: “Electronics”
}
) {
sku
}
p3: insertProduct(
product: { name: “Instant Pot”, sku: “inst12”, category: “Kitchen” }
) {
sku
}
}

Now we can query with a custom predicate, in this case, by our skus:

{
productsBySkus(skus: [“swtch42”, “inst12”]) {
sku
name
}
}

And here are the results:

Figure 4: Results from our sku list query.

And, if we run a query by index, we get the results shown in the figure below:

{
productsByCategory(category: “Electronics”) {
sku
name
}
}
Figure 5: Results from our index query.

Data federation

So far, we’ve been working with Stargate GraphQL and a Cassandra database. Now imagine that we have another GraphQL schema elsewhere in our system.

Figure 6: Conceptual diagram of our federated schemas

The schema in this second database doesn’t necessarily have to be a Stargate schema. Maybe it’s a legacy database somewhere and you have written a custom GraphQL service for it. In this example, we’re going to use a mock NodeJS server. And, we want to federate both of our schemas so we can expose them as a single schema with Apollo Gateway.

Federation not only allows us to put our two schemas together but also to reference entities across both schemas. Let’s take a closer look at how this is done.

Building our mock Node.js service

With federation, we can reference entities from the other schema from our target schema. To do this, we’ll stubb in the product type that we created in Stargate earlier and reference the SKU, which serves as the key that allows us to fetch our entities:

const { ApolloServer, gql } = require(“apollo-server”);
const { buildFederatedSchema } = require(“@apollo/fedeeration”);
const typeDefs = gql’ # Stub of the product entity from Stargate:
extend type Product @key(fields: “sku”) {
sku: String! @external
}

Now we can use that as one of our own types in the local schema here. So, in this example, let’s add an order type to give us a list of products:

   type Order {
id: Int!
products: [Product]
}
extend type Query {
order(id: Int): Order
}
;
const resolvers = {
Query: {
order(_, orgs) {
return orders.find(order => order.id == orgs.id);
}
}
};

To create the mock orders schema, we’ll use the following code:

const server = new ApolloServer({
schema: buildFederatedSchema([
{
typeDefs,
resolvers
}
])
});
server.listen({ port: 4001 }).then(({ url }) => {
console.log( Orders service ready at ${url}’);
});

We’ll also add a query to fetch an order by ID. Since this is just a mock service, we’ll hard code everything right here in the code:

const orders = [
{ id: 1, products: [ {sku: “kbd123”, {sku: “inst12”} ] },
{ id: 2, products: [ {sku: “kbd123”, {sku: “switch42”} ] },
]:

Notice that we now have two orders that reference the SKUs from our other schema. And, since we’re using the stub, the SKUs are the only thing present here.

You can see the full source code for the mock Node.js service here.

Coding our Apollo Gateway

Here’s how we’ll code our Apollo Gateway:

const { ApolloServer } = require(apollo-server”);
const { ApolloGateway, RemoteGraphQLDataSource } = require(“@apollo/gateway”);

For the Stargate token that Apollo Gateway needs to fetch the schema, you will need to provide your own x-cassandra-token HTTP header and the gateway will forward it to Stargate:

const stargateIntrospectionToken = ‘your x-cassandra-token’ HTTP header’;class StargateGraphQLDataSource extends RemoteGraphQLDataSource {
willSendRequest({ request, context }) {
const token = context.stargateToken
if (token != null) {
request.http.header.set(‘x-cassandra-token’, token);
}
}
}

Note that here, we are referencing the URLs of the two services that we’re going to federate, the Stargate schema that we created earlier and the mock schema we just created. Note that if you’re using Astra DB, you would need to change the URL from the Stargate URL shown below to point to your Astra DB instead:

const gateway = new ApolloGateway({
serviceList: [
// Stargate:
{name: “catalog”, url: “http://127.0.0.2:8080/graphql”}
// External service (mock):
{name: “orders”, url: “http://localhost: 4001/graphql” }
],
buildService({name, url}) {
if (name == “catalog”) {
return new StargateGraphQLDataSource({url});
} else {
return new RemoteGraphQLDataSource({url});
}
},

You can see the full source code for the Gateway on GitHub.

Running your two federated services

Now let’s start our two services:

Our two federated services running together.

Jumping back into our GraphQL playground, we can see our federated schema:

Federated schema exposed on the Gateway.

So, you can see in our results that our two services are properly federated:

  • The product mutation is from Stargate.
  • The order type and product type are from our mock Node.js service.
  • The product input is from Stargate.
  • And we have the queries from both services.

Now, if we want to further test this, we’ll query on “order” we’ll get the following results:

Results illustrating successful federation.

You’ll notice the SKUs returned were from in our mock data and that the query also completed the other product fields with data coming from the Stargate service, illustrating that our services are indeed correctly federated.

With this example, you can see how easy it can be with Stargate GraphQL to build a schema in Cassandra or Astra DB without ever having to touch CQL.If you’d like to try this yourself, you can find all of this information and more in our Stargate Federation Demo on GitHub.

Follow the DataStax Tech Blog for more developer stories. Check out our YouTube channel for tutorials and here for DataStax Developers on Twitter for the latest news about our developer community.

Resources

  1. Accessing Cassandra with the Stargate GraphQL API
  2. Astra DB
  3. Introduction to GraphQL
  4. Stargate GraphQL API QuickStart
  5. A guide to using Apollo Gateway for Federation
  6. GitHub Example of Data Federation in Stargate

--

--

--

We’re huge believers in modern, cloud native technologies like Kubernetes; we are making Cassandra ready for millions of developers through simple APIs; and we are committed to delivering the industry’s first and only open, multi-cloud serverless database: DataStax Astra DB.

Recommended from Medium

Install Redis Cluster with Ansible

Tackling global warming with online chess game 🌎 ♞

AWS DynamoDB. Access capacity and service throttling.

SQL — Create & Insert

Part 2 of 3: Coding a Real-time Sensor Data Web Widget

Deploy Laravel Projects On Shared Hosting

Desktop and Web Versions of WhatsApp will soon require two-step verification — Top Tutor guru

Why have I created a new Query Language? Story of ColorfulDB — PART 2

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
DataStax

DataStax

DataStax is the company behind the massively scalable, highly available, cloud-native NoSQL data platform built on Apache Cassandra®.

More from Medium

A Beginner’s Guide to Benchmarking with NoSQLBench

Defusing an ElasticSearch Mapping Explosion with Slots

Create a Distributed Database with High Availability with Apache ShardingSphere

Kafka Connector with Custom Transformation