前書き
ミツモアでエンジニアやってる もっさん(当然偽名)です!
アドベントカレンダーの役回りをもらい、もう、ほんっとうに書くことがなくて 他の方の記事をちら見してると、やっぱりAI関連が多いわけですね。
で、最近よく「AI使って〇〇みたいなの作ってよ(適当)」と言われて、それがバックエンドのロジックメインの時、フロントエンドを実装するのが大変億劫です。 少なくとも現時点の生成AIの文脈だと「枠組み(前提)があるかないか」で成果物が大きく変わる認識で、億劫な フロントエンドの枠組みを提供してくれるやつに焦点を当てて、簡単な使い方とデモを書き連ねることで誤魔化したいと思います…。
この強引な導入で紹介するのは react-admin。 名前の通り、Reactで管理画面をサクッと作れる子で、MUIベースのフロントエンドフレームワークです。
カスタマイズしなくてもそれっぽく動きますし、作りが丁寧なので「こう変えたいんだけどなー」が大体できる、今回の趣旨にもぴったりなやつです(適当)。
真面目な 前書き
こういう系って「見ながら・触りながら」追えるものが意外と少ないんですよね。特にチュートリアル寄りのやつ。 「ドキュメント見ろ甘えてんじゃねぇ」は100%の正論なんですが、丁寧なチュートリアル形式で一通り触れる状態だと、吸収できる量が増えるな〜と思ったり思わなかったりです。
というわけで、以降は基本的な使い方を羅列しつつ、実際に触れるデモページも用意しました。適宜、触りながら確認してみてください!
個人的に借りてるサーバーなので無茶しないでね
この記事でやること
「AI使って〇〇みたいなの作ってよ。え?動くやつは見れるようにして(適当)」って言われたときに、まず最低限これが揃ってると助かるよね、を作ります。
dataProviderとは何か- 最短CRUD(List / Create / Edit / Show)
- 保存後の遷移・通知を制御する
- リレーションの最低限
- many-to-one(投稿 → 著者)
- one-to-many(投稿 → コメント)
- one-to-one(投稿 → 詳細)
- 一覧を「画面内スクロール」にして運用しやすくする
ra-data-simple-restで足りない場合の customdataProvider例
ここでの「最短」は、“実装量が少ない”だけじゃなくて、“後から色々追加要求を受けても壊れにくい”も含みます。
0. まず dataProvider を押さえる
react-adminで最初に詰まりがちなのがここです。
- Listコンポーネントを書いた
- でも、データが…ない
- というか、どこから…取るの?
- axiosは?fetchは?どこに書くの?
結論:react-adminは、画面側で直接 fetch/axios を書かずに、dataProvider という“通信の窓口”を通します。
画面は「postsを一覧したい」「id=1を取得したい」みたいな“意図”だけ伝えて、HTTPの詳細はdataProviderが責任を持つ、という設計です(=責務分離)。
dataProvider の役割
- react-adminからの要求(例:
getList('posts', params))を受ける - API形式に合わせてHTTPリクエストを組み立てる(REST / GraphQL / RPC / SOAPでも)
- レスポンスを、react-adminが期待する形(
{ data, total }等)に変換して返す
これがあると何が嬉しいかというと、
- UIが汚れない(「このAPIのfilterはこう」みたいな癖を画面に漏らさない)
- API形式が変わっても直す場所が一箇所に寄る
- “バックエンドがまだ”でも、モック
dataProviderで先に画面が作れる
…という、地味に重要な運用メリットがあります。 生成AIがばーっと実装しても役割が明確なので、結果的に読みやすくなったりもします。
1. セットアップ
まずはサクッと動く状態を整えます。適当な作業ディレクトリで以下を実行してください。
npm install react-admin npm create react-admin@latest my-admin cd my-admin npm install npm run dev
ブラウザが開いてウェルカムページが出ていればOKです。
なおこの記事は Vite ベースの構成で話を進めますが、Next.js や Remix 等にも対応しています(詳細は公式ドキュメント参照)。
- Vite: https://marmelab.com/react-admin/Vite.html
- Next.js: https://marmelab.com/react-admin/NextJs.html
- Remix: https://marmelab.com/react-admin/Remix.html
2. ra-data-simple-rest で一覧を出す
RESTで素直なAPIなら、まずは既製のdataProviderを使うのが最速です。
ra-data-simple-rest は、フィルタやソート等を「GETクエリ」で渡すタイプのRESTに向いてます。
2.1 dataProvider
// src/dataProvider.ts import simpleRestProvider from "ra-data-simple-rest"; export const dataProvider = simpleRestProvider("<http://localhost:3000>");
2.2 API側のレスポンス形式
当然ですがreact-admin側が期待しているレスポンス形式があります。 割と普遍的な形なので、API側の実装も以下の形式に沿うように実装してあげるとdataProviderで形式を変更してあげる必要がなくなるので楽です。
// 例:posts の Record 型(最小) // ※ Identifier は string | number 相当(react-admin側の型) import type { Identifier, RaRecord } from "react-admin"; export type PostRecord = RaRecord & { title: string; body?: string; created_at: string; // 例: ISO文字列でもOK author_id?: Identifier; };
具体的なサンプルは以下の通り。
getList('posts', ...) の戻り値(一覧)
- data: レコード配列(各要素が id を持つ)
- total: 全件数(ページネーションのために必要)
// dataProvider.getList の戻り値イメージ { data: [ { id: 1, title: "Hello", body: "...", created_at: "2025-12-15T00:00:00.000Z", author_id: 10 }, { id: 2, title: "World", body: "...", created_at: "2025-12-14T00:00:00.000Z", author_id: 11 } ], total: 235 }
getOne('posts', { id }) の戻り値(詳細)
// dataProvider.getOne の戻り値イメージ { data: { id: 1, title: "Hello", body: "....", created_at: "2025-12-15T00:00:00.000Z", author_id: 10 } }
重要:id が無い/別名(post_id とか)になっている場合、UI側で頑張るのではなく dataProvider側で id にマッピングして返すのが基本です(=責務分離の恩恵が出るところ)。
2.3 Admin + Resource
// src/App.tsx import { Admin, Resource } from "react-admin"; import { dataProvider } from "./dataProvider"; import { PostList } from "./posts/PostList"; import { PostCreate } from "./posts/PostCreate"; import { PostEdit } from "./posts/PostEdit"; import { PostShow } from "./posts/PostShow"; export const App = () => ( <Admin dataProvider={dataProvider}> <Resource name="posts" list={PostList} create={PostCreate} edit={PostEdit} show={PostShow} /> </Admin> );
Resource name="posts" が「postsというリソースを扱う」宣言になり、以降は name="posts" に紐づくCRUD画面が組み上がっていきます。
3. CRUDを揃える(List / Create / Edit / Show)
3.1 List(一覧)
// src/posts/PostList.tsx import { List, DataTable, DateField } from "react-admin"; export const PostList = () => ( <List> <DataTable rowClick="edit" bulkActionButtons={false}> {/* sourceに記載する値は、レスポンスbodyの各keyを指してください */} <DataTable.Col source="id" /> <DataTable.Col source="title" /> <DataTable.Col source="created_at"> <DateField source="created_at" showTime /> </DataTable.Col> </DataTable> </List> );
ここまでで「一覧が出る」=管理画面っぽさが一気に出ます。
3.2 Create(新規作成)
// src/posts/PostCreate.tsx import { Create, SimpleForm, TextInput, required } from "react-admin"; export const PostCreate = () => ( <Create> <SimpleForm> <TextInput source="title" validate={[required()]} fullWidth /> <TextInput source="body" multiline fullWidth /> </SimpleForm> </Create> );
3.3 Edit(編集)
// src/posts/PostEdit.tsx import { Edit, SimpleForm, TextInput, required } from "react-admin"; export const PostEdit = () => ( <Edit> <SimpleForm> <TextInput source="id" disabled /> <TextInput source="title" validate={[required()]} fullWidth /> <TextInput source="body" multiline fullWidth /> </SimpleForm> </Edit> );
3.4 Show(詳細)
// src/posts/PostShow.tsx import { Show, SimpleShowLayout, TextField } from "react-admin"; export const PostShow = () => ( <Show> <SimpleShowLayout> <TextField source="id" /> <TextField source="title" /> <TextField source="body" /> </SimpleShowLayout> </Show> );
4. 保存後の遷移・通知
CRUDが揃うと、次に欲しくなるのが「作成・更新後の挙動の調整」です。 ユースケースはだいたいこの辺。
- 保存したあと、自分自身の画面(list/show/edit)ではなく別画面に遷移させたい
- 遷移タイミングを「保存成功後」にしたい(=レスポンスを受けてから動きたい)
- 連続編集したいので、次のリソースに遷移させたい
react-adminはこの辺も用意されてて、成功時の挙動をコンポーネント側で宣言的に制御できます。
// src/posts/PostEdit.tsx import { Edit, SimpleForm, TextInput, useNotify, useRedirect } from "react-admin"; export const PostEdit = () => { const notify = useNotify(); const redirect = useRedirect(); return ( <Edit mutationOptions={{ onSuccess: () => { notify("保存しました", { type: "info" }); redirect("list", "posts"); }, }} > <SimpleForm> <TextInput source="id" disabled /> <TextInput source="title" fullWidth /> <TextInput source="body" multiline fullWidth /> </SimpleForm> </Edit> ); };
notify()で「保存した」が即分かるredirect()で「保存後は一覧へ戻る」「ページに止まる」など自由に設計できる
「一覧へ戻す/編集画面に留まる/詳細へ飛ぶ」など、運用の都合で正解が変わるところなので、こういう“後から変えられる余白”は偉いです。
5. フィールド・入力の見た目と構造(「それっぽい」画面にする小技)
ここからは、完全におまけです。 でも“それっぽさ”の差は意外とここで出ます。
5.1 sx で崩れないレイアウトを作る
フォームが縦にダラっと並ぶと、情報量が増えたときに視認性が落ちます。
MUIベースなので、sx でレイアウトを整理しやすいです。
// src/posts/PostEdit.tsx import { Edit, SimpleForm, TextInput } from "react-admin"; import { Box } from "@mui/material"; export const PostEdit = () => ( <Edit> <SimpleForm> <Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 2 }}> <TextInput source="title" fullWidth /> <TextInput source="slug" fullWidth /> </Box> <TextInput source="body" multiline fullWidth sx={{ mt: 2 }} /> </SimpleForm> </Edit> );
5.2 独自Fieldを作る
「この列、文字列だけじゃなくてアイコン付けたい」「idとtitleをまとめたい」みたいなやつです。
// src/posts/fields/TitleWithId.tsx import { useRecordContext } from "react-admin"; type TitleWithIdProps = { separator?: string; }; type PostRecord = { id: string | number; title?: string; }; export const TitleWithId = (props: TitleWithIdProps) => { const record = useRecordContext<PostRecord>(); if (!record) return null; return ( <span> {record.id} {props.separator ?? ": "} {record.title ?? ""} </span> ); };
使う側はこう。
import { List, DataTable } from "react-admin"; import { TitleWithId } from "./fields/TitleWithId"; export const PostList = () => ( <List> <DataTable rowClick="edit" bulkActionButtons={false}> <DataTable.Col label="ID / Title"> <TitleWithId separator=" / " /> </DataTable.Col> </DataTable> </List> );
「列=コンポーネント」になってくると、表現の自由度が急に上がります。
5.3 独自Inputを作る(useInput に寄せる)
react-adminのフォームはreact-hook-formで制御されます。
独自Inputを作るなら useInput に寄せておくと、バリデーションや送信の仕組みに自然に乗れます。
例として「入力を大文字に整形するInput」を置きます。
// src/common/inputs/UppercaseTextInput.tsx import { TextField } from "@mui/material"; import { useInput } from "react-admin"; type UppercaseTextInputProps = { source: string; label?: string; fullWidth?: boolean; }; export const UppercaseTextInput = (props: UppercaseTextInputProps) => { const { field, fieldState, id, isRequired } = useInput({ source: props.source, }); return ( <TextField id={id} label={props.label} required={isRequired} fullWidth={props.fullWidth} value={typeof field.value === "string" ? field.value : ""} onChange={(e) => field.onChange(e.target.value.toUpperCase())} onBlur={field.onBlur} error={!!fieldState.error} helperText={fieldState.error?.message} /> ); };
使う側:
import { Edit, SimpleForm } from "react-admin"; import { UppercaseTextInput } from "../common/inputs/UppercaseTextInput"; export const PostEdit = () => ( <Edit> <SimpleForm> <UppercaseTextInput source="slug" label="Slug" fullWidth /> </SimpleForm> </Edit> );
6. リレーション:many-to-one / one-to-many / one-to-one(「関連する〇〇も出して」)
よくある要求ですね、作るのはめんどくさいぞー…と。
「投稿に著者名も出して」 「投稿のコメントも見たい」 「詳細情報(1対1)も編集したい」
react-adminはこの辺も“関係性コンポーネント”が用意されてます。 以降の例は、こういうデータを想定します。
posts:{ id, title, body, author_id }users:{ id, name }comments:{ id, post_id, body }post_details:{ id, post_id, seo_title, seo_description }
6.1 many-to-one(Post → User):著者名を表示&選択
一覧で表示(ReferenceField)
import { List, DataTable, TextField, ReferenceField } from "react-admin"; export const PostList = () => ( <List> <DataTable rowClick="edit" bulkActionButtons={false}> <DataTable.Col source="title" /> <DataTable.Col label="Author"> <ReferenceField source="author_id" reference="users" link="show"> <TextField source="name" /> </ReferenceField> </DataTable.Col> </DataTable> </List> );
Editで選択(ReferenceInput + SelectInput)
import { Edit, SimpleForm, TextInput, ReferenceInput, SelectInput } from "react-admin"; export const PostEdit = () => ( <Edit> <SimpleForm> <TextInput source="title" fullWidth /> <ReferenceInput source="author_id" reference="users"> <SelectInput optionText="name" fullWidth /> </ReferenceInput> </SimpleForm> </Edit> );
6.2 one-to-many(Post → Comments):Showにコメント一覧をぶら下げる
外部キーが子側(comments.post_id)にある典型パターンです。
import { Show, SimpleShowLayout, TextField, ReferenceManyField, DataTable } from "react-admin"; export const PostShow = () => ( <Show> <SimpleShowLayout> <TextField source="id" /> <TextField source="title" /> <ReferenceManyField reference="comments" target="post_id" label="コメント"> <DataTable bulkActionButtons={false}> <DataTable.Col source="id" /> <DataTable.Col source="body" /> </DataTable> </ReferenceManyField> </SimpleShowLayout> </Show> );
裏では dataProvider.getManyReference が呼ばれます。つまり、リレーションも「画面ではなく dataProvider 側の責務」として閉じられるのが良いところです。
6.3 one-to-one(Post → PostDetails):詳細を表示する
1対1も、やりたいことは「1件取って表示」。
ReferenceOneField は getManyReference で取って先頭を使う、という挙動になります。
import { Show, SimpleShowLayout, TextField, ReferenceOneField } from "react-admin"; export const PostShow = () => ( <Show> <SimpleShowLayout> <TextField source="title" /> <ReferenceOneField reference="post_details" target="post_id" label="詳細"> <SimpleShowLayout> <TextField source="seo_title" /> <TextField source="seo_description" /> </SimpleShowLayout> </ReferenceOneField> </SimpleShowLayout> </Show> );
1対1の“編集”について(注意)
ReferenceOneInput のような「1対1編集」コンポーネントもありますが、Enterprise版のコンポーネントとして提供されます。
本記事では「まず表示できる」までに留めます。
7. 一覧を「画面内スクロール」にする
CRUD/リレーションが一通り揃うと、次に運用で地味に効いてくるのが一覧の使い勝手です。 一般的な管理画面だと「ページ全体」ではなく テーブル部分だけスクロールする構成が多いと思います。
この章は Layout 側も少し触るので、必要になったタイミングで読むのがおすすめです。
特に避けたいのはこの辺。
- 一覧が縦に伸びすぎて、検索やアクションがスクロールの彼方へ
- 画面上部に戻るのがだるい
- ヘッダー(列名)も見失う
react-adminの各種コンポーネントは、既存の仕組みに乗っかりつつ外からレイアウト調整できるので、「仕組みはそのまま・見た目だけ整える」がやりやすいです。
ここでは List を分解して ListBase + ListView で組み直し、ツールバーとページネーションを“スクロールの外”に出します。
前提:Layout 側で「高さ」と「スクロール」を決める
List 側で height: 100% を使うなら、親(コンテンツ領域)の高さが確定している必要があります。
ページ全体(body)が縦に伸びてスクロールしてしまうと、結局「ページ全体スクロール」になってしまうので、先に Layout 側で「スクロールさせる箱」を決めておくのがコツです。
// src/layout/CustomLayout.tsx(デモの考え方) import { AppBar, Sidebar, Menu } from "react-admin"; import { Box, useTheme } from "@mui/material"; type CustomLayoutProps = { children: React.ReactNode }; export const CustomLayout = (props: CustomLayoutProps) => { const theme = useTheme(); return ( <Box sx={{ display: "flex", height: "100vh" }}> <Box sx={{ height: "100%", zIndex: theme.zIndex.appBar - 1 }}> <Sidebar> <Menu /> </Sidebar> </Box> <AppBar alwaysOn={false} /> <Box component="main" sx={{ height: "100vh", width: "calc(100% - 50px)", display: "flex", flexDirection: "column", }} > <Box id="main-content" sx={{ flex: 1, overflow: "auto" }}> {props.children} </Box> </Box> </Box> ); };
これは Admin の layout prop(例:<Admin layout={CustomLayout} ...>)で差し込めます。
実アプリでは Layout の .RaLayout-content に height/overflow を当てる形でもOKです。重要なのは「スクロールさせる箱を1つに決める」ことです。
// src/posts/PostList.tsx import { DataTable, DateField, ShowButton, EditButton, ListViewProps, ListBase, ListView, ListToolbar, } from "react-admin"; import { Box, Paper } from "@mui/material"; const dataTableStyles = { // テーブル部分だけスクロールさせる(ページ全体は伸ばさない) "& .RaDataTable-tableWrapper": { maxHeight: "calc(100vh - 120px)", overflow: "auto", }, // 横が溢れやすいので、列は折り返さずに見せる(必要なら ellipsis 等を追加) "& .RaDataTable-headerCell, & .RaDataTable-rowCell": { whiteSpace: "nowrap", textOverflow: "ellipsis", }, }; const PostDataTable = () => { return ( <DataTable rowClick="show" bulkActionButtons={false} sx={{ ...dataTableStyles }}> <DataTable.Col source="id" /> <DataTable.Col source="title" /> <DataTable.Col source="status" /> <DataTable.Col source="author" /> <DataTable.Col source="created_at"> <DateField source="created_at" showTime /> </DataTable.Col> <DataTable.Col> <ShowButton /> <EditButton /> </DataTable.Col> </DataTable> ); }; export const PostList = (props: ListViewProps) => { const { pagination, filters, actions } = props; return ( <ListBase perPage={25} sort={{ field: "id", order: "ASC" }}> <ListView> <Box sx={{ display: "flex", flexDirection: "column", height: "100%", minHeight: 0, position: "relative" }}> <ListToolbar filters={filters} actions={actions} /> <Paper sx={{ flex: 1, minHeight: 0, overflow: "hidden", position: "relative" }}> <PostDataTable /> </Paper> <Box sx={{ position: "absolute", bottom: 0, right: 16 }}>{pagination}</Box> </Box> </ListView> </ListBase> ); };
ポイントはこんな感じです。
- 前提:Layout 側でコンテンツ領域の高さを固定し、スクロールさせる箱を1つに決める(デモは
#main-content) .RaDataTable-tableWrapperを狙って“テーブルだけスクロール”に寄せる- 親は
height: 100%+minHeight: 0(flex + overflow の定番罠回避) - ツールバー/ページネーションをスクロール外に出して「操作がスクロールの彼方へ」を防ぐ
- DataTable は列ヘッダがデフォルトで sticky(列名を見失いにくい)
calc(100vh - 120px) の 120px は「あなたのヘッダー/フィルタ/ページネーションの高さ」に合わせて調整してください。ここだけはUIと相談です(でも調整箇所はここ1箇所で済む)。 わかりやすさのため、react-adminの
を大胆に書き換えていますが、継承する形でも問題はないです。
8. ra-data-simple-rest で足りない場合の dataProvider 定義(axios版)
ra-data-simple-rest が合わないケースもあります。たとえば、
- クエリ形式が独自(filter/sort/rangeの形が違う)
- 送信時にデータを付与しないといけない
- レスポンスの形が
{ items, total }みたいに違う - パスが
/api/v1/...で、リソースごとに歪みがある - 認証ヘッダやリトライ、共通エラー整形を入れたい
こういう時に、画面に通信の実装を漏らさず、dataProvider に閉じ込めるのが正解です。
ここでは axios でAPIを叩く custom dataProvider の例を置きます(デモ実装寄り)。
// src/api.ts import axios from "axios"; export const api = axios.create({ baseURL: "<http://localhost:3000>", // 認証が cookie ベースなら withCredentials: true, });
// src/dataProvider.ts import type { DataProvider } from "react-admin"; import { api } from "./api"; type ListResponse<T> = { items: T[]; total: number; }; export const dataProvider: DataProvider = { getList: async (resource, params) => { const page = params.pagination?.page ?? 1; const perPage = params.pagination?.perPage ?? 10; const field = params.sort?.field ?? "id"; const order = params.sort?.order ?? "ASC"; const res = await api.get<ListResponse<unknown>>(`/${resource}`, { params: { page, perPage, sort: field, order, filter: params.filter, }, }); return { data: res.data.items as unknown as { id: string | number }[], total: res.data.total }; }, getOne: async (resource, params) => { const res = await api.get<unknown>(`/${resource}/${params.id}`); return { data: res.data as unknown as { id: string | number } }; }, getMany: async (resource, params) => { const res = await api.get<ListResponse<unknown>>(`/${resource}`, { params: { page: 1, perPage: params.ids.length, sort: "id", order: "ASC", filter: { id: params.ids }, }, }); return { data: res.data.items as unknown as { id: string | number }[] }; }, getManyReference: async (resource, params) => { const page = params.pagination?.page ?? 1; const perPage = params.pagination?.perPage ?? 10; const field = params.sort?.field ?? "id"; const order = params.sort?.order ?? "ASC"; const res = await api.get<ListResponse<unknown>>(`/${resource}`, { params: { page, perPage, sort: field, order, filter: { ...(params.filter ?? {}), [params.target]: params.id, }, }, }); return { data: res.data.items as unknown as { id: string | number }[], total: res.data.total }; }, update: async (resource, params) => { const res = await api.put<unknown>(`/${resource}/${params.id}`, params.data); return { data: res.data as unknown as { id: string | number } }; }, updateMany: async (resource, params) => { await Promise.all(params.ids.map((id) => api.put(`/${resource}/${id}`, params.data))); return { data: params.ids }; }, create: async (resource, params) => { const res = await api.post<unknown>(`/${resource}`, params.data); return { data: res.data as unknown as { id: string | number } }; }, delete: async (resource, params) => { const res = await api.delete<unknown>(`/${resource}/${params.id}`); return { data: res.data as unknown as { id: string | number } }; }, deleteMany: async (resource, params) => { await Promise.all(params.ids.map((id) => api.delete(`/${resource}/${id}`))); return { data: params.ids }; }, };
9. バックエンド側に求められる最低限
この記事のコードは「そのままでは動かないが、周辺を用意してコピペすれば動く」前提だったりするので、最低限の契約を書いておきます。
CRUDの最低限(posts)
GET /posts(ページング/ソート/フィルタを受ける)GET /posts/:idPOST /postsPUT /posts/:idDELETE /posts/:id
リレーション用(例)
GET /users/GET /users/:idGET /comments?filter[post_id]=...(または同等)GET /post_details?filter[post_id]=...(または同等)
ra-data-simple-rest の方言に乗るなら、その形式に合わせれば繋がります。独自方言ならcustom dataProviderで吸収します。
おまけ:バックエンドがまだでも画面を作りたい(モック dataProvider)
「バックエンドは後で」って言われがちな現場向けに、クライアント側だけでデータを生やす ra-data-fakerest という選択肢もあります。
まず画面を固めて、後からAPI接続に置き換える、ができます。
おわりに
つらつらと記載しましたが、今回はかなり最低限のできることに留めています。 公式ドキュメントを見てもらうと項目が多いのと、7章で紹介した画面内スクロールみたいに「ドキュメントに載ってないけど便利」な部品が、びっくりするくらい紛れてたりもします。
(ドキュメントに羅列だけでもいいからして欲しい…切実に)
大体のことはできるし、拡張性もかなり高いので、ぜひ快適なフロントエンド作成につなげてもらえるとです。 ここまで枠組みを用意してあげれば、AIエージェントに「〇〇のCRUD作って。リクエストとレスポンスはこんな感じ(型定義添付)」で、大体それっぽいものを出してくれます。
今回はreact-adminを取り上げましたが、Refineでも似たことができるので、機会があればそっちの紹介と比較もできれば。
ではでは〜。
そういえばの蛇足
そういえば、エンジニア募集しているのでよければ見てやってください。 採用情報:https://hrmos.co/pages/meetsmore/jobs
エンジニアだったら私が採用フローに出没します...。
参考
- react-admin 公式ドキュメント(Data Providers / Data Fetching / Reference* / Inputs など)
- MUI の公式ドキュメント