ayisha-client
v0.5.2
Published
Official JavaScript client for the Ayisha CMS API
Readme
ayisha-client
Official JavaScript client for the Ayisha CMS REST API.
Works in Node.js 18+ and modern browsers (native fetch).
Install
Published on npm: ayisha-client
npm install ayisha-clientAlternatives:
yarn add ayisha-client
pnpm add ayisha-clientQuick start
import { AyishaClient, createMediaField, SCHEMA_ATTRIBUTE_TYPES } from 'ayisha-client';
const ayisha = new AyishaClient({
baseUrl: 'https://your-cms.example.com',
});
// In browsers, login() persists the JWT to localStorage and restores it on reload.
// 1. Login (required for writes and schema admin)
await ayisha.login('username', 'password');
// 2. List public schema names
const schemaNames = await ayisha.getSchemas();
// [{ name: "product" }, { name: "event" }]
// 3. Read content by schema + name (GET /cms/product/tshirt)
const tshirt = await ayisha.getContent('product', 'tshirt');
// 4. List all products
const { contents } = await ayisha.getContents('product', { page: 1, limit: 20 });
// 5. Create content
await ayisha.createContent('product', {
name: 'tshirt',
active: true,
});
// All supported schema attribute types
console.log(SCHEMA_ATTRIBUTE_TYPES.map((t) => t.type));API naming
The SDK uses a consistent get / create / update / delete pattern:
| Resource | List | Single | Create | Update | Delete |
| --- | --- | --- | --- | --- | --- |
| Content | getContents(schema, params?) | getContent(schema, nameOrId) | createContent | updateContent | deleteContent |
| Schema | getSchemas() / getSchemasAdmin() | getSchema(id) | createSchema | updateSchema | deleteSchema |
| User | getUsers(params?) | — | createUser | updateUser | deleteUser |
Pass a schema name (e.g. 'product') for /cms/{schema} routes, or a schema ObjectId for /cms/objects routes — same methods, automatic routing.
Migration from 0.3.x
| Deprecated | Use instead |
| --- | --- |
| listSchemas() | getSchemas() |
| listSchemasAdmin() | getSchemasAdmin() |
| listContents(schema, params?) | getContents(schema, params?) |
| getContentByName(schema, name) | getContent(schema, name) |
| getContentById(schema, id) | getContent(schema, id) |
| listContentsByAuthor(...) | getContentsByAuthor(...) |
| listObjects(schemaId, params?) | getContents(schemaId, params?) |
| getObject(schemaId, id) | getContent(schemaId, id) |
| createObject / updateObject / deleteObject | createContent / updateContent / deleteContent |
| listUsers() | getUsers() |
| listCmsUsers() | getCmsUsers() |
Deprecated aliases remain available and delegate to the new methods.
API routes overview
| Area | Base path | Use case |
| --- | --- | --- |
| CMS API | /cms/* | All API operations (content, auth, schemas, users, backup, analytics) |
| Media files | /api/media/:id | Proxy URLs embedded in JSON responses |
| Settings | /cms/settings | Locale and theme |
Auth
| Method | Route | Auth | Description |
| --- | --- | --- | --- |
| login(username, password) | POST /cms/auth | — | JWT (30d), stored on client and persisted in the browser (localStorage by default). Fails with 403 / AUTH_ACCOUNT_PENDING if the account is inactive. |
| loginAdmin(username, password) | POST /cms/auth | — | Same as login() (alias). |
| register({ username, password }) | POST /cms/register | — | Creates an inactive user account. No JWT; an administrator must set isActive: true before sign-in. |
| registerAdmin({ username, password }) | POST /cms/register | — | First call creates superAdmin (active). Later calls create inactive user accounts (same activation flow as register). |
| setToken(token) | — | — | Set or restore JWT (also persists when storage is enabled) |
| getToken() | — | — | Current JWT |
| logout() | — | — | Clears JWT from memory and storage |
| getStore(key) | — | — | Read a persisted app value (browser storage by default) |
| setStore(key, value) | — | — | Save a persisted app value (null removes it) |
| removeStore(key) | — | — | Remove a persisted app value |
Token persistence (browser)
By default (storage: 'auto'), the client saves the JWT to localStorage after login() and restores it on the next page load:
const ayisha = new AyishaClient({ baseUrl: window.location.origin });
// token restored automatically if the user logged in before
await ayisha.login('username', 'password'); // saved to localStorage
ayisha.logout(); // clears memory + localStorageOptions:
| storage | Behavior |
| --- | --- |
| 'auto' (default) | localStorage in browsers, disabled in Node.js |
| 'local' | Always use localStorage |
| 'session' | Use sessionStorage (cleared when the tab closes) |
| 'none' | Memory only (previous behavior) |
| Custom adapter | { getToken, setToken, removeToken, getStore, setStore, removeStore } |
new AyishaClient({
baseUrl: 'https://your-cms.example.com',
storageKey: 'my-app-ayisha-token',
storePrefix: 'my-app-store:',
});App store (browser)
Persist your own app data with the same storage backend as the JWT:
ayisha.setStore('cart', [{ id: 'tshirt', qty: 2 }]);
const cart = ayisha.getStore('cart');
ayisha.setStore('theme', 'dark');
ayisha.removeStore('theme');Values are JSON-serialized. Keys are stored under the prefix ayisha-store: in localStorage by default.
Account activation
Self-registration (register / registerAdmin after bootstrap) returns { pendingActivation: true, code: 'AUTH_REGISTRATION_PENDING' } and does not issue a JWT.
An administrator activates the account with:
await ayisha.loginAdmin('admin', 'password');
await ayisha.updateCmsUser(userId, { isActive: true });
// or updateUser(userId, { isActive: true }) on /cms/usersUser records expose isActive (false until activated).
Content
Read
| Method | Route | Returns |
| --- | --- | --- |
| getContents(schema, params?) | GET /cms/{schema} or GET /cms/objects/{schemaId} | Paginated { contents, totalItems, pageItems, totalPages, currentPage, limit } |
| getContent(schema, nameOrId) | GET /cms/{schema}/{segment} or GET /cms/objects/{schemaId}/{id} | Single item (contents[0] on CMS) or null |
| getContentsByAuthor(authorId, schema, params?) | GET /cms/{authorId}/{schema} | Paginated wrapper |
| iterateContents(schema, params?) | paginated helper | Yields each item |
// List
await ayisha.getContents('product', { page: 1, limit: 20, q: 'cotone' });
// Single by name or ObjectId
const tshirt = await ayisha.getContent('product', 'tshirt');
const byId = await ayisha.getContent('product', '66d4296014cb559e8ba2ce86');
// Admin: same methods with schema ObjectId from getSchemasAdmin()
const { schemas } = await ayisha.getSchemasAdmin();
await ayisha.getContents(schemas[0]._id, { page: 1 });
const obj = await ayisha.getContent(schemas[0]._id, 'OBJECT_ID');Note: CMS path lookups return the paginated wrapper over HTTP. The SDK unwraps contents[0] for getContent when using schema names.
Write
| Method | Route | Description |
| --- | --- | --- |
| createContent(schema, data) | POST /cms/{schema} or POST /cms/objects | data.name required |
| updateContent(schema, id, data) | PUT /cms/{schema}/{id} or PUT /cms/objects/{schemaId}/{id} | Update by ObjectId |
| deleteContent(schema, id) | DELETE /cms/{schema}/{id} or DELETE /cms/objects/{schemaId}/{id} | Delete by ObjectId |
Schema admin
Requires JWT with createSchema, updateSchema, deleteSchema capabilities.
| Method | Route | Description |
| --- | --- | --- |
| getSchemas() | GET /cms/schemas | Public — [{ name }] only |
| getSchemasAdmin(params?) | GET /cms/schemas | Full schemas with attributes |
| getSchema(schemaId) | GET /cms/schemas/:id | Single schema |
| createSchema({ name, attributes }) | POST /cms/schemas | Create schema |
| updateSchema(id, { name, attributes }) | PUT /cms/schemas/:id | Update schema |
| deleteSchema(id) | DELETE /cms/schemas/:id | Delete schema and all content |
Schema attribute types
Import SCHEMA_ATTRIBUTE_TYPES and SCHEMA_ATTRIBUTE_EXAMPLE from the package.
| Type | Value in data | Notes |
| --- | --- | --- |
| text | "hello" | Short string |
| longtext | "paragraph..." | Multi-line text |
| richtext | "<p>HTML</p>" | HTML |
| number | 42 | Number |
| boolean, checkbox | true | Filter with ?field=true |
| select | "option-a" | Requires options[] on attribute |
| multiselect | ["a","b"] | Filter with comma-separated values |
| date | "2026-08-07" | ISO date |
| datetime-local | "2026-08-07T12:00" | ISO datetime |
| time | "12:30" | HH:mm |
| email, url, telephone | string | Validated in UI |
| password | string | Masked in UI |
| color | "#ff0000" | Hex |
| currency | "EUR" | ISO 4217 — use options |
| rating | 5 | Numeric |
| geolocation | "41.90,12.49" | Coordinates |
| json | { key: "value" } | JSON object |
| array | ["word1","word2"] | Word array |
| image, file | { name, media } | Base64 data URL on write |
| images, files | [{ name, media }] | Multiple media |
| relation | "OBJECT_ID" | relatedCollection = target schema name |
| multirelation | ["ID1","ID2"] | Array of ObjectIds |
| id | string | Custom id field |
Attribute definition shape:
{
id: 'uuid',
type: 'select',
label: 'category', // becomes data key and query param
required: true,
immutable: false,
options: ['a', 'b'], // select, multiselect, currency
relatedCollection: 'brand', // relation, multirelation
}Every schema must include a name field (type: 'text', label: 'name', required: true). Ayisha adds it automatically if missing.
import { SCHEMA_ATTRIBUTE_EXAMPLE } from 'ayisha-client';
await ayisha.createSchema(SCHEMA_ATTRIBUTE_EXAMPLE);Users admin
Requires JWT with user capabilities.
| Method | Route | Capability |
| --- | --- | --- |
| getUsers(params?) | GET /cms/users | readUser |
| createUser(payload) | POST /cms/users | createUser |
| updateUser(id, payload) | PUT /cms/users/:id | updateUser |
| deleteUser(id) | DELETE /cms/users/:id | deleteUser |
| getCmsUsers() | GET /cms/users | JWT |
| getCmsUser(id) | GET /cms/users/:id | JWT |
| updateCmsUser(id, payload) | PUT /cms/users/:id | JWT |
Profile fields (all optional on create/update): name, surname, email, phone, gender, birthdate, bio, picture (media object).
On create, only username and password are required. Use isActive: false to create a pending account, or activate later with { isActive: true }.
Backup
Server-side async jobs avoid timeouts on large databases. Authenticate first (POST /cms/auth), then use the job endpoints below. See backend/README.md for environment variables and wipe behaviour.
| Method | Route | Description |
| --- | --- | --- |
| exportBackup({ onProgress }) | POST /cms/backup/export/jobs (+ poll + download) | Server-side async export job |
| restoreBackup({ backup }, { onProgress }) | POST /cms/backup/restore/jobs (+ chunks + finalize) | Server-side async restore job |
| deleteAllData() | DELETE /cms/backup | Wipe CMS data except current user (schemas, objects, media including GridFS, settings, analytics; cancels backup jobs) |
Media
Media is saved inside content data as { name, media } (base64 data URL). Served at GET /api/media/:id.
| Method | Description |
| --- | --- |
| createMediaField(file) | Browser File → media object |
| fileToDataUrl(file) | Raw data URL |
| buildMediaUrl(mediaId) | Public URL |
| resolveMediaUrl(mediaField) | From API response field |
| getMedia(mediaId) | Download binary (ArrayBuffer) |
Gallery
List all images stored in the CMS (proxy URLs, parent content or user, orphan flag) at GET /cms/gallery.
| Method | Route | Auth |
| --- | --- | --- |
| getGallery(params?) | GET /cms/gallery | Optional JWT |
| iterateGallery(params?) | Async generator over getGallery pages | Optional JWT |
| deleteGalleryImage(mediaId) | DELETE /cms/gallery/:mediaId | deleteObject |
User profile pictures appear in the list but cannot be deleted from the gallery endpoint (update the user instead). Deleting a linked CMS image removes the parent content and all its media.
const { images, totalPages } = await ayisha.getGallery({ page: 1, limit: 24, q: 'cover' });
for await (const image of ayisha.iterateGallery({ limit: 50 })) {
console.log(image.url, image.orphan, image.content?.name);
}
await ayisha.deleteGalleryImage(images[0].id);Filters and search
await ayisha.getContents('product', {
name: 'tshirt',
category: 'abbigliamento',
active: 'true',
tags: 'new,sale', // multiselect
q: 'cotone',
page: 1,
limit: 20,
});Capabilities
Backoffice users need capabilities on their account:
- Users:
readUser,createUser,updateUser,deleteUser - Schemas:
readSchema,createSchema,updateSchema,deleteSchema - Content:
readContent,createContent,updateContent,deleteContent - Objects:
readObject,createObject,updateObject,deleteObject
Errors
import { AyishaApiError } from 'ayisha-client';
try {
await ayisha.getContent('product', 'missing');
} catch (error) {
if (error instanceof AyishaApiError) {
console.error(error.status, error.message, error.body);
}
}Publish (maintainers)
cd packages/ayisha-client
npm run releaseBump "version" in package.json before each publish.
Author
devben — Benito Massidda
- Website: devben.app
- GitHub: BenJrSky
License
MIT
