🐕
GraphQL APIの取得データを成形しCSV出力
はじめに
- 以下記事において、Suspenseコンポーネントを利用し、SWRライブラリ経由でデータ取得、その取得結果を画面コンポーネントへ表示させた。
- 今回は取得したデータの他システム連携を考慮して、ファイル出力(.csv)を実施する。
- Suspenseタグ内部のコンポーネントのローディングが完了していない場合、propsのfallbackの内容が表示され、取得後にファイルダウンロードリンクを表示させる。データ取得が完了しない限りはリンクが表示されることはない。
- SWRライブラリ経由でGraphQL APIよりデータ取得 -> オブジェクト多階層構造となっているAPIレスポンスデータ受領 -> ファイルに書き出すデータのみ抽出し、CSVデータへ成形 -> CSVファイル出力
GitHub APIアクセストークン取得
-
https://github.com/settings/tokens よりアクセストークンを取得する。
-
repoの情報を取得できるように権限設定。
-
取得したアクセストークンを利用し、API経由で情報取得できることを確認する。
- ghp_token 部分に取得したトークンを入力する。
- ヘッダーには「Authorization: bearer」部分まで含めて情報が必要。"data"部のデータ取得を確認。
curl --insecure -H "Authorization: bearer ghp_token" -X POST -d " \
{ \
\"query\": \"query { viewer { login }}\" \
} \
" https://api.github.com/graphql
{"data":{"viewer":{"login":"hiroharu8864"}}}
SWRライブラリによるGraphQL API呼び出し ./src/hooks/useGetRepos.ts(カスタムフック)
- 指定ユーザのリポジトリ名と作成日時を取得するクエリを発行する。
- レスポンスの型は定義する。(GitHubRepos型)
import { GraphQLClient } from "graphql-request";
import useSWR from "swr";
import { GitHubRepos } from "../type/GitHubRepos";
const getLoginUserReposQuery = `
query loginUserRepository($loginUser: String!, $firstFetchNums: Int!) {
repositoryOwner(login: $loginUser) {
repositories(privacy: PUBLIC, first: $firstFetchNums) {
edges{
node {
createdAt
name
}
}
}
}
}
`;
const loginUserReposName = "hiroharu8864";
const first = 100;
export const useGetRepos = () => {
const access_token = "ghp_token";
const client = new GraphQLClient("https://api.github.com/graphql", {
headers: {
Authorization: `bearer ${access_token}`,
"Content-Type": "application/json"
},
method: "POST"
});
const { data, error } = useSWR<GitHubRepos>(
[getLoginUserReposQuery, loginUserReposName, first],
(query, loginUser, firstFetchNums) =>
client.request(query, {
loginUser: loginUserReposName,
firstFetchNums: first
}),
{
suspense: true
}
);
return { data, error };
};
APIレスポンスの型指定 ./src/types/GitHubRepos.ts
- GraphQL APIの返却される型は、オブジェクトの階層構造となっている。
- 戻り値の型は自動生成するのがベターだが、ここは敢えて手動で定義する。
- edges配列の中にNodeオブジェクトが格納されているのではなく、edges配列 -> 番号が付与されたオブジェクト -> Nodeオブジェクトの階層構造となっている。その構造に合わせて型定義する必要がある。
export type GitHubRepos = {
repositoryOwner: RepositoryOwner;
};
type RepositoryOwner = {
repositories: RepositoryConnection;
};
type RepositoryConnection = {
edges: Array<NumObject>;
};
type NumObject = {
node: Node;
};
type Node = {
createdAt: string;
name: string;
};
出力表示するコンポーネント ./src/components/pages/GraphQLFetchCSV.tsx
- CSV作成・出力には「react-csv」ライブラリを使用。
- ページ出力後、GraphQL APIのデータ取得が完了するまで、Suspenseコンポーネントによりダウンロードリンクは表示されない。
- GraphQL APIのデータ取得 -> JSON展開 -> JSON.parse で必要なデータのみ抽出し、CSVライブラリへ渡す。
- データ抽出対象のオブジェクトを指定してJSON展開する。
import { FC, memo, Suspense, useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { CSVLink } from "react-csv";
import moment from "moment";
import { useGetRepos } from "../../hooks/useGetRepos";
import { FormContainer } from "../molecules/FormContainer";
import { MainButton } from "../atoms/MainButton";
const ResultComponent = () => {
const { data, error } = useGetRepos();
/**
* 空白2文字で整形して出力
* 取得対象のオブジェクト階層を指定して、JSON展開
*/
const dataReposJson = JSON.stringify(
data?.repositoryOwner.repositories.edges,
null,
2
);
console.log(dataReposJson);
const parseData = JSON.parse(dataReposJson);
/**
* CSV用のデータ作成
*/
const headers = [
{ label: "Repository Name", key: "repositoryname" },
{ label: "Repository Create Date", key: "repositorycreatedate" }
];
const csvdata = parseData.map((repository) => ({
repositoryname: `${repository.node.name}`,
repositorycreatedate: `${repository.node.createdAt}`
}));
const now = moment().format("YYYYMMDD_HHmmss");
return (
<>
<CSVLink data={csvdata} headers={headers} filename={`${now}.csv`}>
CSV Files Download
</CSVLink>
</>
);
};
export const GraphQLFetchCSV: FC = memo(() => {
const navigate = useNavigate();
const onClickHome = useCallback(() => {
navigate("/");
}, [navigate]);
return (
<>
<FormContainer>
<h3>Result CSV Files</h3>
<p>APIデータ取得後、リンク出力</p>
<br />
<Suspense fallback={<p>...データ取得中</p>}>
<ResultComponent />
</Suspense>
<MainButton onClick={onClickHome}>Go To Home</MainButton>
</FormContainer>
</>
);
});
- APIデータ取得後、ダウンロードリンクが出力されて、CSVファイル形式で取得できる。
今後の課題
- GitHub APIの仕様によりデータは100件までしか取得できない。まとめてデータ取得してファイル出力したいので、101件目以降のデータは別途どこかにストックしておく必要がある。その方式については継続検討する。もしくはクエリで一括取得可能かもしれない。
"message": "Requesting 101 records on the `repositories` connection exceeds the `first` limit of 100 records."
Repository
Discussion