mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-03 00:26:01 +00:00
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:
@@ -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");
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user