docs: plugins, cloudflare, sdk, elements, database (#240)

* docs: added plugins docs, updated cloudflare docs

* updated cli help text

* added `systemEntity` and added docs on how to work with system entities

* docs: added defaults to cloudflare image plugin

* docs: updated sdk and elements
This commit is contained in:
dswbx
2025-08-29 12:50:23 +02:00
committed by GitHub
parent 5b879ac6bf
commit 5ed1cf19b6
17 changed files with 655 additions and 100 deletions
+12 -10
View File
@@ -16,22 +16,24 @@ Here is the output:
$ npx bknd
Usage: bknd [options] [command]
⚡ bknd cli v0.16.0
⚡ bknd cli v0.17.0
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
user <action> create/update users, or generate a token (auth)
types [options] generate types
schema [options] get schema
run [options] run an instance
debug <subject> debug bknd
create [options] create a new project
copy-assets [options] copy static assets
config [options] get default config
help [command] display help for command
config [options] get app config
copy-assets [options] copy static assets
create [options] create a new project
debug <subject> debug bknd
mcp [options] mcp server stdio transport
run [options] run an instance
schema [options] get schema
sync [options] sync database
types [options] generate types
user [options] <action> create/update users, or generate a token (auth)
help [command] display help for command
```
## Starting an instance (`run`)
@@ -349,6 +349,91 @@ Note that we didn't add relational fields directly to the entity, but instead de
manually.
</Callout>
### System entities
There are multiple system entities which are added depending on if the module is enabled:
- `users`: if authentication is enabled
- `media`: if media is enabled and an adapter is configured
You can add additional fields to these entities. System-defined fields don't have to be repeated, those are automatically added to the entity, so don't worry about that. It's important though to match the system entities name, otherwise a new unrelated entity will be created.
If you'd like to connect your entities to system entities, you need them in the schema to access their reference when making relations. From the example above, if you'd like to connect the `posts` entity to the `users` entity, you can do so like this:
```typescript
import { em, entity, text, number, systemEntity } from "bknd";
const schema = em(
{
posts: entity("posts", {
title: text().required(),
slug: text().required(),
content: text(),
views: number(),
// don't add the foreign key field, it's automatically added
}),
comments: entity("comments", {
content: text(),
}),
// [!code highlight]
// add a `users` entity
users: systemEntity("users", { // [!code highlight]
// [!code highlight]
// optionally add additional fields
}) // [!code highlight]
},
// now you have access to the system entity "users"
({ relation, index }, { posts, comments, users }) => {
// ... other relations
relation(posts).manyToOne(users); // [!code highlight]
},
);
```
### Add media to an entity
If media is enabled, you can upload media directly or associate it with an entity. E.g. you may want to upload a cover image for a post, but also a gallery of images. Since a relation to the media entity is polymorphic, you have to:
1. add a virtual field to your entity (single `medium` or multiple `media`)
2. add the relation from the owning entity to the media entity
3. specify the mapped field name by using the `mappedBy` option
```typescript
import { em, entity, text, number, systemEntity, medium, media } from "bknd";
const schema = em(
{
posts: entity("posts", {
title: text().required(),
slug: text().required(),
content: text(),
views: number(),
// [!code highlight]
// `medium` represents a single media item
cover: medium(), // [!code highlight]
// [!code highlight]
// `media` represents a list of media items
gallery: media(), // [!code highlight]
}),
comments: entity("comments", {
content: text(),
}),
// [!code highlight]
// add the `media` entity
media: systemEntity("media", { // [!code highlight]
// [!code highlight]
// optionally add additional fields
}) // [!code highlight]
},
// now you have access to the system entity "media"
({ relation, index }, { posts, comments, media }) => {
// add the `cover` relation
relation(posts).polyToOne(media, { mappedBy: "cover" }); // [!code highlight]
// add the `gallery` relation
relation(posts).polyToMany(media, { mappedBy: "gallery" }); // [!code highlight]
},
);
```
### Type completion
To get type completion, there are two options:
@@ -44,16 +44,8 @@ export default function UserAvatar() {
#### Props
- `initialItems?: xMediaFieldSchema[]`: Initial items to display, must be an array of media objects.
- `entity?: { name: string; id: number; field: string }`: If given, the initial media items fetched will be from this entity.
- `query?: RepoQueryIn`: Query to filter the media items.
- `overwrite?: boolean`: If true, the media item will be overwritten on entity media uploads if limit was reached.
- `maxItems?: number`: Maximum number of media items that can be uploaded.
- `autoUpload?: boolean`: If true, the media items will be uploaded automatically.
- `onRejected?: (files: FileWithPath[]) => void`: Callback when a file is rejected.
- `onDeleted?: (file: FileState) => void`: Callback when a file is deleted.
- `onUploaded?: (file: FileState) => void`: Callback when a file is uploaded.
- `placeholder?: { show?: boolean; text?: string }`: Placeholder text to show when no media items are present.
<AutoTypeTable path="../app/src/ui/elements/media/DropzoneContainer.tsx" name="DropzoneContainerProps" />
#### Customize Rendering
@@ -199,6 +199,37 @@ To delete many records of an entity, use the `deleteMany` method:
const { data } = await api.data.deleteMany("posts", { views: { $lte: 1 } });
```
### `data.readManyByReference([entity], [id], [reference], [query])`
To retrieve records from a related entity by following a reference, use the `readManyByReference` method:
```ts
const { data } = await api.data.readManyByReference("posts", 1, "comments", {
limit: 5,
sort: "-created_at",
});
```
### `data.count([entity], [where])`
To count records in an entity that match certain criteria, use the `count` method:
```ts
const { data } = await api.data.count("posts", {
views: { $gt: 100 }
});
```
### `data.exists([entity], [where])`
To check if any records exist in an entity that match certain criteria, use the `exists` method:
```ts
const { data } = await api.data.exists("posts", {
title: "Hello, World!"
});
```
## Auth (`api.auth`)
Access the `Auth` specific API methods at `api.auth`. If there is successful authentication, the
@@ -241,3 +272,89 @@ To retrieve the current user, use the `me` method:
```ts
const { data } = await api.auth.me();
```
## Media (`api.media`)
Access the `Media` specific API methods at `api.media`.
### `media.listFiles()`
To retrieve a list of all uploaded files, use the `listFiles` method:
```ts
const { data } = await api.media.listFiles();
// ^? FileListObject[]
```
### `media.getFile([filename])`
To retrieve a file as a readable stream, use the `getFile` method:
```ts
const { data } = await api.media.getFile("image.jpg");
// ^? ReadableStream<Uint8Array>
```
### `media.getFileStream([filename])`
To get a file stream directly, use the `getFileStream` method:
```ts
const stream = await api.media.getFileStream("image.jpg");
// ^? ReadableStream<Uint8Array>
```
### `media.download([filename])`
To download a file as a File object, use the `download` method:
```ts
const file = await api.media.download("image.jpg");
// ^? File
```
### `media.upload([item], [options])`
To upload a file, use the `upload` method. The item can be any of:
- `File` object
- `Request` object
- `Response` object
- `string` (URL)
- `ReadableStream`
- `Buffer`
- `Blob`
```ts
// Upload a File object
const { data } = await api.media.upload(item);
// Upload from a URL
const { data } = await api.media.upload("https://example.com/image.jpg");
// Upload with custom options
const { data } = await api.media.upload(item, {
filename: "custom-name.jpg",
});
```
### `media.uploadToEntity([entity], [id], [field], [item], [options])`
To upload a file directly to an entity field, use the `uploadToEntity` method:
```ts
const { data } = await api.media.uploadToEntity(
"posts",
1,
"image",
item
);
```
### `media.deleteFile([filename])`
To delete a file, use the `deleteFile` method:
```ts
const { data } = await api.media.deleteFile("image.jpg");
```