Here's a quick summary of everything we released in Q1 2024.

Working with Remote Sources to build a stock market website

Let's learn 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.
Muhammad Yasoob Ullah Khalid

Muhammad Yasoob Ullah Khalid

May 22, 2023
How to build a stock market website with Hygraph

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:

Graph of annual earnings of IBM

Editor's Note

You can take a look at the final code on GitHub.

#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.

Language options in the Earnings API

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.

Screenshot 2023-05-25 at 16.52.34.png

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.

Creating a new project in Hygraph

Name it whatever you want—this tutorial uses the name Stockmarket API—and selects the region closest to you.

Project name and region

Add a Remote Source

Under Remote Sources in the sidebar of your new project, click + Add.

Adding a Remote Source

Name it whatever you want—this tutorial uses Alpha Vantage API.

Name, prefix, description of Remote Source

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.

Adding custom type definition

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.

The type definitions for AnnualEarning, QuarterlyEarning, and Root

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.

Adding models

Name it whatever you want—this tutorial uses Earning.

Display name, API ID, Plural API ID, and Description for new model

Scroll to the bottom of the available fields section to add a REST field to the model.

Adding a REST field

Name it Alpha Vantage and change the Return type to Root.

Settings for the Alpha Vantage REST field

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.

Input arguments

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.

The Alpha Vantage and Symbol fields

Go to the Content section in the sidebar and add a new entry from the UI.

Adding content

Add IBM as the Symbol value and click Save & publish.

Publishing the content entry

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.

Hygraph’s API Playground

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.

Add your first permanent auth token

Name it whatever you want—this tutorial uses the name Web App token.

Add and configure permissions for your token

On the permissions page, go to Content permissions and click + Add permission.

Content API permissions

Give your token read permissions.

Add 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.

API Access endpoints

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.0
Ok 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`? Yes
npm WARN deprecated @npmcli/move-file@1.1.2: This functionality has been moved to @npmcli/fs
npm 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 instead
added 938 packages, and audited 939 packages in 1m
235 packages are looking for funding
run `npm fund` for details
found 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

You can get both of these values from the last section where you created a permanent token in the Hygraph dashboard.

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 {
reportedEPS
fiscalDateEnding
}
}
}
}
`
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>
<LineChart
data={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:

The final application

Try using GME as a symbol, and your graph should update accordingly.

Editor's Note

Remember, you can take a look at the final code in this repo.

#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.

Blog Author

Muhammad Yasoob Ullah Khalid

Muhammad Yasoob Ullah Khalid

Yasoob is an author, blogger, and tech speaker. His books include "Intermediate Python" and "Practical Python Projects," and he's currently working on Azure at Microsoft.

Share with others

Sign up for our newsletter!

Be the first to know about releases and industry news and insights.