GraphQL federation example with Apollo Federation and Apollo GraphOS

Building extremely fast, low-latency GraphQL APIs with Apollo and Cube.

Cover of the 'GraphQL federation example with Apollo Federation and Apollo GraphOS' blog post

This article will teach you how to build fast GraphQL APIs with sub-second latency, reduce architecture complexity, and make engineering teams more efficient using Apollo GraphQL Federation and Cube’s GraphQL API.

Step-by-step, we'll build an Apollo supergraph with Apollo GraphOS, federate queries from external services, and boost query performance (reduce latency) for analytical queries more than tenfold.

live demo

You can play around with the demo application and find the complete code on GitHub.

What are we building?

Take a look at the architecture diagram below. We'll first create an Apollo Server to load data from PostgreSQL. Then, we'll build an Apollo supergraph with Apollo GraphOS that fetches data from both a caching layer that accesses the same PostgreSQL instance, built with Cube’s GraphQL API, and the Apollo Server we built previously.

This will enable delivering data in milliseconds to end users of a web application built with React and Apollo Client. You will see that Cube can make any analytical query to PostgreSQL run with sub-second latency, making your GraphQL queries just as fast.

diagram

We’ll use an example dataset of fraudulent financial transactions. The dataset is collected from Kaggle and only has one table called fraud. To read more, check out the description of the dataset.

Challenges of data-intensive applications

Data-intensive front-end apps consume data from multiple services with GraphQL endpoints. These GraphQL endpoints can be accessed independently. But, best practice is to combine them into a supergraph.

This makes it easier for your front-end team to work with one endpoint and unified schema while the back-end team has modular graphs and separation of concerns among multiple services.

https://ucarecdn.com/b5b770a1-2701-4189-8cff-04f474822624/

When building data-intensive applications, you’ll run complex queries with heavy joins and aggregations. Querying massive amounts of data and aggregating them in the application layer is time-consuming and causes sub-optimal performance.

Let's see how these issues are resolved with Apollo and Cube.

What is Apollo GraphQL?

Apollo GraphQL is the leading open-source GraphQL implementation with 17 million monthly downloads! Their toolset helps build, deliver, and observe modern GraphQL apps and APIs by leveraging the power of supergraphs.

Apollo Federation — the API Gateway. Apollo Federation is the industry-standard open architecture for building a distributed supergraph that scales across teams. The Apollo team recently released a new Apollo Router to compose a supergraph. However, their most recent addition, Apollo GraphOS uses the Apollo Router in the Cloud, so you don't have to go through the hassle of running it yourself. We'll use Apollo GraphOS to compose a supergraph from multiple subgraphs, determine a query plan, and route requests across your services. Learn more about Apollo Federation in this post.

Apollo Server — the back-end server. The best way to build a production-ready TypeScript GraphQL server that connects to any microservice, API, or database. Compatible with all popular JavaScript frameworks and deployable in serverless environments.

Apollo Client — the front-end framework. The industry-standard open-source GraphQL client for web, iOS and Android apps with everything you need to fetch, cache, and modify application data.

Apollo Studio — the supergraph manager. Studio manages your supergraph lifecycle. Apollo tracks your GraphQL schemas in a registry to create a central source of truth for everything in your supergraph. Studio allows developers to explore data, collaborate on queries, observe usage, and deliver schema changes with agility and confidence.

Getting started with Apollo GraphQL Server

Let’s start by creating an Apollo Server with Node.js, and connecting it to our PostgreSQL data source.

Create a fresh folder, and run npm init -y to initialize npm.

Next, let’s install a few modules we need.

npm i apollo-server-express \
express \
graphql \
postgres

Let’s move on to creating the database connection. Here’s are the credentials to the database we provided.

Hostname: demo-db-examples.cube.dev
Port: 5432
Database: fraud
Username: cube
Password: 12345

Create a folder called database and paste this code into an index.js file.

// database/index.js
const postgres = require('postgres')
const sql = postgres('postgres://cube:12345@demo-db-examples.cube.dev:5432/fraud', { max: 10 })
module.exports = sql

Once the database is configured, we can configure the GraphQL queries.

Create another folder called graphql and paste this code into a fraud.js file.

// graphql/fraud.js
const { gql } = require('apollo-server-express')
const sql = require('../database')
async function amountSumFraudsWithStep({ isFraud, stepStart, stepEnd }) {
return sql`
SELECT
"fraud"."isFraud" as isfraud,
"fraud"."step" as step,
"fraud"."type" as type,
sum("fraud"."amount") as amountsum
FROM public.fraud AS "fraud"
WHERE
"fraud"."isFraud" = ${isFraud}
AND
"fraud"."step" BETWEEN ${stepStart} AND ${stepEnd}
GROUP BY 1, 2, 3
ORDER BY 1 ASC;
`
}
module.exports = {
typeDefs: gql`
extend type Query {
fraudsByAmountSumWithStep(isFraud: Int, stepStart: Int, stepEnd: Int): [Fraud]
}
type Fraud {
id: ID!
step: Float
type: String
isfraud: Int
amountsum: Float
}
`,
resolvers: {
Query: {
fraudsByAmountSumWithStep: async (obj, args, context, info) =>
amountSumFraudsWithStep(
{ isFraud: args.isFraud, stepStart: args.stepStart, stepEnd: args.stepEnd }
)
},
}
}

You see the amountSumFraudsWithStep function will query PostgreSQL and aggregate the amount column.

Now create another folder called src and within it create a file called server.js.

// src/server.js
async function startServer() {
const express = require('express')
const { ApolloServer } = require('apollo-server-express')
const app = express()
const server = new ApolloServer({
modules: [
require('../graphql/fraud'),
],
})
await server.start()
server.applyMiddleware({ app })
app.get('/', (req, res) => res.send('🚀 Server is running!'))
app.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000`),
)
}
module.exports = startServer()

Finally, add an index.js file in the root directory.

// index.js
require('./src/server')

With all the code added, start the Node.js server. Jump into your terminal and run:

node index.js
...
🚀 Server ready at http://localhost:4000

Boom! With that, you have an Apollo Server running. Let’s run some analytics queries!

Running analytical queries with Apollo GraphQL Server

With the Apollo Server running, open up http://localhost:4000/graphql in your browser.

https://ucarecdn.com/4863849c-7388-4ad8-bc9d-21fc2d23ff3b/

Go ahead and click Query your server. This will take you to Apollo Studio sandbox. In the Operation field, go ahead and run the query below.

query FraudsByAmountSumWithStep($isFraud: Int, $stepStart: Int, $stepEnd: Int) {
fraudsByAmountSumWithStep(
isFraud: $isFraud,
stepStart: $stepStart,
stepEnd: $stepEnd
) {
step
type
amountsum
isfraud
}
}

Don’t forget to add values for the variables.

https://ucarecdn.com/488bc893-166d-4715-aaad-7e11a81bc25b/

The query will take around 2 seconds.

You can use caching in Apollo, but it’s difficult to cache queries that often have varying filters. Caching alone can only cover a finite number of use cases. If I add a where clause, I get the same issue again even though caching is enabled.

Let’s try getting to the bottom of why running analytical queries is slow with GraphQL and PostgreSQL.

Why are analytical queries slow with GraphQL and PostgreSQL?

Analytical queries require aggregating the data by column. Postgres is a traditional row-oriented database. Row-oriented databases store information in the disk row by row.

Row-oriented databases don’t perform well in this case because all columns in every row need to be read from the disk instead of the few used in a query. You can learn more about how row-oriented databases work and their limitations in this blog post.

To mitigate this issue you need a caching mechanism that’s reliable and flexible.

User experience research consistently shows a positive correlation between faster response times and higher customer satisfaction. Half of your users will immediately drop off if the response time is over 3 seconds if we believe Google.

What we’ll do to mitigate this is to add Cube into our data stack. It’ll help us accelerate queries with caching and pre-aggregations by adding Cube’s GraphQL API to our federated supergraph.

What is Cube?

Cube is an open-source API-first headless business intelligence platform that connects to your data sources and makes queries fast, responsive, cost-effective, and consistent across your applications.

It enables data engineers and application developers to access and organize data to build performant data-intensive applications.

https://cubedev-blog-images.s3.us-east-2.amazonaws.com/0ec5f24c-a0e1-4202-8d35-5531be1a60e3.jpg

Cube’s API layer can efficiently aggregate your data and serve it to applications. Instead of querying complex, large datasets directly in your PostgreSQL database, you can use Cube as a middleware layer. Cube performs cachingpre-aggregation, and much more, making your analytical queries faster and more efficient.

Cube has a GraphQL API that easily connects to your Apollo Federation supergraph as a subgraph. This is how you unify all your GraphQL endpoints with Apollo Federation and get the added benefit of performant analytical queries.

Getting started with Cube

The easiest way to get started with Cube is with Cube Cloud. It provides a fully managed, ready-to-use Cube cluster. However, if you prefer self-hosting, follow this guide in the docs.

Let’s move on and create a new Cube deployment in Cube Cloud. You can select a cloud platform of your choice.

https://ucarecdn.com/893846b3-cf27-480d-85e4-6da56e679808/

Next, select + Create to get started with a fresh instance from scratch.

https://ucarecdn.com/fcdbd62c-dee1-416a-8a09-30543879c8f0/-/resize/1600x/-/format/webp/

Next, provide the database connection information. Select PostgreSQL.

https://ucarecdn.com/b026a245-1b42-4a88-9ff0-5ac7dd7f12be/-/resize/1600x/-/format/webp/

Now enter the same database credentials we used above when setting up Apollo Server, and select continue.

Hostname: demo-db-examples.cube.dev
Port: 5432
Database: fraud
Username: cube
Password: 12345

https://ucarecdn.com/18679106-baff-4675-a3aa-f72d7e9e3acf/-/resize/1600x/-/format/webp/

Cube auto-generates a Data Schema from your SQL tables. It’s used to model raw data into meaningful business definitions.

Select the fraud table for schema generation, and click Generate. It takes a few minutes for the Cube instance to get provisioned.

https://ucarecdn.com/ba39661f-5a6f-431f-b587-cccc978c4f7e/

Now, we can move on to defining our data model and accelerating queries with pre-aggregations.

Centralized data modeling

In your Cube deployment, select Schema in the left-hand navigation and click Enter Development Mode. Now let’s edit the Fraud.js schema definition to add a measure for the sum of transaction amounts.

https://ucarecdn.com/7a40e8a4-0c66-4f79-aad8-147e14a64ea7/

Once Development Mode is enabled, go ahead and paste the code below into the Fraud.js schema file.

cube(`Fraud`, {
sql: `SELECT * FROM public.fraud`,
preAggregations: {},
joins: {},
measures: {
count: {
type: `count`,
drillMembers: [nameorig, namedest]
},
amountSum: {
sql: `${CUBE}."amount"`,
type: `sum`,
},
},
dimensions: {
id: {
sql: `id`,
type: `number`,
primaryKey: true
},
step: {
sql: `${CUBE}."step"`,
type: `number`
},
newbalancedest: {
sql: `${CUBE}."newbalanceDest"`,
type: `string`
},
nameorig: {
sql: `${CUBE}."nameOrig"`,
type: `string`
},
oldbalanceorg: {
sql: `${CUBE}."oldbalanceOrg"`,
type: `string`
},
namedest: {
sql: `${CUBE}."nameDest"`,
type: `string`
},
newbalanceorg: {
sql: `${CUBE}."newbalanceOrg"`,
type: `string`
},
oldbalancedest: {
sql: `${CUBE}."oldbalanceDest"`,
type: `string`
},
type: {
sql: `${CUBE}."type"`,
type: `string`
},
amount: {
sql: `${CUBE}."amount"`,
type: `number`
},
isFraud: {
sql: `${CUBE}."isFraud"`,
type: `boolean`
},
isFlaggedFraud: {
sql: `${CUBE}."isFlaggedFraud"`,
type: `boolean`
}
}
});

Save the changes, and the data model will be updated. Next, commit and push these changes. Cube uses Git for version control. You can revert your changes anytime you like.

https://ucarecdn.com/abb3ab9e-e66b-484f-bb18-279ce06377f7/

Moving over to the Playground, you can run the same analytical query we ran previously with Apollo.

https://ucarecdn.com/aafbffdd-c6e0-40d0-96d6-8c4b4e96a865/

The query will also be mapped to a GraphQL query through the GraphiQL IDE that’s exposed within Cube Cloud.

https://ucarecdn.com/06bc7aa6-d6a5-486e-beab-558f21f97f0e/

Now we have a centralized data model where we can reliably handle business definitions without making any changes to your PostgreSQL database.

But, we’re only halfway there. Let’s add query acceleration with pre-aggregations as well.

Adding pre-aggregations to increase query performance

One of Cube’s most powerful features is pre-aggregations. They can reduce the execution time of a query drastically. In our case with this tutorial, we’ll be reducing the response time to well below 200 ms, or even less, for queries that took above 2 seconds.

In Cube, pre-aggregations are condensed versions of the source data. They are materialized ahead of time and persisted as tables separately from the raw data. To learn more about pre-aggregations, please follow this tutorial.

We also highly recommend you check these in-depth video workshops on pre-aggregations: Mastering Cube Pre-Aggregations and Advanced Pre-aggregations in Cube.

But now, let’s jump back into Development Mode. Select the Fraud.js schema file again. Update the preAggregations section to add a pre-aggregation definition.

cube(`Fraud`, {
sql: `SELECT * FROM public.fraud`,
preAggregations: {
fraudAmountSum: {
measures: [
Fraud.amountSum
],
dimensions: [
Fraud.isFraud,
Fraud.step,
Fraud.type
]
}
},
...
});

Save the changes, click Commit and push, and the pre-aggregation will be built for our analytical query. Here’s what the pre-aggregation should look like once the schema has been updated.

https://ucarecdn.com/8d1ce8e4-7848-4376-b5cd-985a340e4d86/

When you run the query next time in Cube, the data will be pre-aggregated and saved in Cube's caching layer inside of Cube Store.

Running this query again, you’ll see a massive performance increase.

https://ucarecdn.com/ef036fcf-3836-49ea-b13b-c26f1b83f89d/

The true power lies in still retaining query acceleration when using filters. That’s why pre-aggregations are so much more powerful than basic caching strategies.

https://ucarecdn.com/08fab11e-437c-423e-9e4c-3d4b0a2d3890/

At this point, your Cube instance is ready to be added to the Apollo Federation supergraph.

Creating an Apollo Federation supergraph with Apollo Studio and GraphOS

Head over to Apollo Studio. If you’re new, check out the Apollo Studio getting started guide here.

Go ahead and sign up and create an organization.

Studio automatically redirects you to your newly created organization, which is on the Serverless (Free) plan by default.

You now have an Apollo Studio organization, but it doesn't contain any graphs. Next we'll create a cloud supergraph, which will incorporate the existing GraphQL API we created above.

From your new organization in Apollo Studio, navigate to the Supergraphs tab. Click Connect your GraphQL API, and follow the steps below.

Go ahead and grab Cube’s GraphQL API endpoint and Authorization token.

https://ucarecdn.com/c75c856c-4d70-49c3-ba8b-4240f4590e05/

Paste the URL in the Endpoint URL field, and add the token by clicking the Provide HTTP Headers button.

https://ucarecdn.com/8a8f0214-1388-464a-9b5c-293c46fc3941/

Make sure to add a name to your subgraph as well, and click Next.

Now, we need to set up the Supergraph.

https://ucarecdn.com/2bdf460e-aaa1-494c-9355-c91c90f661c1/

Give the Supergraph an ID and Name, and move on to the next step. Now, add a variant to your Supergraph. Leave it as main for now and click the Create Supergraph button.

https://ucarecdn.com/048f7fa0-6360-4fc2-8129-5524976a1cf5/

This will take you to the Supergraph overview page.

https://ucarecdn.com/d93f5dd2-3f2f-490f-a0e9-4c9c90086fc6/

Open up the main variant of your Supergraph, and go into Settings → Cloud Routing. Here's where you see the API Endpoint to access the Apollo Federation Router within GraphOS.

https://ucarecdn.com/45e53f4b-a8df-4ff5-bdae-c480726b0cf3/

On this page you can also the Router config and add secrets.

Let's leave this as-is for now and move on to adding another subgraph. Navigate to the subgraphs page inside if the main Variant. Here, click the Add a subgraph button.

https://ucarecdn.com/970d25bd-2f52-496d-afb0-41b37eec1553/

Make sure to install Rover, Apollo’s CLI tool, then authenticate the Rover CLI by running this command:

rover config auth

This command will return a prompt. Copy the user token Apollo generated for you and paste it into the terminal prompt.

Next, create a file called apollo.graphql that will contain the SDL for the Apollo Server GraphQL API we created previously.

# graphql/apollo.graphql
type Fraud {
id: ID!
step: Float
type: String
isfraud: Int
amountsum: Float
}
type Query {
fraudsByAmountSumWithStep(isFraud: Int, stepStart: Int, stepEnd: Int): [Fraud]
}

We'll use this SDL to add a subgraph. In this example the Supergraph is called cube-team@main.

Make sure to run the rover command in the same directory where you saved the apollo.graphql file.

rover subgraph check cube-team@main \
--schema "./apollo.graphql" \
--name ApolloServer

This will run a check to make sure there are no breaking changes.

Finally, re-deploy your changes and publish your subgraph by running the following command.

rover subgraph publish cube-team@main \
--schema "./apollo.graphql" \
--name ApolloServer \
--routing-url "http://localhost:4000/graphql"

After running the command above, you'll see another subgraph get added to your Supergraph variant.

https://ucarecdn.com/dd9a00c8-d305-419e-98df-f0ec9ea939fe/

Replace the API endpoints with your own. Or, use these to get a working sample.

Nice! Now you have Apollo GraphOS running. How about we run some analytics queries?

Running analytical queries with Apollo GraphOS

With your Supergraph in Apollo GraphOS running, open up the API Endpoint that gets generated for you in your browser. For this example it's https://main--cube-team.apollographos.net/graphql.

https://ucarecdn.com/ae86c60b-9120-49c2-aeac-b8fb5c897e73/

Open the Apollo Studio sandbox and you’ll see you can query both Cube’s GraphQL API and the Apollo Server.

Again, in the left-hand side navigation you can see both query definitions. Go ahead run the same GraphQL query against the Apollo Server.

query FraudsByAmountSumWithStep($isFraud: Int, $stepStart: Int, $stepEnd: Int) {
fraudsByAmountSumWithStep(
isFraud: $isFraud,
stepStart: $stepStart,
stepEnd: $stepEnd
) {
step
type
amountsum
isfraud
}
}

https://ucarecdn.com/f2fda249-9921-4c98-a299-038cc4143125/

Now, let’s run a GraphQL query against Cube.

query CubeQuery {
cube(
where: {
fraud: {
AND: [
{isFraud: {equals: "1"} },
{step: {gte: 1}},
{step: {lte: 50}}
]
}
}
) {
fraud(orderBy: {step: asc}) {
amountSum
step
type
}
}
}

https://ucarecdn.com/804af4da-6bfd-4fc7-9634-15cef1c92658/

With that, let’s move on to building the front-end app!

Building data visualization with Apollo Client and React

For the front-end app, we’ll use React and Apollo Client, and query the Apollo GraphOS federated GraphQL API. We’ll use the nivo charting library, a modern production-ready data visualization tool.

You can check the full source code on GitHub and instantly run it with yarn dev. You'll get a copy of this demo application.

live demo

The entry point is src/index.js, and it uses a LineChart.jsx file to generate the nivo line chart.

We decided to showcase the power of pre-aggregations by generating queries that filter the steps into pages of 50 each, as well as choosing whether to show valid or fraudulent transactions.

Even though the query uses filters, it will still be accelerated due to using pre-aggregations in Cube!

Let’s walk through the contents of the React files. First the index.js.

import React from 'react';
import { useState, useEffect } from 'react'
import * as classes from './index.module.css'
import * as ReactDOM from 'react-dom/client';
import {
ApolloClient,
InMemoryCache,
ApolloProvider,
useQuery,
gql,
createHttpLink,
ApolloLink,
from,
} from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import {
tablePivotCube,
tablePivotApollo,
availableStepRanges,
defaultIsFraudSelection,
defaultStepSelection,
DisplayFraudAmountSum,
randomIntFromInterval,
} from './utils/utils';
const httpLink = createHttpLink({
uri: `${process.env.GRAPHQL_API_URL}`,
});
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
'Authorization': `${process.env.AUTHORIZATION}`,
}
}
});
let timestampsGlobal = {};
const roundTripLink = new ApolloLink((operation, forward) => {
operation.setContext({ start: new Date() });
timestampsGlobal = {};
return forward(operation).map((data) => {
timestampsGlobal[operation.operationName] = new Date() - operation.getContext().start;
return data;
});
});
const client = new ApolloClient({
link: from([roundTripLink, authLink.concat(httpLink)]),
cache: new InMemoryCache()
});
ReactDOM
.createRoot(document.getElementById('app'))
.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
)
function App() {
const [ timestamps, setTimestamps ] = useState(0);
useEffect(() => {
setTimestamps(timestampsGlobal)
}, [ timestampsGlobal ]);
const [ fraudChartDataCube, setFraudChartDataCube ] = useState([])
const [ fraudChartDataApollo, setFraudChartDataApollo ] = useState([])
const [ stepSelection, setStepSelection ] = useState(defaultStepSelection);
const selectedStep = availableStepRanges.find(x => x.id === stepSelection);
const [ isFraudSelection, setIsFraudSelection ] = useState(defaultIsFraudSelection);
const shuffleAndRun = () => {
setStepSelection(randomIntFromInterval(1, 14));
setIsFraudSelection(randomIntFromInterval(0, 1));
}
const GET_FRAUD_AMOUNT_SUM_CUBE = gql`
query CubeQuery {
cube(
where: {fraud: {AND: [
{step: {gte: ${selectedStep.start} }},
{step: {lte: ${selectedStep.end} }},
{isFraud: {equals: "${isFraudSelection}" }}
]}},
orderBy: {fraud: {step: asc}}
) {
fraud {
amountSum
step
type
}
}
}
`;
const {
loading: loadingFraudDataCube,
error: errorFraudDataCube,
data: fraudDataCube,
} = useQuery(GET_FRAUD_AMOUNT_SUM_CUBE);
useEffect(() => {
if (loadingFraudDataCube) { return; }
setFraudChartDataCube(tablePivotCube(fraudDataCube));
}, [ fraudDataCube ]);
const GET_FRAUD_AMOUNT_SUM_APOLLO = gql`
query ApolloQuery {
fraudsByAmountSumWithStep(
isFraud: ${isFraudSelection},
stepStart: ${selectedStep.start},
stepEnd: ${selectedStep.end}
) {
step
type
amountsum
}
}
`;
const {
loading: loadingFraudDataApollo,
error: errorFraudDataApollo,
data: fraudDataApollo,
} = useQuery(GET_FRAUD_AMOUNT_SUM_APOLLO);
useEffect(() => {
if (loadingFraudDataApollo) { return; }
setFraudChartDataApollo(tablePivotApollo(fraudDataApollo.fraudsByAmountSumWithStep));
}, [ fraudDataApollo ]);
return <>
<div style={{display: 'flex', justifyContent: 'center'}}>
<select
className={classes.select}
value={stepSelection}
onChange={e => setStepSelection(parseInt(e.target.value))}
>
<option value="" disabled>Select transaction step in time...</option>
{availableStepRanges.map(stepRange => (
<option key={stepRange.id} value={stepRange.id}>
Transactions from {stepRange.start} to {stepRange.end}
</option>
))}
</select>
<select
className={classes.select}
value={isFraudSelection}
onChange={e => setIsFraudSelection(parseInt(e.target.value))}
>
<option value="" disabled>Select if it's a fraudulent transaction...</option>
<option key={0} value={0}>
Non-fraudulent transactions
</option>
<option key={1} value={1}>
Fraudulent transactions
</option>
</select>
<div className={`${classes.buttonwrp}`}>
<button className={`Button Button--size-s Button--pink`} onClick={shuffleAndRun}>
Shuffle and Run!
</button>
</div>
</div>
<table style={{ width: '100%' }}>
<tbody>
<tr>
<td style={{ width: '50%' }}>
<div style={{ height: '375px', margin: '20px 0' }}>
<h3 style={{display: 'flex', justifyContent: 'center'}}>Cube {timestamps.CubeQuery ? `(${(timestamps.CubeQuery / 1000).toFixed(2)}s)` : ``}</h3>
<DisplayFraudAmountSum
loading={loadingFraudDataCube}
error={errorFraudDataCube}
chartData={fraudChartDataCube}
/>
</div>
</td>
<td style={{ width: '50%' }}>
<div style={{ height: '375px', margin: '20px 0' }}>
<h3 style={{display: 'flex', justifyContent: 'center'}}>PostgreSQL {timestamps.ApolloQuery ? `(${(timestamps.ApolloQuery / 1000).toFixed(2)}s)` : `(...)`}</h3>
<DisplayFraudAmountSum
loading={loadingFraudDataApollo}
error={errorFraudDataApollo}
chartData={fraudChartDataApollo}
/>
</div>
</td>
</tr>
</tbody>
</table>
</>
}

Let me explain the main points of the code above.

  • We use @apollo/client and wrap the React <App /> in <ApolloProvider>...<ApolloProvider/>.
    • This includes using httpLink and authLink to load the Apollo GraphOS federated GraphQL API endpoint and Cube’s secret token.
  • A typical API interaction flow in a React app with React hooks looks like this:
    • use useState to create a state variable (e.g., fraudChartDataCube);
    • compose a GraphQL query (e.g., GET_FRAUD_AMOUNT_SUM_CUBE);
    • call useQuery to fetch the result set (e.g., fraudDataCube);
    • use useEffect to await for the data and to transform it into fraudChartDataCube to be loaded into LineChart;
    • assign the data to the state variable (e.g., with setFraudChartDataCube).
  • We configure the GET_FRAUD_AMOUNT_SUM_CUBE GraphQL query to load parameters dynamically from the two dropdown selectors.
  • Lastly, the data is rendered by using DisplayFraudAmountSum with LineChart.

That's it! Now you know how to build the React app. Check out the live demo once again if you need some inspiration.

Summary

In the modern data stack, performance plays a big role. Cube's API layer makes it possible to model, cache, and optimize your data-intensive queries. Moreover, Cube's GraphQL API makes it effortless to integrate with your existing GraphQL architecture.

By using GraphQL federation with Apollo GraphOS, you can keep using your Apollo GraphQL server even when faced with running time-consuming analytical queries!

You can sign up for Cube Cloud for free and try it for yourself. To learn more about how Cube can help you to build your project, head over to the official documentation page.

If you have questions or feedback, we would love to hear what you have to say! Come join our Slack community. Click here to join!

That’s all for today. Feel free to leave Cube a ⭐ on GitHub if you liked this article!

Share this article