Collections
Collections are the primary way to structure and organize your data in VSKI. Each collection has a schema that defines the fields and their types.
Collections can live in in different databases, but collection metadata is
stored in the default database.
Creating Collections
Section titled “Creating Collections”Collections are declared, not created imperatively: the schema lives in YAML
and vski apply turns changes into reviewable, rollback-able server-side
migrations (see IaC).
Basic Collection
Section titled “Basic Collection”version: 1database: default
collections: posts: type: base fields: - {name: title, type: text, required: true} - {name: content, type: text} - {name: published, type: bool} - {name: views, type: number}vski plan # + collection default/posts (create base table with 4 fields)vski apply # creates it via a migrationScaffolding a new project? vski setup writes a starter file exactly like
this one.
Field Types
Section titled “Field Types”| Type | Description | Example |
|---|---|---|
text | Plain text string | "Hello" |
email | Email address | "user@example.com" |
number | Numeric value | 42 or 3.14 |
bool | Boolean value | True or False |
date | Date string | "2024-01-01" |
datetime | Datetime with timezone | "2024-01-01T00:00:00Z" |
select | Predefined options | Use with values: [...] |
url | URL string | "https://example.com" |
editor | Rich text content | HTML/Markdown content |
json | JSON object | { "key": "value" } |
relation | Reference to another collection | Use with collectionId |
Auth Collections
Section titled “Auth Collections”collections: _users: type: auth fields: - {name: email, type: email, required: true} - {name: password, type: text, required: true} - {name: name, type: text}Auth collections automatically include system fields (id, created,
updated). Fields whose names start with _ are internal and never managed
by IaC.
When declaring a collection you write
fields:; when reading collection metadata back the server returns the schema asschema:. Same data, different key on the wire.
Creating Records
Section titled “Creating Records”Runtime data operations use the Python SDK (pip install vski-sdk; see
SDK Installation):
import asynciofrom vski import VskiClient
async def main(): async with VskiClient("http://127.0.0.1:3000") as client: res = await client.admins.auth_with_password( "admin@yourdomain.com", "your-password" ) client.set_token(res["token"])
post = await client.collection("posts").create( {"title": "Hello World", "content": "My first post", "published": True, "views": 0} ) print(post["id"]) # auto-generated print(post["created"]) # auto timestamp
asyncio.run(main())For quick one-offs the command line does the same:
vski records create posts '{"title": "Hello World", "published": true}'vski records list posts --filter 'published = true'Using Custom ID
Section titled “Using Custom ID”post = await client.collection("posts").create( {"id": "custom-post-id", "title": "Custom ID Post", "content": "..."})Reading Records
Section titled “Reading Records”Get Single Record
Section titled “Get Single Record”post = await client.collection("posts").get_one("record-id")print(post["title"])List Records
Section titled “List Records”page = await client.collection("posts").get_list(1, 20)print(page["totalItems"]) # total number of recordsprint(page["items"]) # records on this pageprint(page["totalPages"]) # total pagesFilter Records
Section titled “Filter Records”page = await client.collection("posts").get_list( 1, 20, filter="published = true AND views > 10")Sort Records
Section titled “Sort Records”newest = await client.collection("posts").get_list(1, 20, sort="-created")multi = await client.collection("posts").get_list(1, 20, sort="-published,created")Updating Records
Section titled “Updating Records”updated = await client.collection("posts").update( "record-id", {"title": "Updated Title", "views": 100})# updates are partial: unchanged fields keep their valuespartial = await client.collection("posts").update("record-id", {"title": "Just the title"})Deleting Records
Section titled “Deleting Records”ok = await client.collection("posts").delete("record-id") # TrueQuery Filters
Section titled “Query Filters”Filters are SQL-like expression strings:
get = lambda **kw: client.collection("posts").get_list(1, 20, **kw)
# Comparison: = != > < >= <=await get(filter="views > 100")
# Logical: AND, OR, parentheses for groupingawait get(filter="(status = 'active' OR status = 'pending') AND published = true")
# Strings: ~ is a substring match (LIKE with wildcards added automatically)await get(filter="title ~ 'Hello'")
# Null checksawait get(filter="deleted = null")await get(filter="deleted != null")
# JSON/array fieldsawait get(filter="tags ~ ['python', 'vski']")await get(filter="tags ~ 'python'")Pagination
Section titled “Pagination”page1 = await client.collection("posts").get_list(1, 20)page2 = await client.collection("posts").get_list(2, 20)if page1["page"] < page1["totalPages"]: ... # more pages availableExpand Relations
Section titled “Expand Relations”When you have relation fields, you can expand them:
post = await client.collection("posts").get_one("post-id", expand="author,category")print(post["expand"]["author"]) # full author recordprint(post["expand"]["category"]) # full category recordCollection Management
Section titled “Collection Management”Schema changes (add/remove fields, indexes, rules, FTS) belong in the YAML;
vski apply diffs desired against live and emits the migration. To drop a
collection, mark it state: absent (or delete it with vski collections rm,
which removes the table immediately).
collections: old_posts: state: absentInspecting what exists:
vski collections listvski collections show posts # fields, rules, indexes, viewsReading collection metadata from the SDK:
result = await client.settings.collections.get_list()posts = next(c for c in result["items"] if c["name"] == "posts")print(posts["schema"])Warning: Deleting a collection deletes all its records.
Best Practices
Section titled “Best Practices”- Use meaningful field names: follow consistent naming conventions
- Set required fields: use
required: truefor essential data - Index frequently queried fields: improve query performance
- Use appropriate types: choose the most specific type for your data
- Validate on client side: reduce unnecessary API calls
- Use pagination: always use
get_list()with limits for large datasets
API Endpoints
Section titled “API Endpoints”| Method | Endpoint | Description |
|---|---|---|
GET | /api/collections | List collections |
POST | /api/collections | Create collection |
GET | /api/collections/:name | Get collection |
PATCH | /api/collections/:name | Update collection |
DELETE | /api/collections/:name | Delete collection |
GET | /api/collections/:name/records | List records |
POST | /api/collections/:name/records | Create record |
GET | /api/collections/:name/records/:recordId | Get record |
PATCH | /api/collections/:name/records/:recordId | Update record |
PUT | /api/collections/:name/records/:recordId | Update record (full) |
DELETE | /api/collections/:name/records/:recordId | Delete record |
PATCH | /api/collections/:name/records/bulk | Bulk update records |
DELETE | /api/collections/:name/records/bulk | Bulk delete records |
GET | /api/collections/:name/records/search | Full-text search records |
GET | /api/collections/:name/records/views/:slug | Get records from view |