Hexabase TypeScript SDKを使ったデモ（ページネーションの実装）

[Hexabase（ヘキサベース）](https://www.hexabase.com/)は企業においても安心して利用できるBaaS（Backend as a Service）を提供しています。多くのBaaSがそうであるように、主にフロントエンド開発者に利用してもらいたいと考えています。そこで現在、TypeScript SDKの開発が進められています。

この記事ではv2系になって新しくなったHexabase TypeScript SDKのインストールと、データストア（クラウドデータベース）を使ってページネーションを実現します。

## デモ

以下はデモアプリです。データの登録と、ページネーション表示を行っています。

https://stackblitz.com/edit/stackblitz-starters-nbvye8?embed=1&file=pages%2Findex.tsx

## インストール

インストールはnpmやyarnを使って行います。

```
# npmの場合
npm install @hexabase/hexabase-js

# yarnの場合
yarn add @hexabase/hexabase-js
```

## インポート

インポートすると、 `HexabaseClient` というオブジェクトが取得できます。

```jsx
import { HexabaseClient, Item } from "@hexabase/hexabase-js";
```

## 初期化

HexabaseClientを初期化します。

```tsx
// 本番閑居向け
const client = new HexabaseClient();

// 開発環境向け
const client = new HexabaseClient('dev');

```

なお、すでに認証済みだった場合に備えて、2番目の引数にトークンを指定できます。

```tsx
const client = new HexabaseClient('dev', 'YOUR_TOKEN');
```

### 認証

Hexabaseでは業務利用を想定しているため、利用する際に認証情報が必須になります。最初はメールアドレスとパスワードで認証し、その後はトークンを使ってGraphQLにアクセスします。 `client` を使って処理します。

初回の認証は次のようになります。emailとパスワード、またはトークンが必須です。

```tsx
await client.login({email, password, token});
```

後はこの `client` に対して処理を行います。

## ワークスペースとプロジェクト、データストアの設定

Hexabsaeではワークスペースとプロジェクト、そしてデータストアがそれぞれ1:nの関係になっています。そこで、まずワークスペースだけ設定します。

```tsx
interface Pagination {
  previous: boolean;
  next: boolean;
  limit: number;
  total: number;
  page: number;
}

const [pagination, setPagination] = useState<Pagination>({
  previous: false,
  next: false,
  limit: 3,
  total: 0,
  page: 1,
});

// Init
const initialize = async () => {
  // Set workspace
  await client.setWorkspace('YOUR_WORKSPACE_ID');
  await getItems(pagination);
};

useEffect(() => {
  initialize();
}, []);
```

## データの取得

データの取得はTypeScript SDKに用意しているクエリーオブジェクトで行っています。[O/Rマッパー風に取得もできる](https://qiita.com/goofmint/items/a827d282d30c9a0dcda8)ので、好みに合わせて使い分けてください。

後半ではページネーションに関係する処理を行っています。トータルの件数、前のページや次のページが存在するかどうかをチェックしています。

```tsx
// Get items and update pagination
const getItems = async (p: Pagination) => {
  const query = client.query('YOUR_DATASTORE_ID');
  const items = await query
    .from(process.env.NEXT_PUBLIC_DATASTORE)
    .page(p.page)
    .limit(p.limit)
    .order({ title: 'desc' })
    .select('*');
  // Set tasks
  setItems(items);
  // get total items count
  const total = await query.from('YOUR_DATASTORE_ID').count();
  p.total = total;
  // check previous page
  p.previous = p.page > 1;
  // check next page
  p.next = p.page * p.limit < p.total;
  setPagination({ ...p });
};
```

## 一覧表示とページネーション表示

今回はNext.jsを利用していますので、一覧表示は以下のようになります。 `item.get` に対して `<string>` のように指定することで文字列の返却として処理されます。

```tsx
<ul>
  {items.map((item) => (
    <li key={item.id}>
      <span onClick={() => done(item)}>✓</span>
      {item.get<string>('Title')}
    </li>
  ))}
</ul>
```

もし `Title` というカラムがない場合には例外が返ってきます。それが問題ある場合には2番目の引数にデフォルト値を指定してください。

```tsx
item.get<string>('Title2', 'デフォルトの値');
```

### ページネーション表示

ページネーション表示は以下のように行っています。今回はページ番号は出さず、前のページと次のページの有無によって `<>` を表示しています。

```tsx
<div className={styles.pagination}>
  {pagination.previous ? (
    <span onClick={() => changePage(-1)}>&lt;</span>
  ) : (
    ''
  )}
  &nbsp;
  {(pagination.page - 1) * pagination.limit + 1} to &nbsp;
  {pagination.page * pagination.limit > pagination.total
    ? pagination.total
    : pagination.page * pagination.limit}
  &nbsp;
  {pagination.next ? (
    <span onClick={() => changePage(1)}>&gt;</span>
  ) : (
    ''
  )}
</div>
```

## データの追加

データの追加についてもSQL風に処理を行います。こちらも、[O/Rマッパー風の処理も可能](https://qiita.com/goofmint/items/a827d282d30c9a0dcda8)です。

```tsx
// Save item to datastore
const submit = async (e) => {
  e.preventDefault();
  const query = client.query('YOUR_PROJECT_ID');
  const item = (await query.from('YOUR_DATASTORE_ID').insert({
    Title: value,
  })) as Item;
  // Reset input value
  setValue('');
  getItems(pagination);
};
```

`insert` はバッチ処理にも対応しており、その場合は `Item[]` で返ってきます。 `Item` または `Item[]` は必要に応じて指定してください。

## データの削除

データの削除についてもSQL風に行います。注意点としては対象データを `i_id` として指定することです。[O/Rマッパー風に削除する場合には、こちらを参照](https://qiita.com/goofmint/items/a827d282d30c9a0dcda8)してください。一件削除するだけであれば、 `item.delete()` の方が手軽そうです。

```tsx
const done = async (item: Item) => {
  // Delete task
  const query = client.query(process.env.NEXT_PUBLIC_PROJECT_ID!);
  await query
    .from(process.env.NEXT_PUBLIC_DATASTORE)
    .where(query.condition.equalTo('i_id', item.id))
    .delete();
  // Remove deleted task from tasks
  getItems(pagination);
};
```

## まとめ

SDKを使うことで、Hexabaseのデータストアを簡単に利用できます。ぜひWebアプリケーション開発に活用してください。

Hexabaseには他にもファイルストレージやスクリプトなどの機能があります。それらの機能も利用してください。