# FastSchema SDK

FastSchema SDK provides a convenient way to connect to the FastSchema backend and perform various operations.

Source: https://fastschema.com/docs/sdk

FastSchema SDK provides a convenient way to connect to the FastSchema backend and perform various operations.

The FastSchema JavaScript SDK works in both Node.js and browser environments. To use the SDK, you need to install it using npm.

## Installation

FastSchema SDK can be installed using browser script tags or npm.

### Browser

```html
<script src="https://unpkg.com/fastschema@latest/dist/index.umd.js"></script>
<script>
  const fs = new fastschema.FastSchema("http://localhost:8000");
</script>
```

### NPM

```bash
npm install fastschema
```

## Login and initialize

The initialization must be done before any other operation.

```typescript
import { FastSchema } from "fastschema";

// Create a new instance of FastSchema
const fs = new FastSchema("http://localhost:8000");

// Login
await fs.auth().login({
  login: "admin",
  password: "123",
});

// Initialize: This must be called before any other operation
await fs.init();
```

## Expired sessions

When the server has refresh tokens enabled, the SDK renews the access token on its own: a request
answered with `401` triggers one refresh and is then retried once. Requests that arrive together
share a single refresh call, so a rotated refresh token is never spent twice.

A refresh only fails for good when the server rejects the refresh token (any `4xx`). The SDK then
clears both tokens and calls `onAuthExpired`, which is where an app prompts the user to sign in
again:

```typescript
const fs = new FastSchema("http://localhost:8000", {
  onAuthExpired: () => {
    // The session is gone. Ask the user to sign in again.
  },
});
```

`onAuthExpired` never fires for a visitor who was not signed in.

A refresh that fails because the server is unreachable or answers `5xx` leaves both tokens in place
and does not report an expired session: an outage is not a sign-out. The original request fails, and
the next one refreshes again.

`auth().refreshToken()` follows the same rule. It resolves to `undefined` when the session is gone,
and throws only on a server or network failure.

## Schema operations

### Create schema

```typescript
await fs.schemas().create({
  name: "tag",
  namespace: "tags",
  label_field: "name",
  fields: [
    {
      name: "name",
      label: "Name",
      type: "string",
      sortable: true,
      filterable: true,
      unique: false,
    },
    {
      name: "description",
      label: "Description",
      type: "string",
      optional: true,
    },
  ],
});
```

### Get schema

This operation will throw an error if the schema does not exist.

```typescript
const schemaTag = fs.schema("tag");
```

### Update a schema

```typescript
await fs.schemas().update("tag", {
  schema: {
    // Same as create
  },
  rename_fields: {
    // Rename fields
  },
  rename_tables: {
    // Rename tables
  },
});
```

### Delete a schema

```typescript
await fs.schemas().delete("tag");
```

## Content operations

### Get content

```typescript
fs.schema("tag").get<Tag>(params);
```

`params` can be one of the following:

- `id: string`: ID of the content (UUID string)

- A filter object that represents the following interface:

  ```typescript
  interface ListOptions {
    filter?: Filter;
    page?: number;
    limit?: number;
    sort?: string;
    select?: string;
  }
  ```

  Refer to the [Filter documentation](https://fastschema.com/docs/backend/list-records#filter) for more information about the filter object.

### Create content

```typescript
interface Tag {
  name: string;
  description: string;
}

const createdTag = await fs.schema("tag").create<Tag>({
  name: "Tag 01",
  description: "A description",
});
```

### Update content

```typescript
const updated = await fs.schema("tag").update(id, {
  description: "updated desc tag 1",
});
```

### Delete content

```typescript
await fs.schema("tag").delete(id);
```

### Delete multiple records

`bulkDelete` takes a filter, not a list of ids, and resolves with the number of
records the server actually removed. To delete a specific set of records, build
the filter from their primary keys with `createPrimaryKeyFilter`, otherwise every
record matching a broader filter is removed.

```typescript
import { createPrimaryKeyFilter } from "fastschema";

const deleted = await fs
  .schema("tag")
  .bulkDelete(createPrimaryKeyFilter("id", [1, 2, 3]));
```

The returned count can be lower than the number of ids you passed if the server
refuses part of the request, so compare it against your selection rather than
assuming every record was deleted.

### Upload files

```typescript
const files: File[] = [];
for (let i = 0; i < 5; i++) {
  files.push(new File([`test ${i}`], `test${i}.txt`));
}

const result = await fs.file().upload(files);
```

**Note**

Nodejs version before 20 does not support the `File` object.
You can use package `@web-std/file` to create a `File` object.

## Realtime Updates

FastSchema provides a way to listen to events in real-time.

- `create`: When a new record is created
- `update`: When a record is updated
- `delete`: When a record is deleted
- `*`: Listen to all events

```typescript
const schemaTag = fs.schema("tag");

const cb1 = (data: T, event: EventType, error: Error) => {
  console.log("Event:", event, "Data:", data, "Error:", error);
};

const cb2 = (data: T[], event: EventType, error: Error) => {
  console.log("Event:", event, "Data:", data, "Error:", error);
};

const cb3 = (data: T | T[], event: EventType, error: Error) => {
  console.log("Event:", event, "Data:", data, "Error:", error);
};

schemaTag.on("create", cb1);
schemaTag.on("update", cb2);
schemaTag.on("delete", cb2);
schemaTag.on("*", cb3);
```

You can also listen to events for a specific record.

```typescript
schemaTag.on("create", id, cb1);
schemaTag.on("update", id, cb1);
schemaTag.on("delete", id, cb1);
```

or use the configuration events:

```typescript
schemaTag.on({
  id?: string;
  once?: boolean;
  select?: string;
  filter?: Filter;
}, cb1);
```

The configuration object can have the following properties:

- `id`: ID of the record (UUID string)
- `once`: If true, the callback will be called only once
- `select`: Fields to select, separated by commas. This is useful when you want to select only specific fields to reduce the payload size.
- `filter`: Filter object, used to filter the records that will trigger the event.
