link example: https://svelte-hxb-sdk.vercel.app
npm create svelte@latest svelte-hxb-sdk
code svelte-hxb-sdk
You can choose your favavite IDE
npm install
yarn add
hexabase package
npm install @hexabase/hexabase-js
yarn add @hexabase/hexabase-js
svelte.config.ts
import adapter from "@sveltejs/adapter-auto";
import preprocess from "svelte-preprocess";
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: preprocess({
postcss: true,
}),
kit: {
adapter: adapter(),
},
};
export default config;
//It's default config, if you want to use external tools, let's add more configs
If you have never working with svelte or svelte-kit, please visit https://kit.svelte.dev/docs/introduction
Firstly, create auth services to handle login
, logout
or register
For example, add login
and logout
functions to user.service
// src\routes\auth\login\+page.ts
import { createClient, HexabaseClient } from "@hexabase/hexabase-js";
const url = import.meta.env.VITE_URL;
async function login(email: string, password: string) {
console.log("import.meta", import.meta);
let user = {} as any;
const hexabase = await createClient({
url,
token: "",
email,
password,
});
const { token, error } = await hexabase.auth.login({ email, password });
if (token && !error) {
const { userInfo, error } = await hexabase.users.get(token);
if (userInfo && !error) {
user = userInfo;
user.token = token;
}
localStorage.setItem("user", JSON.stringify(user));
}
return token;
}
async function logout() {
const token = JSON.parse(localStorage.getItem("user")!).token;
const hexabase = await createClient({ url, token });
await hexabase.auth.logout(token);
localStorage.removeItem("user");
}
export const userService = {
login,
logout,
};
//add other service to handle authentication
//Auth guard should be created to handle auth-related redirects or further bussiness logics
After login, let's get all available service.
// src\routes\workspaces\+page.ts
import { createClient, HexabaseClient } from "@hexabase/hexabase-js";
export const workspaceService = {
getWorkspaces,
};
const url = import.meta.env.VITE_URL;
async function initHxbClient() {
const token = JSON.parse(localStorage.getItem("user")!).token;
const hexabase = token && (await createClient({ url, token }));
return hexabase;
}
// get all workspaces
async function getWorkspaces() {
const hexabase = await initHxbClient();
console.log(hexabase);
console.log(hexabase);
const { workspaces, error } = await hexabase.workspaces.get();
return workspaces;
}
Once get workspaces list, we can render the <Select />
to choose a specific workspace to show its detail including projects and datastores. As example bellow, workspace with name Ola Mundo
is going to be selected to be displayed.
Let's pass the current_workspace_id
to getProjectsAndDatastores
api to get all projects and datastore of that workspace:
async function getAppAndDs(id: string) {
const hexabase = await initHxbClient();
const { appAndDs, error } =
await hexabase.applications.getProjectsAndDatastores(id);
return appAndDs;
}
You can create a new application
in current workspace
NOTE: application
and project
is equivalent in terms of meaning in this context
import { CreateProjectPl } from "@hexabase/hexabase-js/src/lib/types/application";
async function createApp(createProjectParams: CreateProjectPl) {
const hexabase = await initHxbClient();
const { app, error } = await hexabase.applications.create(
createProjectParams
);
return app?.project_id;
}
You can use a template, it is optional. Then you can create site to display detail information of datastores
//get items of a datastore
async function getItems(
projectId: string,
datastoreId: string,
getItemsParameters: GetItemsPl
) {
const hexabase = await initHxbClient();
const { dsItems, error } = await hexabase.items.get(
getItemsParameters,
datastoreId,
projectId
);
return dsItems;
}
//get item detail
async function getItemDetail(
datastoreId: string,
itemId: string,
projectId: string,
itemDetailParams: GetItemDetailPl
) {
const hexabase = await initHxbClient();
const { itemDetails, error } = await hexabase.items.getItemDetail(
datastoreId,
itemId,
projectId,
itemDetailParams
);
return itemDetails;
}
After get items from a datastore by call getItems
function, you should call getFields
as well, to get should-be-displayed fields according to fields setting
.
async function getFields(datastoreId: string, projectId: string) {
const hexabase = await initHxbClient();
const { dsFields, error } = await hexabase.datastores.getFields(
datastoreId,
projectId
);
return dsFields;
}
Click to any item to view the detail. The initial item detail automatically taken from the first elemtent of the table
attachments of item are displayed in files
tab
You can download those files to see it in your devices
async function deleteItem(
projectId: string,
datastoreId: string,
itemId: string,
deleteItemReq: DeleteItemReq
) {
const hexabase = await initHxbClient();
const { data, error } = await hexabase.items.delete(
projectId,
datastoreId,
itemId,
deleteItemReq
);
return data;
}
status
and user_id
options should taken as follows:
//get status list
async function getStatuses(datastoreId: string) {
const hexabase = await initHxbClient();
const { dsStatuses, error } = await hexabase.datastores.getStatuses(
datastoreId
);
return dsStatuses;
}
get user_id list
has not available, so you now just can take an user_id from items data
New item id
is taken from an api:
async function createItemId(datastoreId: string) {
const hexabase = await initHxbClient();
const { item_id, error } = await hexabase.items.createItemId(datastoreId);
return item_id;
}
const newItemPl = {
action_id: actionIdCreate,
item: createItemParams,
...,
};
// createItemParams should be an object contains what is up for an new item
{
field1_id: value1,
field2_id: value2
}
uploadFile service
is taken immediately when user change input of file field
. But it will not automatically add the new created file id
to new item payload, untill createItem service
is fired!
Let's upload a file with below params
const payload = {
contentTypeFile: contentTypeFile,
filename: nameFile,
filepath: `${datastoreId}/${itemId}/${fieldId}/${nameFile}`,
d_id: datastoreId,
p_id: projectId,
item_id: itemId,
display_order: 0,
field_id: fieldId,
content: content, //result as base64 format
};
To be uploaded image need converting to base64
format, so let create an helper function.
// src\lib\helper.ts
const toBase64 = (file: any) =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
export { toBase64 };
Beside to those inputs, other config in payload should be done logically
async function createItem(
projectId: string,
datastoreId: string,
newItemPl: CreateNewItemPl
) {
const hexabase = await initHxbClient();
const { itemNew, error } = await hexabase.items.create(
projectId,
datastoreId,
newItemPl
);
return itemNew;
}
Let's take a look at what are needed to update an item
async function updateItem(
projectId: string,
datastoreId: string,
itemId: string,
itemActionParameters: ItemActionParameters
) {
const hexabase = await initHxbClient();
const { data, error } = await hexabase.items.update(
projectId,
datastoreId,
itemId,
itemActionParameters
);
return data;
}
// Overall the last payload will look like this
const itemActionParameters: ItemActionParameters = {
changes: updateItemData,
action_id: updateId,
history: {
datastore_id: item.d_id,
action_id: updateId,
},
rev_no: Number(item.rev_no),
};
rev_no
should be taken from itemdetail
service while changes
object is more complicated. It would stands for item data
after edited
objectChange = {
as_title: field?.as_title,
cols: fieldLayout.size_x,
dataType: "file",
id: field.id,
idx,
rowHeight: "item.rowHeight",
rows: fieldLayout.size_y,
status: field.status,
tabindex,
title: title,
unique: field?.unique,
x: fieldLayout.col,
y: fieldLayout.row,
post_file_ids: value ? [...fileIds, value] : [...fileIds],
value: value ? [...fileIds, value] : [...fileIds],
};
//field and fieldLayout are perspectively field of layoutSettings and layoutField which alos has data_type of 'file'
//`tabIndex` and `idx` will be calculated as follows
tabindex = (fieldIdLayout.row + 1) * 10 + fieldIdLayout.col;
//fieldIdLayout is field settings of chosen filed, `find` from fieldLayout list, taken from getFields api (datastore service)
const fieldSettings = await datastoreService.getDetail(this.ds_id);
const idx = fieldSettings.fields.find(
(s: any) => s.id === field.field_id
).field_index;
// fieldSettings taken from api getDetail of datastore services.
// field is input specific element of one item
const layoutSettings = await datastoreService.getDetail(item.d_id);
const fieldLayout = layoutSettings.field_layout.find(
(f: any) => f.id === field.id
);
const field = layoutSettings.fields.find((f: any) => f.id === field.id);
// value is from the field which has data_type of 'file' in itemDetail data
// fieldIds is an array of existed file's id
Notice that if want to delete existed files when editing an item, the fileIds
also need to updated before do update item
service