Every team in every industry uses content, whether they create, manage, analyze, or distribute it. Federated content platforms like Hygraph give your frontend team options for how to integrate your content sources with the tech stack you're already using (and that your non-technical team members are already comfortable with). Companies have leaned on Hygraph as a headless CMS, as a media products and platforms data source, as a next-generation knowledge management tool, and even as an inventory and catalog management platform.
But for now, let’s explore a more niche use case—displaying the financial data of publicly traded companies via a simple web app.
For the next few minutes, you will learn how to display a simple line chart containing a company's historical Earnings Per Share (EPS). We’ll use the Alpha Vantage API to get financial data, hook it up as a Hygraph Remote Source, and run queries against it with GraphQL. This tutorial uses Remix to build the web app, Tailwind CSS, and Tremor for styling and professional out-of-the-box charting components.
Here’s what the final app should look like:
Editor's Note
#Register for an Alpha Vantage API Key
Let’s start with a free Alpha Vantage account. Save the API key you receive after registering—you’ll need it later.
You’ll be working with the Earnings API. It provides earnings data for whichever publicly traded company you want. You can immediately test it with language-specific code from its documentation.
Click the example URL to get a JSON response similar to this:
{"symbol": "IBM","annualEarnings": [{"fiscalDateEnding": "2022-12-31","reportedEPS": "9.12"},// ...{"fiscalDateEnding": "1997-12-31","reportedEPS": "3.09"},{"fiscalDateEnding": "1996-12-31","reportedEPS": "2.77"}],"quarterlyEarnings": [{"fiscalDateEnding": "2022-12-31","reportedDate": "2023-01-25","reportedEPS": "3.6","estimatedEPS": "3.6","surprise": "0","surprisePercentage": "0"},// ...{"fiscalDateEnding": "1996-06-30","reportedDate": "1996-07-25","reportedEPS": "0.63","estimatedEPS": "0.61","surprise": "0.02","surprisePercentage": "3.2787"},{"fiscalDateEnding": "1996-03-31","reportedDate": "1996-04-17","reportedEPS": "0.55","estimatedEPS": "0.6","surprise": "-0.05","surprisePercentage": "-8.3333"}]}
As you can see, it provides quarterly and yearly earnings per share and a few other details. The final web app built from this tutorial will chart annual earnings per share using this data.
#Generate a GraphQL SDL
When adding the Earnings API as a Remote Source in Hygraph, you can specify the shape of the data from the remote API. This lets you use the REST API like a native GraphQL API. Use the Convert JSON to GraphQL Schema Online website to generate the SDL from the JSON response.
Save the Output SDL somewhere—you’ll need it in the next step.
#Set up Hygraph
This tutorial uses Hygraph as an intermediary, letting you interact with the Alpha Vantage API via a GraphQL interface. Create a new Hygraph account, and you’re ready to create a new project.
Click Add project.
Name it whatever you want—this tutorial uses the name Stockmarket API—and selects the region closest to you.
Add a Remote Source
Under Remote Sources in the sidebar of your new project, click + Add.
Name it whatever you want—this tutorial uses Alpha Vantage API.
Scroll down a bit to specify a base URL. Set https://www.alphavantage.co
as the base URL, then click + Add custom type definition. This is where you add the GraphQL SDL you generated in the last section.
Click + Add custom type definition three times to add three different definitions. Note that you can refer to one type from another, similar to how the Root
type refers to AnnualEarning
and QuarterlyEarning
in the following image.
Once you’ve defined the custom types, click Add to add the new Remote Source successfully.
Creating a custom model
To create a custom model that uses this Remote Source, click + Add` in the Models section.
Name it whatever you want—this tutorial uses Earning.
Scroll to the bottom of the available fields section to add a REST field to the model.
Name it Alpha Vantage
and change the Return type to Root
.
Scroll until you reach the option to specify an input argument and the Remote Source path. You can either refer to another field in the model and use that as part of the input path, or specify an input just for this field.
Let's specify a string input for this field that lets us pass in a symbol to query for.
Click Add, and Hygraph should add this new Alpha Vantage field to your model.
Adding the content entry
However, Hygraph needs data stored for the model before it can return any data from a remote source.
The Alpha Vantage field you created is read-only—you can’t run any mutations against it. So you need to create a new field where you can save data. Add a new single-line text field to the model and name it Symbol.
Go to the Content section in the sidebar and add a new entry from the UI.
Add IBM
as the Symbol value and click Save & publish.
Click API playground in the sidebar and try querying your custom model. If everything works, you should also see the Alpha Vantage API response as part of the output.
Creating a permanent token
Hygraph's Content API rejects unauthenticated requests by default. That means you have one last thing to do before moving on to the web app development part of the tutorial: create a new permanent token for making requests against your model.
Go to Project settings > API Access > + Add tokento create a new permanent token.
Name it whatever you want—this tutorial uses the name Web App token.
On the permissions page, go to Content permissions and click + Add permission.
Give your token read permissions.
If you return to the API Access page, you’ll see the Content API endpoint. Save the token you created in the last step and this API endpoint. You need both in the next section when you create the web app.
With everything ready on the Hygraph end, let's start building the web app.
#Creating the web app
In this half of the tutorial, let’s make a Remix application that utilizes Tailwind CSS and Tremor to display a friendly UI. We’ll use graphql-request to request Hygraph's Content API.
Requirements
To get a basic Remix app up and running, you need to run the following:
$ npx create-remix@latest
Follow the on-screen instructions and name your app whatever you like. The app in this tutorial is called hygraph-app
. Simple.
Need to install the following packages:create-remix@1.14.0Ok to proceed? (y) y? Where would you like to create your app? hygraph-app? What type of app do you want to create? Just the basics? Where do you want to deploy? Choose Remix App Server if you're unsure; it's easy to change deployment targets. Remix App Server? TypeScript or JavaScript? TypeScript? Do you want me to run `npm install`? Yesnpm WARN deprecated @npmcli/move-file@1.1.2: This functionality has been moved to @npmcli/fsnpm WARN deprecated rollup-plugin-inject@3.0.2: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec insteadadded 938 packages, and audited 939 packages in 1m235 packages are looking for fundingrun `npm fund` for detailsfound 0 vulnerabilities💿 That's it! `cd` into "/hygraph-app" and check the README for development and deploy instructions!
While you’re at it, go ahead and install graphql-request
, graphql
, Tailwind CSS
, and tremor
as dependencies:
$ npm install graphql-request graphql @tremor/react$ npm install -D tailwindcss
Setting up Tailwind CSS
Remix has experimental built-in support for Tailwind CSS, so you don't need to run the Tailwind CSS compiler separately. However, this support is experimental and hidden behind a feature flag. Enable it by editing the remix.config.js
file and setting the unstable_tailwind
option to true
.
/** @type {import('@remix-run/dev').AppConfig} */module.exports = {ignoredRouteFiles: ["**/.*"],future: {unstable_tailwind: true,},};
Next, run Tailwind CSS's init
command to create a Tailwind CSS config file:
$ npx tailwindcss init
This should result in a new tailwind.config.js
file in your project directory. Update it to look like this:
/** @type {import('tailwindcss').Config} */module.exports = {content: ["./app/**/*.{ts,tsx,jsx,js}"],theme: {extend: {},},plugins: [],}
The modified value for the content
key ensures that Tailwind CSS parses and compiles Tailwind CSS-specific classes in your code files.
Next, create a main CSS file and import all your Tailwind CSS layers. Create a new styles
directory in the app
folder and a new tailwind.css
file. Populate this file with the following imports:
@tailwind base;@tailwind components;@tailwind utilities;
Link this stylesheet in your Remix app by editing the root.tsx
file in the app
folder and adding the following code:
import styles from "./styles/tailwind.css";export const links: LinksFunction = () => [{ rel: "stylesheet", href: styles },];
You’re all set up to use Tailwind CSS in your project. Whenever you run npm run dev
or npm run build
, Remix automatically runs the Tailwind CSS compiler to ensure all appropriate styles are generated.
Setting up Tremor
Tremor setup is a bit easier than Tailwind CSS. You have to ensure you import the default styles that come with it.
Edit the index.tsx
file in the routes
directory and add the following code to the file:
import styles from "@tremor/react/dist/esm/tremor.css";export function links() {return [{ rel: "stylesheet", href: styles }];}
Loading data from Hygraph
Remix makes use of loader functions for data fetching. These are exported functions in your route files named loader
. The code within these functions only runs on the server side when a request is made to the route file. This code is never bundled in the client-side JS files, which means you can safely use any environment variables in it.
To get started, save a few environment variables and use them in your Remix app by creating a .env
file in the root of your project folder. Remix automatically loads these environment variables and provides access to them in the loader function.
Add the following two environment variables in the .env
file:
HYGRAPH_URL=__YOUR_HYGRAPH_URL__API_TOKEN=__YOUR_API_TOKEN
Editor's Note
Edit the app/routes/index.tsx
file and add the following code:
import { json, LoaderArgs, LoaderFunction } from "@remix-run/node";import { gql, GraphQLClient } from "graphql-request";export const loader: LoaderFunction = async ({ request }: LoaderArgs) => {const url = new URL(request.url);let symbol = url.searchParams.get("symbol");if (!symbol) {symbol = "IBM";}const hygraph = new GraphQLClient(process.env.HYGRAPH_URL || "");hygraph.setHeader("Authorization", `Bearer ${process.env.API_TOKEN}`);const dataQuery = gql`query financeAPI($symbol: String!) {earnings {alphaVantage(symbol: $symbol) {annualEarnings {reportedEPSfiscalDateEnding}}}}`const data: any = await hygraph.request(dataQuery, { symbol: symbol });return json({ data: data['earnings'][0]['alphaVantage'], symbol: symbol })}
The code is fairly straightforward but does have a few interesting bits.
The url.searchParams
object returns any values that might be passed to the route as part of the key/value pair in the GET request. For instance, if you send a request to hygraph.com/?symbol=ibm
, it provides you access to symbol=ibm
. You’ll use this to hook a form and update the chart when the user queries for a new symbol.
Additionally, the code uses the graphql-request
library to simplify GraphQL queries. It allows you to set up an authorization header once and then utilize it for all future queries. This tutorial is a simple project with only one route, so you can define everything in this loader function. But if you use multiple routes, you can move the queries to a separate file and import them wherever required.
The function terminates by returning the JSON data from Hygraph, ready to be used in the exported Component—more on this in just a second.
Displaying the line chart
Edit the Index
function to make use of the data from the loader function:
import { Card, Title, LineChart } from "@tremor/react";import { Form, useLoaderData } from "@remix-run/react";export default function Index() {const { data, symbol } = useLoaderData();const company = symbol;const newchartdata = data['annualEarnings'].map(({ reportedEPS, fiscalDateEnding }) => {return {'reported EPS': parseFloat(reportedEPS),fiscalDateEnding: fiscalDateEnding}}).reverse()const earliestDate = newchartdata[0].fiscalDateEnding.split("-")[0]const latestDate = newchartdata[newchartdata.length - 1].fiscalDateEnding.split("-")[0]const dataFormatter = (number: number) =>`\$${Intl.NumberFormat("us").format(number).toString()}`;return (<></>)}
The code is doing a few things here:
- It’s loading the JSON returned by the loader function.
- It creates a new object using that in reverse order so that the chart starts with the earliest date on the left.
- And it’s parsing the EPS values to floats so that they display correctly on the chart.
Then, it extracts the earliest and the latest date from the data just to make the UI a bit nicer by mentioning these dates in the title. It does this by taking the first and last fiscalDateEnding
values from the data and extracting the year portion from that.
A dataFormatter
function is also defined. This formats the y-axis values and puts a $
sign at the front.
Here’s the updated return
statement that displays the whole UI:
return (<div className="mx-auto w-3/6"><Form method="get" className="flex"><label htmlFor="symbol" className="sr-only">Search</label><div className="relative w-full"><div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" className="w-5 h-5"><path fill-rule="evenodd" d="M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z" clip-rule="evenodd"></path></svg></div><input name="symbol" type="text" className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5" placeholder="Input company symbol..." required /></div><button type="submit" className="inline-flex items-center py-2.5 px-3 ml-2 text-sm font-medium text-white bg-blue-700 rounded-lg border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300">Search</button></Form><Card marginTop="mt-3"><Title>Annual Earnings of {company} ({earliestDate} to {latestDate})</Title><LineChartdata={newchartdata}dataKey="fiscalDateEnding"categories={["reported EPS"]}colors={["blue"]}valueFormatter={dataFormatter}marginTop="mt-10"yAxisWidth="w-10"/></Card></div>);
The Form
component is a Remix built-in that allows you to implement loading states in the UI. It’s based on the HTML form
element.
Other than that, the code makes use of the Card, Title, and LineChart components from Tremor. You can read the official docs to learn more about them.
Optionally, you can make your UI tidier by centering the chart and input vertically on the page. Edit the app/root.tsx
file and modify the body
tag to resemble this:
<body className="flex min-h-screen place-items-center">
If you’ve done everything correctly, you can open the browser, point it to the Remix server address, and observe your application in all its glory:
Try using GME
as a symbol, and your graph should update accordingly.
Editor's Note
#Conclusion
In this fairly long but hopefully interesting tutorial, you learned how to use a finance API as a Remote Source with Hygraph and how to beautifully display the data using Remix, Tremor, and Tailwind CSS.
Using your ideas from this tutorial, you can construct your future projects using Hygraph, as the powerful platform has a wide range of functionalities that help developers build anything, including publishing platforms like BioCentury that fetch live financial data.
Ask anything in our official Slack workspace if you have questions, and our team would be happy to help you.