In this tutorial, we would develop a GraphQL based admin dashboard with Seaography and Loco.
Read our first and second tutorial of the series, Getting Started with Loco & SeaORM, Adding GraphQL Support to Loco with Seaography, if you haven't.
The full source code can be found here.
What is Seaographyโ
Seaography is a GraphQL framework for building GraphQL resolvers using SeaORM entities. It ships with a CLI tool that can generate ready-to-compile Rust GraphQL servers from existing MySQL, Postgres and SQLite databases.
Setup React-Admin Frontend Boilerplateโ
We use React-Admin
as the frontend framework. It provides a convenient boilerplate to start with:
$ npm init react-admin frontend
Select the data provider you want to use, and validate with Enter:
โฏ None
I'll configure the data provider myself.
Select the auth provider you want to use, and validate with Enter:
โฏ Hard coded local username/password
Enter the name of a resource you want to add, and validate with Enter (leave empty to finish):
โฏ (Leave empty and press Enter)
How do you want to install the dependencies?
โฏ Using npm
Run the boilerplate then visit http://localhost:5173/, you should see the welcome page:
$ cd frontend
$ npm install
$ npm run dev
> dev
> vite
VITE v4.5.3 ready in 440 ms
โ Local: http://localhost:5173/
Now, we want to display the React-Admin
data table template with mock data. First, we need to add ra-data-json-server
dependency, it provides a ready-to-go mock data loader:
$ npm install ra-data-json-server
To prepare the mock data loader, we create a new file:
import jsonServerProvider from 'ra-data-json-server';
export const dataProvider = jsonServerProvider('https://jsonplaceholder.typicode.com');
Then, we change the UI file:
+ import { Admin, Resource, ListGuesser, ShowGuesser } from 'react-admin';
+ import { dataProvider } from './dataProvider';
ReactDOM.createRoot(document.getElementById('root')!).render(
- <React.StrictMode>
- <App />
- </React.StrictMode>
+ <Admin dataProvider={dataProvider}>
+ <Resource name="users" list={ListGuesser} show={ShowGuesser} />
+ </Admin>
);
Run the boilerplate now you should see the user listing page:
$ npm run dev
Click on each row to view its detail page.
Add NPM dependencyโ
Next, we start to integrate our Loco and Seaography backend with React-Admin frontend. We use axios
for sending POST request to our GraphQL backend:
$ npm install axios
GraphQL Data Providerโ
Then, we can start implementing the GraphQL data provider by replacing the content of dataProvider.ts
:
- import jsonServerProvider from 'ra-data-json-server';
-
- export const dataProvider = jsonServerProvider('https://jsonplaceholder.typicode.com');
Integrating with our GraphQL endpoint at http://localhost:3000/api/graphql. We implemented two handler below, one fetch data for the post listing and the other to fetch data for a single post:
import { DataProvider } from "react-admin";
import axios from 'axios';
const apiUrl = 'http://localhost:3000/api/graphql';
export const dataProvider: DataProvider = {
// Fetch data for post listing
getList: (resource, params) => {
// Paginator status
const { page, perPage } = params.pagination;
// Sorter status
const { field, order } = params.sort;
// POST request to GraphQL endpoint
return axios.post(apiUrl, {
query: `
query {
notes (
orderBy: { ${field}: ${order} },
pagination: { page: { limit: ${perPage}, page: ${page - 1} }}
) {
nodes {
id
title
createdAt
updatedAt
}
paginationInfo {
pages
current
offset
total
}
}
}
`
})
.then((response) => {
// Unwrap the response
const { nodes, paginationInfo } = response.data.data.notes;
// Return the data array and total number of pages
return {
data: nodes,
total: paginationInfo.total,
};
});
},
// Fetch data for a single post
getOne: (resource, params) => {
// POST request to GraphQL endpoint
return axios.post(apiUrl, {
query: `
query {
notes(filters: {id: {eq: ${params.id}}}) {
nodes {
id
title
content
createdAt
updatedAt
}
}
}
`
})
.then((response) => {
// Unwrap the response
const { nodes } = response.data.data.notes;
// Return the one and only data
return {
data: nodes[0],
};
});
},
getMany: (resource, params) => { },
getManyReference: (resource, params) => { },
update: (resource, params) => { },
updateMany: (resource, params) => { },
create: (resource, params) => { },
delete: (resource, params) => { },
deleteMany: (resource, params) => { },
};
Customize React-Admin Frontendโ
Replace the React-Admin template frontend with our own custom UI to list all notes from the database.
- ReactDOM.createRoot(document.getElementById('root')!).render(
- <Admin dataProvider={dataProvider}>
- <Resource name="users" list={ListGuesser} show={ShowGuesser} />
- </Admin>
- );
Implement the list and details page with specific columns:
import ReactDOM from 'react-dom/client';
import { Admin, Resource, List, Datagrid, TextField, Show, SimpleShowLayout } from 'react-admin';
import { dataProvider } from './dataProvider';
const PostList = () => (
<List>
<Datagrid bulkActionButtons={false}>
<TextField source="id" />
<TextField source="title" />
<TextField source="content" />
<TextField source="createdAt" />
<TextField source="updatedAt" />
</Datagrid>
</List>
);
const PostShow = () => (
<Show>
<SimpleShowLayout>
<TextField source="id" />
<TextField source="title" />
<TextField source="content" />
<TextField source="createdAt" />
<TextField source="updatedAt" />
</SimpleShowLayout>
</Show>
);
ReactDOM.createRoot(document.getElementById('root')!).render(
<Admin dataProvider={dataProvider}>
<Resource name="posts" list={PostList} show={PostShow} />
</Admin>
);
Auth Free GraphQL Endpointโ
Disabled user authentication on GraphQL POST handler endpoint for convenient:
async fn graphql_handler(
- _auth: auth::JWT,
State(ctx): State<AppContext>,
req: Request<Body>,
) -> Result<Response> {
const DEPTH: usize = 1_000;
const COMPLEXITY: usize = 1_000;
// Construct the the GraphQL query root
let schema = query_root::schema(ctx.db.clone(), DEPTH, COMPLEXITY).unwrap();
// GraphQL handler
let mut graphql_handler = async_graphql_axum::GraphQL::new(schema);
// Execute GraphQL request and fetch the results
let res = graphql_handler.call(req).await.unwrap();
Ok(res)
}
Put It into Action!โ
Run the React-Admin frontend:
$ cd frontend
$ npm run dev
Run the Loco backend:
$ cd backend
$ cargo run start
Visit http://localhost:5173/, you should see the post listing page:
We are fetching data from the GraphQL backend:
Click on column header to sort by the column in ascending or descending order:
Click on each row to view its detail page:
Conclusionโ
Adding GraphQL support to Loco application is easy with the help of Seaography. It is an ergonomic library that perfectly integrate with any frontend framework. This tutorial only cover the basic integration of LOco and Seaography including only the querying of data via the GraphQL endpoint. GraphQL mutations are not demonstrated and we leave it for you to code it out!
SQL Server Supportโ
SQL Server for SeaORM is now available as a closed beta. If you are interested`, please signup here.
Migrating from sea-orm
to sea-orm-x
is straightforward with two simple steps. First, update the existing sea-orm
dependency to sea-orm-x
and enable the sqlz-mssql
feature. Note that you might need to patch SeaORM dependency for the upstream dependencies.
sea-orm = { path = "<SEA_ORM_X_ROOT>/sea-orm-x", features = ["runtime-async-std-rustls", "sqlz-mssql"] }
sea-orm-migration = { path = "<SEA_ORM_X_ROOT>/sea-orm-x/sea-orm-migration" }
# Patch SeaORM dependency for the upstream dependencies
[patch.crates-io]
sea-orm = { path = "<SEA_ORM_X_ROOT>/sea-orm-x" }
sea-orm-migration = { path = "<SEA_ORM_X_ROOT>/sea-orm-x/sea-orm-migration" }
Second, update the connection string to connect to the MSSQL database.
# If the schema is `dbo`, simply write:
mssql://username:password@host/database
# Or, specify the schema name by providing an extra `currentSchema` query param.
mssql://username:password@host/database?currentSchema=my_schema
# You can trust peer certificate by providing an extra trustCertificate query param.
mssql://username:password@host/database?trustCertificate=true
SeaORM X has full Loco support and integrate seamlessly with many web frameworks:
- Actix
- Axum
- Async GraphQL
- jsonrpsee
- Loco
- Poem
- Salvo
- Tonic
Happy Coding!