Using Measure Data from a Specific Report (Grouped by Population)
This guide explains the process of searching for reports and their measures, fetching desired data and rendering the chart in a React component.
Searching for Reports
First, we need find the Report we'd like to use. Let's use the GetReports
GraphQL query to list all Reports grouped by Report Type.
query GetReports {
reportTypes_A {
reportTypeId
name
reports(order: { releaseDate: DESC }) {
reportId
name
releaseDate
}
}
}
Using the Playground, we can run the query and get the response:
{
"data": {
"reportTypes_A": [
{
"reportTypeId": 1,
"name": "Annual",
"reports": [
{
"reportId": 276,
"name": "2023 Annual",
"releaseDate": 2023
}
]
},
{
"reportTypeId": 2,
"name": "Senior",
"reports": [
{
"reportId": 277,
"name": "2024 Senior",
"releaseDate": 2024
}
]
},
{
"reportTypeId": 3,
"name": "HWC",
"reports": [
{
"reportId": 275,
"name": "2023 HWC",
"releaseDate": 2023
}
]
}
]
}
}
The data.reportTypes_A
and data.reportTypes_A.reports
array has been truncated for brevity. The actual response contains more data points.
Getting Report Measures
Once we've identified the Report of interest, we retrieve all Report Measure data for that Report using the GetReportMeasures
GraphQL query.
Measure data is nested within reportMeasures
with a new field called endDate
. This is the date that measure data will be filtered by to only use data from the specified Report.
query GetReportMeasures {
report_A(editionId: 276) {
reportId
name
releaseDate
reportMeasures {
endDate
measure {
measureId
name
description
unitType
format
}
}
}
}
Using the Playground, we can run the query and get the response:
{
"data": {
"report_A": {
"reportId": 276,
"name": "2023 Annual",
"releaseDate": 2023,
"reportMeasures": [
{
"endDate": "2022-01-01T00:00:00.000Z",
"measure": {
"measureId": "147",
"name": "Air Pollution",
"description": "Average exposure of the general public to particulate matter of 2.5 microns or less, measured in micrograms per cubic meter",
"unitType": "Micrograms of fine particles per cubic meter",
"format": "Numeric"
}
},
{
"endDate": "2022-01-01T00:00:00.000Z",
"measure": {
"measureId": "171",
"name": "Smoking",
"description": "Percentage of adults who reported smoking at least 100 cigarettes in their lifetime and currently smoke daily or some days",
"unitType": "Percentage of adults",
"format": "Percent"
}
},
{
"endDate": "2022-01-01T00:00:00.000Z",
"measure": {
"measureId": "173",
"name": "Obesity",
"description": "Percentage of adults who have a body mass index of 30.0 or higher based on reported height and weight ",
"unitType": "Percentage of adults",
"format": "Percent"
}
}
]
}
}
}
The data.report_A.reportMeasures
array has been truncated for brevity. The actual response contains more data points.
Getting Measure Population Data
Once we have identified the measure of interest, we retrieve the detailed data for that measure using the GetMeasurePopulationData
GraphQL query.
Instead of fetching all data points, we're getting grouped data points for each population at the national level (state = "ALL").
Note that the datum endDate
is included to compare against the Report Measure endDate
from the previous step.
query GetMeasurePopulationData {
measure_A(metricId: 171) {
measureId
name
description
unitType
format
source {
name
}
subpopulations {
name
population {
name
populationCategory {
name
}
}
data(where: { state: { in: ["ALL"] } }) {
endDate
dateLabel
value
}
}
}
}
Using the Playground, we can run the query and get the response:
{
"data": {
"measure_A": {
"measureId": "171",
"name": "Smoking",
"description": "Percentage of adults who reported smoking at least 100 cigarettes in their lifetime and currently smoke daily or some days",
"unitType": "Percentage of adults",
"format": "Percent",
"source": {
"name": "CDC, Behavioral Risk Factor Surveillance System"
},
"subpopulations": [
{
"name": "Smoking - College Grad",
"population": {
"name": "College Grad",
"populationCategory": {
"name": "Education"
}
},
"data": [
{
"endDate": "2012-01-01T00:00:00.000Z",
"dateLabel": "2012",
"value": 7.8
},
{
"endDate": "2013-01-01T00:00:00.000Z",
"dateLabel": "2013",
"value": 7.6
}
]
}
]
}
}
}
The data.measure_A.data
array has been truncated for brevity. The actual response contains more data points.
Setting up GraphQL Data Fetching
We create a utility function to fetch data from the GraphQL API. This function sends a GraphQL query to the API and returns the response.
export type GraphQLResponse<T> = {
data?: T;
errors?: { message: string }[];
};
export const fetchGraphqlClient = async <T>({
method = "POST",
body,
}: RequestInit): Promise<GraphQLResponse<T>> => {
const headers: HeadersInit = {
"Content-Type": "application/json",
"X-Api-Key": process.env.AHR_API_KEY
};
const response = await fetch("https://api.americashealthrankings.org/graphql", {
method,
headers,
body,
}).then((data) => data.json());
return response as GraphQLResponse<T>;
};
Fetching Chart Data
We then create a utility function fetchChartData to fetch the measure data using a GraphQL client. This function sends a GraphQL query to get the filtered measure data and returns the response.
import { fetchGraphqlClient } from "util/graphql";
export const fetchChartData = async () => {
return await fetchGraphqlClient<any>({
body: JSON.stringify({
query: `query GetMeasurePopulationData {
measure_A(metricId: 171) {
measureId
name
description
unitType
format
source {
name
}
subpopulations {
name
population {
name
populationCategory {
name
}
}
data(where: { state: { in: ["ALL"]}}) {
endDate
dateLabel
value
}
}
}
}`,
}),
});
};
Rendering the Chart
Finally, we use the fetched data to render a list of all available Population Categories and a pie chart in our React component. The DynamicPieChart
component uses the useState
and useEffect
hooks to manage and fetch the data. We use the recharts
library to create a pie chart, displaying the Population data based on the selected Population Category.
npm install recharts
import React, { useEffect, useState } from "react";
import { PieChart, Pie, ResponsiveContainer, Cell, Text } from "recharts";
import { fetchChartData } from "./fetch";
type ChartData = {
name: string;
description: string;
source: string;
year: string;
unitType: string;
format: string;
data: {
[key: string]: {
population: string;
percent: number;
}[];
};
};
const RADIAN = Math.PI / 180;
const colors = ["#1490ff", "#43A6FF", "#72BCFF", "#A1D3FF"];
const reportMeasureEndDate = "2022-01-01T00:00:00.000Z";
const DynamicPieChart = () => {
const [populationCategory, setPopulationCategory] =
useState<string>("Education");
const [chartData, setChartData] = useState<ChartData | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const getChartData = async () => {
try {
const measure = await fetchChartData().then(
(res) => res.data?.measure_A,
);
const data = {};
let dateLabel = "";
measure.subpopulations.forEach((subpopulation) => {
const populationCategory =
subpopulation?.population?.populationCategory?.name;
if (!data[populationCategory]) {
data[populationCategory] = [];
}
const reportMeasureData = subpopulation?.data.find((datum) => {
return datum.endDate === reportMeasureEndDate;
});
if (!!reportMeasureData) {
data[populationCategory].push({
population: subpopulation?.population?.name,
percent: reportMeasureData?.value,
});
if (!dateLabel) {
dateLabel = reportMeasureData?.dateLabel;
}
}
});
const formattedData = {
name: measure?.name,
description: measure?.description,
source: measure?.source.name,
year: dateLabel,
unitType: measure?.unitType,
format: measure?.format,
data,
};
setChartData(formattedData);
} catch (error) {
console.error(error);
setError("An error occurred while fetching the chart data.");
} finally {
setLoading(false);
}
};
getChartData();
}, []);
const renderPopulationCategories = () => {
return Object.keys(chartData?.data || {}).map((pg, i) => {
return (
<p
key={`population-category-${i}`}
onClick={() => {
setPopulationCategory(pg);
}}
className={`${pg === populationCategory ? "nx-border-primary-500" : "nx-cursor-pointer"} nx-border nx-rounded-lg nx-p-2`}
style={{
borderWidth: pg === populationCategory ? "2px" : "1px",
}}
>
{pg}
</p>
);
});
};
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
return (
<figure>
<figcaption className="nx-mt-2 nx-mb-2">
<hgroup>
<h3 className="nx-text-2xl nx-font-bold">{chartData?.name}</h3>
<h4 className="nx-text-md">{chartData?.description}</h4>
</hgroup>
<cite>
America’s Health Rankings analysis of {chartData?.source}, United
Health Foundation, AmericasHealthRankings.org, accessed{" "}
{chartData?.year}.
</cite>
</figcaption>
<div
className="nx-w-full nx-py-4"
style={{ display: "grid", gridTemplateColumns: "1fr 4fr", gap: "8px" }}
>
<div className="nx-col-span-1 nx-flex nx-flex-col nx-gap-1">
{renderPopulationCategories()}
</div>
<div className="nx-col-span-4">
<ResponsiveContainer>
<PieChart>
<Pie
label={renderCustomizedLabel}
labelLine={false}
nameKey={"population"}
dataKey="percent"
data={chartData?.data[populationCategory]}
fill="#000000"
>
{chartData?.data[populationCategory].map((_, index) => (
<Cell
key={`cell-${index}`}
fill={colors[index % colors.length]}
/>
))}
</Pie>
</PieChart>
</ResponsiveContainer>
</div>
</div>
</figure>
);
};
const renderCustomizedLabel = ({
cx,
cy,
midAngle,
innerRadius,
outerRadius,
percent,
name,
}) => {
const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
const x = cx + radius * Math.cos(-midAngle * RADIAN);
const y = cy + radius * Math.sin(-midAngle * RADIAN);
const textX = cx + (outerRadius + 20) * Math.cos(-midAngle * RADIAN);
const textY = cy + (outerRadius + 20) * Math.sin(-midAngle * RADIAN);
return (
<>
<Text
x={textX}
y={textY}
fill="black"
textAnchor={x > cx ? "start" : "end"}
dominantBaseline="central"
width={150}
>
{name}
</Text>
<Text
x={x}
y={y}
fill="white"
textAnchor="middle"
dominantBaseline="central"
fontSize={18}
>
{`${percent.toFixed(1)}%`}
</Text>
</>
);
};
export default DynamicPieChart;