タイトル : Next.js FastAPIと通信 その2 OpenApiGeneratorを使ってみる
更新日 : 2024-02-04
カテゴリ : プログラミング
画面
FastAPIの通信でフロントエンドのモデルファイル作成にOpenApiGeneratorを使ってみます。先に画面です。
FastAPIの方
@dataclass
class Field:
name: str
dtype: str
size: int = None
@dataclass
class Table:
name: str
fields: List[Field]
class TableInfos(BaseModel):
version: str
tables: List[Table]
@app.get("/tables", response_model=TableInfos)
async def get_tables():
tables = []
for t_index in range(1, 4):
t_name = f"TABLE{t_index}"
fields = [
Field(f"col1_t{t_index}", "int"), Field("col2", "text", 8), Field("col3", "float")
]
tables.append(
Table(name=t_name, fields=fields)
)
return TableInfos(version="0.9.1", tables=tables)
OpenApiGeneratorを使う準備
OpenAPI Generatorを使います。
CLI Installationを参考にして、jarファイルをダウンロードします。
Javaをインストールします。上記のページに、Java 11 runtime at a minimumとあるので、とりあえずJava11で。
sudo apt install openjdk-11-jre-headless
(sudo apt install openjdk-11-jdk-headless でも)
OpenApiGeneratorを使ってtypescriptのモデルファイルを作成
FastAPIが作成してくれる openapi.json をダウンロードする。デフォルトは /openapi.json なので、そこにアクセスしてダウンロード。(今回だとhttp://192.168.11.6:8080/openapi.json)
-g typescript-fetch で今回はタイプスクリプト-fetch用のファイルを作成、-i openapi.jsonで上記でダウンロードしたファイルを指定し、 -o /src/openapi_generated で出力先を指定します。
java -jar openapi-generator-cli.jar generate
-g typescript-fetch -i openapi.json -o front/src/openapi_generated
--additional-properties=modelPropertyNaming=camelCase,supportsES6=true,withInterfaces=true,typescriptThreePlus=true
以下のファイルが作成されました。
$ pwd
xxx/src/openapi_generated
$ ls -R
.:
apis index.ts models runtime.ts
./apis:
DefaultApi.ts index.ts
./models:
Field.ts ProjectInfo.ts ProjectInfos.ts Table.ts TableInfos.ts index.ts
$
上記の models/ に作成されたファイルをNext.jsで使っていきます。
Next.jsの方、Zustandのストアを書いて、その中でfetchする
import { TableInfos } from "@/openapi_generated"; で先に作成した TableInfos を使います。
import { create } from "zustand";
import { TableInfos } from "@/openapi_generated";
import { API_URL } from "@/store/settings";
type Tables = {
tableinfo?: TableInfos;
fetchTables: () => void;
};
export const useTableInfoStore = create<Tables>((set) => ({
tableinfo: undefined,
fetchTables: async () => {
try {
const response = await fetch(`${API_URL}/tables`);
const res_json = await response.json();
set({tableinfo: res_json})
} catch (error) {
console.error("Error fetching tables:", error);
}
},
}));
Next.jsの方、Zustandのストアを使う方
"use client"
import { Typography, Stack, Box } from "@mui/material";
import * as React from 'react';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import { useEffect } from "react";
import {useTableInfoStore} from "@/store/tableinfostore";
export default function Home() {
const {tableinfo, fetchTables} = useTableInfoStore()
useEffect(() => {
fetchTables();
}, []);
return (
<Box>
<Stack spacing={2} padding={2}>
<Typography>メニュー3の画面 version: {tableinfo?.version}</Typography>
{
tableinfo?.tables.map((tinfo) => {
return (
<Stack key={`s-${tinfo.name}`}>
<Typography key={tinfo.name}>テーブル名:{tinfo.name}</Typography>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 450 }} size="small" aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>カラム名</TableCell>
<TableCell align="center">型</TableCell>
<TableCell align="center">サイズ</TableCell>
</TableRow>
</TableHead>
<TableBody>
{tinfo.fields.map((field) => (
<TableRow
key={`${tinfo.name}-${field.name}`}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">{field.name}</TableCell>
<TableCell align="center">{field.dtype}</TableCell>
<TableCell align="center">{field.size}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Stack>
)
})
}
</Stack>
</Box>
);
}