### Run the Huly issue-list example Source: https://github.com/hcengineering/huly-examples/blob/main/README.md Installs dependencies and runs the issue-list example with ts-node. Requires a local Huly instance or a workspace on Huly Cloud. ```bash npm install npx ts-node examples/issue-list.ts ``` -------------------------------- ### Example: find with lookup and sort Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/platform-client.md Example from platform-api/examples/issue-list.ts showing how to find a project by identifier with a lookup, then find issues in that project with limit and sort. ```typescript const project = await client.findOne( tracker.class.Project, { identifier: 'HULY' }, { lookup: { type: task.class.ProjectType } } ) if (project === undefined) throw new Error('Project not found') // project.$lookup?.type is the looked-up ProjectType document const projectType = project.$lookup?.type const issues = await client.findAll( tracker.class.Issue, { space: project._id }, { limit: 20, sort: { modifiedOn: SortingOrder.Descending } } ) console.log('found issues:', issues.length) ``` -------------------------------- ### Example: create a milestone Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/platform-client.md Example from platform-api/examples/issue-update.ts showing how to create a milestone document with an explicit ID. ```typescript const milestoneId = generateId() await client.createDoc( tracker.class.Milestone, project._id, { label: `Milestone #${milestones.length + 1}`, status: MilestoneStatus.InProgress, targetDate, comments: 0 }, milestoneId ) ``` -------------------------------- ### Search example with findAll Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/core-query.md Example of using findAll with full-text search. Searches for 'coffee' in the given space, limits to 10 results, sorts by modifiedOn descending, and requests total. Logs the total match count. ```ts import { SortingOrder } from '@hcengineering/core' import tracker from '@hcengineering/tracker' const result = await client.findAll( tracker.class.Issue, { $search: 'coffee', space: project._id }, { limit: 10, sort: { modifiedOn: SortingOrder.Descending }, total: true } ) console.log('matched:', result.total) ``` -------------------------------- ### Example: round-trip markdown using upload and fetch Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/platform-client.md This example demonstrates how to upload a markdown string to a document's attribute, retrieve it back, fetch it back, and print it.xyz. It assumes a previously obtained `client` and `markdownText`, and constructs a new document with a title and parent. The example originates from `platform-ecc/example/document-create.ts` and references undefined variables like `document`, `documentId`, `markdownText`, and `client`. ```ts:Object const content = = = = = = = = = = = = = = exact code as in source: const content = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = . . . ``` -------------------------------- ### Example: TxOperations over REST Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/rest-client.md Example usage of createRestTxOperations to create a TxOperations instance and update a project document. ```typescript import { createRestTxOperations } from '@hcengineering/api-client' import tracker, { MilestoneStatus } from '@hcengineering/tracker' const tx = await createRestTxOperations('https://app.huly.app', 'ws-id', 'token', true) const project = await tx.findOne(tracker.class.Project, { identifier: 'HULY' }) if (project !== undefined) { await tx.updateDoc(tracker.class.Project, project._id, project._id, { description: 'new' }) } ``` -------------------------------- ### DocumentQuery usage examples Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/core-query.md Examples of DocumentQuery usage from the huly-examples repo: equality on plain fields, operator selectors, and exact id matching. ```typescript // equality on plain fields { identifier: 'HULY' } { space: project._id } { name: 'My Documents', archived: false } // operator selectors { status: { $nin: [tracker.status.Done, tracker.status.Canceled] } } // exact id { _id: issueId } ``` -------------------------------- ### Custom factory example with logging Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/socket.md Shows how to create a custom ClientSocketFactory that wraps NodeWebSocketFactory to log outgoing data, and how to pass it to connect() via socketFactory. ```typescript import { connect } from '@hcengineering/api-client' import type { ClientSocket, ClientSocketFactory } from '@hcengineering/client' // Custom WebSocket factory for a mocked connection (used in tests or proxies) const loggingFactory: ClientSocketFactory = (url: string): ClientSocket => { const inner = NodeWebSocketFactory(url) const wrapped: ClientSocket = { get readyState () { return inner.readyState }, send: (data) => { console.log('>>', data); inner.send(data) }, close: (code) => inner.close(code), onmessage: inner.onmessage, onclose: inner.onclose, onopen: inner.onopen, onerror: inner.onerror } inner.onmessage = (ev) => wrapped.onmessage?.(ev) inner.onclose = (ev) => wrapped.onclose?.(ev) inner.onopen = (ev) => wrapped.onopen?.(ev) inner.onerror = (ev) => wrapped.onerror?.(ev) return wrapped } const client = await connect('http://localhost:8087', { email: 'user1', password: '1234', workspace: 'ws1', socketFactory: loggingFactory }) ``` -------------------------------- ### Example — list issues over REST Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/rest-client.md Example usage of connectRest to list issues from a tracker. It connects to a local server, finds issues with a limit of 20, and logs their identifiers and titles. The client has no close method, so nothing is released in the finally block. ```typescript import { connectRest } from '@hcengineering/api-client' import tracker from '@hcengineering/tracker' const client = await connectRest('http://localhost:8087', { email: 'user1', password: '1234', workspace: 'ws1' }) try { const issues = await client.findAll(tracker.class.Issue, { space: projectId }, { limit: 20 }) console.log('total:', issues.total) for (const issue of issues) console.log('-', issue.identifier, issue.title) } finally { // RestClient has no close(); nothing to release } ``` -------------------------------- ### GET {url}/config.json Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/endpoints.md Fetches the server configuration including URLs for accounts, collaborator, files, and upload services. No authentication is required. ```APIDOC ## GET {url}/config.json ### Description Fetches the server configuration including URLs for accounts, collaborator, files, and upload services. No authentication is required. ### Method GET ### Endpoint {url}/config.json ### Parameters None ### Response Returns a `ServerConfig` object with the following fields: - `ACCOUNTS_URL` (string) - URL of the accounts service - `COLLABORATOR_URL` (string) - URL of the collaborator service - `FILES_URL` (string) - URL of the file storage service - `UPLOAD_URL` (string) - URL of the upload service ### Status Codes - 200 OK - Successful response - Any other status - Error('Failed to fetch config') ### Example Response ```json { "ACCOUNTS_URL": "...", "COLLABORATOR_URL": "...", "FILES_URL": "...", "UPLOAD_URL": "..." } ``` ``` -------------------------------- ### get Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md Performs a GET request to retrieve the object's content. Returns the body as a `Readable` stream. ```APIDOC ## `get` ### Description Performs a GET request to retrieve the object's content. Returns the body as a `Readable` stream. ### Method GET ### Parameters #### Path Parameters - `objectName` (string) - Required - Storage key of the object. ### Response #### Success Response (200) - Returns a `Readable` stream of the object's body. #### Error Handling - Throws `StorageError('Missing response body')` if the response has no body. ### Example ```ts const stream = await client.get('my-object'); ``` ``` -------------------------------- ### GET /api/v1/account/{workspace} Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/endpoints.md Returns the authenticated account information for the specified workspace. ```APIDOC ## GET /api/v1/account/{workspace} ### Description Returns the authenticated account information for the specified workspace. ### Method GET ### Endpoint /api/v1/account/{workspace} ### Parameters #### Path Parameters - **workspace** (string) - Required - The workspace UUID. ### Response - **200**: Account object with fields: uuid, role, primarySocialId, socialIds, fullSocialIds. ``` -------------------------------- ### Update project with $inc Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/core-foundations.md Example usage from `platform-api/examples/issue-create.ts`. Updates a project document under the root space, incrementing its sequence field. ```ts import core, { type Ref, SortingOrder, generateId } from '@hcengineering/core' const incResult = await client.updateDoc( tracker.class.Project, core.space.Space, // project documents live under the root space project._id, { $inc: { sequence: 1 } }, true ) ``` -------------------------------- ### Sort query examples Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/core-query.md Examples of sort queries using SortingOrder: sort by modifiedOn descending for newest first, by name ascending for documents, and by rank descending for lexically last issue. ```ts { sort: { modifiedOn: SortingOrder.Descending } } // newest first { sort: { name: SortingOrder.Ascending } } // documents by name { sort: { rank: SortingOrder.Descending } } // lexically last issue ``` -------------------------------- ### GET /api/v1/load-model/{workspace} Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/endpoints.md Loads the model transactions for the workspace. If full=true, returns the full model; otherwise returns a partial model stream. ```APIDOC ## GET /api/v1/load-model/{workspace} ### Description Loads the model transactions for the workspace. If full=true, returns the full model; otherwise returns a partial model stream. ### Method GET ### Endpoint /api/v1/load-model/{workspace} ### Parameters #### Path Parameters - **workspace** (string) - Required - The workspace UUID. #### Query Parameters - **full** (boolean) - Optional - If true, returns the full model. ### Response - **200**: Tx[] model stream. The client rebuilds Hierarchy and ModelDb via buildModel. ``` -------------------------------- ### Read HULY_* environment variables into ConnectOptions Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/configuration.md Example from platform-api/examples/issue-list.ts showing how to read HULY_* environment variables with defaults and construct a ConnectOptions object for connect(). Uses NodeWebSocketFactory and a 30-second connection timeout. ```typescript const url = process.env.HULY_URL ?? 'http://localhost:8087' const options: ConnectOptions = { email: process.env.HULY_EMAIL ?? 'user1', password: process.env.HULY_PASSWORD ?? '1234', workspace: process.env.HULY_WORKSPACE ?? 'ws1', socketFactory: NodeWebSocketFactory, connectionTimeout: 30000 } ``` -------------------------------- ### Basic connection with email/password Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/connect.md Basic connection example using email/password authentication. Reads HULY_URL, HULY_EMAIL, HULY_PASSWORD, and HULY_WORKSPACE from environment variables with defaults. Uses NodeWebSocketFactory and a 30-second connection timeout. After connecting, finds a project by identifier and closes the client in a finally block. ```ts import { ConnectOptions, NodeWebSocketFactory, connect } from '@hcengineering/api-client' const url = process.env.HULY_URL ?? 'http://localhost:8087' const options: ConnectOptions = { email: process.env.HULY_EMAIL ?? 'user1', password: process.env.HULY_PASSWORD ?? '1234', workspace: process.env.HULY_WORKSPACE ?? 'ws1', socketFactory: NodeWebSocketFactory, connectionTimeout: 30000 } const client = await connect(url, options) try { const project = await client.findOne(tracker.class.Project, { identifier: 'HULY' }) console.log('project:', project?.identifier) } finally { await client.close() } ``` -------------------------------- ### Create a teamspace (teamspace-create.ts) Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/document-plugin.md Creates a teamspace using the document API. Note the source caveat: the shipped example calls getAccount() without await and reads account._id, but getAccount returns Promise whose key is uuid. The correct pattern is shown here. ```ts import core from '@hcengineering/core' import document from '@hcengineering/document' const account = await client.getAccount() // note: example must await; Account has `uuid` const teamspaceId = await client.createDoc( document.class.Teamspace, core.space.Space, { name: 'My Documents', description: 'Space for my shared documents', private: false, archived: false, members: [account.uuid], owners: [account.uuid], icon: document.icon.Teamspace, type: document.spaceType.DefaultTeamspaceType } ) ``` -------------------------------- ### createRestTxOperations Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/core-tx.md Example of using createRestTxOperations to perform batched document updates via REST. ```APIDOC ## createRestTxOperations ### Description Creates a REST-based transaction operations client for performing batched document updates. ### Method createRestTxOperations ### Endpoint https://app.huly.app ### Parameters - **url** (string) - Required - The base URL of the Huly instance. - **workspaceId** (string) - Required - The workspace identifier. - **token** (string) - Required - Authentication token. - **useBroadcast** (boolean) - Optional - Whether to use broadcast for updates. ### Request Example ```ts import { createRestTxOperations } from '@hcengineering/api-client' import tracker from '@hcengineering/tracker' const tx = await createRestTxOperations('https://app.huly.app', workspaceId, token, true) ``` ### Response Returns a transaction operations object with `apply`, `updateDoc`, and `commit` methods. ### Response Example ```ts const batch = tx.apply() await batch.updateDoc(tracker.class.Issue, projectId, issueA, { priority: IssuePriority.High }) await batch.updateDoc(tracker.class.Issue, projectId, issueB, { priority: IssuePriority.Low }) const res = await batch.commit() console.log('success:', res.result) ``` ``` -------------------------------- ### StorageClient interface Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md The StorageClient interface defines methods for interacting with file blobs: stat, get, put, partial, and remove. ```APIDOC ## StorageClient interface ### Description Interface for the storage client that provides methods to stat, get, put, partial, and remove file blobs. ### Methods #### stat(objectName: string): Promise - **Parameters**: - `objectName` (string) - Required - The name of the object to stat. - **Returns**: Promise resolving to a `Blob` or `undefined` if not found. #### get(objectName: string): Promise - **Parameters**: - `objectName` (string) - Required - The name of the object to retrieve. - **Returns**: Promise resolving to a Node `Readable` stream. #### put(objectName: string, stream: Readable | Buffer | string, contentType: string, size?: number): Promise - **Parameters**: - `objectName` (string) - Required - The name of the object to upload. - `stream` (Readable | Buffer | string) - Required - The data stream or buffer to upload. - `contentType` (string) - Required - The MIME type of the content. - `size` (number) - Optional - The size of the content. - **Returns**: Promise resolving to a `Blob`. #### partial(objectName: string, offset: number, length?: number): Promise - **Parameters**: - `objectName` (string) - Required - The name of the object. - `offset` (number) - Required - The byte offset to start reading from. - `length` (number) - Optional - The number of bytes to read. - **Returns**: Promise resolving to a Node `Readable` stream. #### remove(objectName: string): Promise - **Parameters**: - `objectName` (string) - Required - The name of the object to remove. - **Returns**: Promise resolving when the object is removed. ### Source `src/storage/types.ts:19` ``` -------------------------------- ### Create a person with an email channel (person-create.ts) Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/contact-plugin.md Use this example to create a Person document with an email channel. It imports contact and core utilities, generates a person ID, creates the person with name and city, adds an email channel, and logs the created person. Requires a client instance and the contact plugin. ```TypeScript import contact, { AvatarType, Person } from '@hcengineering/contact' import { generateId } from '@hcengineering/core' const personId = generateId() await client.createDoc( contact.class.Person, contact.space.Contacts, { name: 'Doe,John', city: 'New York', avatarType: AvatarType.COLOR }, personId ) await client.addCollection( contact.class.Channel, contact.space.Contacts, personId, contact.class.Person, 'channels', { provider: contact.channelProvider.Email, value: 'john.doe@example.com' } ) const person = await client.findOne(contact.class.Person, { _id: personId }) console.log('created person:', person) ``` -------------------------------- ### Create a label and attach it to an issue Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/tags-plugin.md Complete example from platform-api/examples/issue-labels.ts: creates a TagElement, attaches a TagReference to an issue's 'labels' collection, then reads them back. ```typescript import tags, { type TagElement } from '@hcengineering/tags' import tracker from '@hcengineering/tracker' // 1. Create the tag element (workspace-scoped) const labelId: Ref = generateId() await client.createDoc( tags.class.TagElement, core.space.Workspace, { title: 'coffee', description: '', targetClass: tracker.class.Issue, color: 11, category: tracker.category.Other }, labelId ) // 2. Attach a TagReference into the issue's 'labels' collection await client.addCollection( tags.class.TagReference, project._id, issueId, tracker.class.Issue, 'labels', { title: 'example', color: 11, tag: labelId } ) // 3. Read them back const labels = await client.findAll( tags.class.TagReference, { attachedTo: issueId, attachedToClass: tracker.class.Issue } ) for (const label of labels) { console.log('- label:', label.title) } ``` -------------------------------- ### Token-based auth and error handling Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/connect.md Token-based authentication example with error handling. Uses a token from the HULY_TOKEN environment variable and a 15-second connection timeout. Catches errors like 'Workspace ... not found', 'Login failed', or PlatformError, logs them, and exits with code 1. ```ts import { connect, NodeWebSocketFactory } from '@hcengineering/api-client' import contact from '@hcengineering/contact' try { const client = await connect('https://app.huly.app', { token: process.env.HULY_TOKEN!, // obtained via the accounts service, see utils.ts workspace: 'acme', socketFactory: NodeWebSocketFactory, connectionTimeout: 15000 }) try { const persons = await client.findAll(contact.class.Person, {}) console.log('found persons:', persons.length) } finally { await client.close() } } catch (err) { // 'Workspace ... not found' or 'Login failed' or PlatformError land here console.error('cannot connect:', err) process.exit(1) } ``` -------------------------------- ### Inserting a document after a previous document Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/rank.md Example from platform-api/examples/documents/document-create.ts. Fetches the last document by rank descending, then creates a new document with a rank generated by makeRank(lastOne?.rank, undefined) to append after it. ```typescript const lastOne = await client.findOne( document.class.Document, { space: teamspace._id }, { sort: { rank: SortingOrder.Descending } } ) const documentId: Ref = generateId() await client.createDoc( document.class.Document, teamspace._id, { title: 'Make coffee', content, parent: document.ids.NoParent, rank: makeRank(lastOne?.rank, undefined) }, documentId ) ``` -------------------------------- ### Handle storage errors with custom error classes Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md Shows how to catch and distinguish NetworkError, NotFoundError, and StorageError when using the storage client. A 404 is mapped to undefined for stat, and get throws NotFoundError. ```TypeScript import { connectStorage, NetworkError, NotFoundError, StorageError } from '@hcengineering/api-client' const storage = await connectStorage('http://localhost:8087', { token: '…', workspace: 'ws1' }) try { const info = await storage.stat('missing.txt') if (info === undefined) console.log('not present') // 404 mapped to undefined await storage.get('missing.txt') } catch (err) { if (err instanceof NotFoundError) console.error('404:', err.message) else if (err instanceof NetworkError) console.error('network down') else if (err instanceof StorageError) console.error('storage failure') else throw err } ``` -------------------------------- ### ServerConfig JSON response Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/endpoints.md Response body of GET {url}/config.json. Returns ServerConfig with account, collaborator, file, and upload service URLs. No authentication required; status 200 OK, otherwise Error('Failed to fetch config'). ```json { "ACCOUNTS_URL": "…", "COLLABORATOR_URL": "…", "FILES_URL": "…", "UPLOAD_URL": "…" } ``` -------------------------------- ### Core Storage Types and Interfaces Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/core-query.md This snippet documents the core storage types and interfaces used by the Huly platform API, including WithLookup, FindResult, TxResult, Storage, and FulltextStorage, along with an example of using findAll with full-text search. ```APIDOC ## `WithLookup` and `FindResult` ```ts export type WithLookup = T & { $lookup?: LookupData $associations?: Record $source?: { $score: number // Score for document result [key: string]: any } } export type FindResult = WithLookup[] & { total: number lookupMap?: Record } ``` Sources: `src/storage.ts:206` (WithLookup), `:218` (FindResult). `findAll` returns an array of `WithLookup`; when `options.lookup` was used, resolved documents appear under `doc.$lookup.` — e.g. `project.$lookup.type` after `lookup: { type: task.class.ProjectType }` (see `platform-api/examples/issue-list.ts`). `total` is the full match count when `options.total` was requested. (The REST client also uses `lookupMap` internally to materialize lookups; it is deleted before returning — `src/rest/rest.ts:127`.) ## `TxResult` ```ts export interface TxResult {} ``` Source: `src/storage.ts:234`. Empty contract type returned by `updateDoc`, `removeDoc`, `createMixin`, `updateMixin`, and `tx`. With `retrieve: true`, the served payload also carries the updated object (the examples read `(result as any).object.sequence`). ## `Storage` and `FulltextStorage` ```ts export interface Storage { findAll: ( _class: Ref>, query: DocumentQuery, options?: FindOptions ) => Promise> tx: (tx: Tx) => Promise } export interface FulltextStorage { searchFulltext: (query: SearchQuery, options: SearchOptions) => Promise } ``` Sources: `src/storage.ts:286`, `:299`. `SearchQuery = { query: string, classes?: Ref>[], spaces?: Ref[] }`; `SearchOptions = { limit?: number }`; `SearchResult = { docs: SearchResultDoc[], total?: number }` (`src/storage.ts:247`, `:253`, `:281`). ### Example — search ```ts import { SortingOrder } from '@hcengineering/core' import tracker from '@hcengineering/tracker' const result = await client.findAll( tracker.class.Issue, { $search: 'coffee', space: project._id }, { limit: 10, sort: { modifiedOn: SortingOrder.Descending }, total: true } ) console.log('matched:', result.total) ``` ``` -------------------------------- ### connectStorage Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md Asynchronously creates a StorageClient by resolving configuration and obtaining a workspace token, then building file URLs. ```APIDOC ## connectStorage ### Description Resolves configuration (fetched from `{url}/config.json` if not passed), obtains a workspace token, and builds the file URLs. Returns a `StorageClient` implementation. ### Signature ```ts export async function connectStorage (url: string, options: AuthOptions, config?: ServerConfig): Promise ``` ### Parameters - `url` (string) - Required - The base URL of the Huly service. - `options` (AuthOptions) - Required - Authentication options. - `config` (ServerConfig) - Optional - Server configuration; if not provided, fetched from `{url}/config.json`. ### Returns - `Promise` - A promise that resolves to a `StorageClient` instance. ### Behavior - `filesUrl` = `config.FILES_URL` with `:workspace` replaced by the workspace UUID (if relative, prefixed with `url`). - `uploadUrl` = `config.UPLOAD_URL` with `:workspace` replaced. ### Source `src/storage/client.ts:209` ``` -------------------------------- ### createStorageClient Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md Directly creates a StorageClient using already-resolved URLs and a token. ```APIDOC ## createStorageClient ### Description Direct constructor for a `StorageClient` using already-resolved file and upload URLs, a token, and a workspace UUID. ### Signature ```ts export function createStorageClient ( filesUrl: string, uploadUrl: string, token: string, workspace: WorkspaceUuid ): StorageClient ``` ### Parameters - `filesUrl` (string) - Required - The base URL for file operations. - `uploadUrl` (string) - Required - The URL for upload operations. - `token` (string) - Required - Authentication token. - `workspace` (WorkspaceUuid) - Required - The workspace UUID. ### Returns - `StorageClient` - A `StorageClient` instance. ### Source `src/storage/client.ts:200` ``` -------------------------------- ### generateId usage example Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/core-utils.md Creates an issue ID using generateId() and passes it as the explicit id to addCollection/createDoc. ```typescript const issueId: Ref = generateId() // later passed as the explicit id of addCollection/createDoc ``` -------------------------------- ### GET {ACCOUNTS_URL}/providers Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/endpoints.md Retrieves the list of authentication providers available for the accounts service. No authentication is required. ```APIDOC ## GET {ACCOUNTS_URL}/providers ### Description Retrieves the list of authentication providers available for the accounts service. No authentication is required. ### Method GET ### Endpoint {ACCOUNTS_URL}/providers ### Parameters None ### Response Returns an array of `ProviderInfo` objects representing the available authentication providers. ### Status Codes - 200 OK - Successful response ``` -------------------------------- ### Round trip with markdown using uploadMarkup and fetchMarkup Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/markup.md Full round trip: uploads markdown content, stores it on an issue, then fetches it back and logs the markup. Demonstrates the expected usage of uploadMarkup and fetchMarkup with a real issue object. ```ts const description = await client.uploadMarkup(tracker.class.Issue, issueId, 'description', ` # Make coffee * Fill tank with fresh water * Put 2 scoops ground coffee `, 'markdown') await client.addCollection(tracker.class.Issue, project._id, project._id, project._class, 'issues', { title: 'Make coffee', description, // MarkupRef // ...other Issue fields }, issueId) const issue = await client.findOne(tracker.class.Issue, { _id: issueId }) if (issue?.description) { const markup = await client.fetchMarkup(issue._class, issue._id, 'description', issue.description, 'markdown') console.log(markup) } ``` -------------------------------- ### partial Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md Performs a GET request with a `Range` header to retrieve a partial portion of the object. Returns the body as a `Readable` stream. ```APIDOC ## `partial` ### Description Performs a GET request with a `Range` header to retrieve a partial portion of the object. Returns the body as a `Readable` stream. ### Method GET ### Parameters #### Path Parameters - `objectName` (string) - Required - Storage key. #### Query Parameters - `offset` (number) - Required - Byte offset to start reading from. - `length` (number) - Optional - Number of bytes to read. If omitted, the range is open-ended (`bytes={offset}`). ### Response #### Success Response (206 Partial Content) - Returns a `Readable` stream of the requested byte range. ### Example ```ts const stream = await client.partial('my-object', 10, 20); ``` ``` -------------------------------- ### Storage and FulltextStorage interfaces Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/core-query.md Core storage interfaces. Storage provides findAll and tx; FulltextStorage adds searchFulltext. SearchQuery = { query: string, classes?: Ref>[], spaces?: Ref[] }; SearchOptions = { limit?: number }; SearchResult = { docs: SearchResultDoc[], total?: number }. ```ts export interface Storage { findAll: ( _class: Ref>, query: DocumentQuery, options?: FindOptions ) => Promise> tx: (tx: Tx) => Promise } export interface FulltextStorage { searchFulltext: (query: SearchQuery, options: SearchOptions) => Promise } ``` -------------------------------- ### getAccount method signature Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/rest-client.md Signature of the getAccount method. It performs a GET request to /api/v1/account/{workspace} and returns the authenticated Account. ```typescript async getAccount (): Promise ``` -------------------------------- ### WebSocket RPC (connect()) Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/endpoints.md Opens a WebSocket to the workspace endpoint using ClientSocketFactory and communicates over the @hcengineering/client binary protocol. Connection timeout aborts with an error if no connection is established. ```APIDOC ## WebSocket RPC (connect()) ### Description Opens a WebSocket to the workspace endpoint using ClientSocketFactory and communicates over the @hcengineering/client binary protocol. Connect event on open; RPC requests/responses and server transactions flow over the socket. ### Connection Details - **Transport**: WebSocket - **Protocol**: @hcengineering/client binary protocol - **Binary Protocol**: Enabled by default (useBinaryProtocol = true unless metadata overrides) - **Compression**: Off by default - **Connection Timeout**: connectionTimeout (ms) aborts with Error('Connection timeout, and no connection established to {endpoint}') ### Events - **open**: Connect event on open ### Errors - **Timeout**: Error('Connection timeout, and no connection established to {endpoint}') ``` -------------------------------- ### GET /api/v1/search-fulltext/{workspace} Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/endpoints.md Performs a full-text search within the workspace. The query parameter is required; classes, spaces, and limit are optional. ```APIDOC ## GET /api/v1/search-fulltext/{workspace} ### Description Performs a full-text search within the workspace. The query parameter is required; classes, spaces, and limit are optional. ### Method GET ### Endpoint /api/v1/search-fulltext/{workspace} ### Parameters #### Path Parameters - **workspace** (string) - Required - The workspace UUID. #### Query Parameters - **query** (string) - Required - The search query. - **classes** (string) - Optional - JSON-encoded classes filter. - **spaces** (string) - Optional - JSON-encoded spaces filter. - **limit** (integer) - Optional - Maximum number of results. ### Response - **200**: SearchResult object with fields: docs (SearchResultDoc[]), total (optional). ``` -------------------------------- ### Auth and connection options Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/types.md Defines authentication and connection options. Accepted by connect(), connectRest(), connectStorage(), getWorkspaceToken(). Full option table in configuration.md. ```ts export interface PasswordAuthOptions { email: string; password: string; workspace: string } export interface TokenAuthOptions { token: string; workspace: string } export type AuthOptions = PasswordAuthOptions | TokenAuthOptions export interface ConnectSocketOptions { socketFactory?: ClientSocketFactory connectionTimeout?: number } export type ConnectOptions = ConnectSocketOptions & AuthOptions ``` -------------------------------- ### makeCollabId usage example Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/core-utils.md Resolves a markup field's blob URL pattern by creating a collaborative document id for the 'content' attribute. ```typescript import { makeCollabId } from '@hcengineering/core' const collabId = makeCollabId(doc._class, doc._id, 'content') // { objectClass, objectId, objectAttr } → encoded on the wire by the collaborator client ``` -------------------------------- ### fetchMarkup and uploadMarkup signatures Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/markup.md Function signatures for fetching and uploading collaborative markup. fetchMarkup computes the collab id via makeCollabId, calls collaborator.getMarkup, and converts to the requested format ('markup', 'html', or 'markdown'). uploadMarkup converts the input to markup and persists it with collaborator.createMarkup, returning a MarkupRef. Both throw Error('Unknown content format') for invalid formats. ```ts async fetchMarkup ( objectClass: Ref>, objectId: Ref, objectAttr: string, doc: MarkupRef, format: MarkupFormat ): Promise ``` ```ts async uploadMarkup ( objectClass: Ref>, objectId: Ref, objectAttr: string, value: string, format: MarkupFormat ): Promise ``` -------------------------------- ### searchFulltext method signature Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/rest-client.md Signature of the searchFulltext method. It performs a GET request to /api/v1/search-fulltext/{workspace} with query, classes, spaces, and limit parameters. ```typescript async searchFulltext (query: SearchQuery, options: SearchOptions): Promise ``` -------------------------------- ### Fetch project with ProjectType lookup Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/task-plugin.md Shows how to fetch a project and its project type via lookup, then access the workflow statuses from the lookup result. The comment indicates the type of the statuses array. ```typescript const project = await client.findOne(tracker.class.Project, { identifier: 'HULY' }, { lookup: { type: task.class.ProjectType } }) // project.$lookup?.type.statuses → ProjectStatus[] (each status ref) ``` -------------------------------- ### StorageClient interface Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md Defines the methods for file blob operations: stat, get, put, partial, and remove. Uses Node Readable streams for downloads and uploads. ```typescript export interface StorageClient { stat: (objectName: string) => Promise get: (objectName: string) => Promise put: (objectName: string, stream: Readable | Buffer | string, contentType: string, size?: number) => Promise partial: (objectName: string, offset: number, length?: number) => Promise remove: (objectName: string) => Promise } ``` -------------------------------- ### storage.get Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md Downloads a file from the storage as a readable stream. ```APIDOC ## storage.get ### Description Downloads a file from the storage as a readable stream. ### Method get ### Endpoint storage.get(blobId: string) ### Parameters #### Path Parameters - **blobId** (string) - Required - The identifier of the blob to download. ### Request Example ```ts const stream: Readable = await storage.get(blobId) ``` ### Response #### Success Response Returns a readable stream of the file content. #### Response Example ```ts for await (const chunk of stream) process.stdout.write(chunk) ``` ``` -------------------------------- ### GET /api/v1/find-all/{workspace} Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/endpoints.md Finds documents of a given class within a workspace. The class parameter is required, while query and options are optional JSON-encoded filters and find options. ```APIDOC ## GET /api/v1/find-all/{workspace} ### Description Finds documents of a given class within a workspace. The class parameter is required, while query and options are optional JSON-encoded filters and find options. ### Method GET ### Endpoint /api/v1/find-all/{workspace} ### Parameters #### Path Parameters - **workspace** (string) - Required - The workspace UUID. #### Query Parameters - **class** (string) - Required - The class reference. - **query** (string) - Optional - JSON-encoded DocumentQuery. - **options** (string) - Optional - JSON-encoded FindOptions. ### Response - **200**: FindResult - JSON array of WithLookup; snappy-encoded TotalArray envelopes are unpacked. - **Non-OK**: PlatformError(unknownError(statusText)) or PlatformError(result.error) if payload error field is present. - **429**: Rate limited; waits Retry-After-ms / Retry-After / X-RateLimit-Reset (or 1 s) and retries. Headers X-RateLimit-Limit and X-RateLimit-Remaining are tracked for client-side throttling. ``` -------------------------------- ### createStorageClient function signature Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md Direct constructor using already-resolved URLs. Takes filesUrl, uploadUrl, token, and workspace UUID. ```typescript export function createStorageClient ( filesUrl: string, uploadUrl: string, token: string, workspace: WorkspaceUuid ): StorageClient ``` -------------------------------- ### getModel method signature Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/rest-client.md Signature of the getModel method. It performs a GET request to /api/v1/load-model/{workspace} (appends ?full=true when full is true) and builds a fresh Hierarchy and ModelDb from the response. ```typescript async getModel (full: boolean = false): Promise<{ hierarchy: Hierarchy, model: ModelDb }> ``` -------------------------------- ### Upload and download blob with connectStorage Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/storage.md Connects to a storage server and demonstrates uploading a text blob, retrieving its metadata, reading the full stream, performing a range read, and removing the blob. Uses the @hcengineering/api-client package. ```TypeScript import { connectStorage } from '@hcengineering/api-client' import { Readable } from 'stream' const storage = await connectStorage('http://localhost:8087', { email: 'user1', password: '1234', workspace: 'ws1' }) // Upload const blob = await storage.put('documents/report.txt', 'Hello Huly', 'text/plain') const blobId = blob._id // Stat and read const info = await storage.stat(blobId) console.log(info?.size) // number of bytes const stream: Readable = await storage.get(blobId) for await (const chunk of stream) process.stdout.write(chunk) // Range read const partial = await storage.partial(blobId, 0, 5) // Cleanup await storage.remove(blobId) ``` -------------------------------- ### Inserting an issue after the last one Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/rank.md Example from platform-api/examples/issue-create.ts. Fetches the last issue by rank descending, then adds a new issue with a rank generated by makeRank(lastOne?.rank, undefined) to append after it. ```typescript import { makeRank } from '@hcengineering/rank' // Fetch rank of the last issue to insert the issue after const lastOne = await client.findOne( tracker.class.Issue, { space: project._id }, { sort: { rank: SortingOrder.Descending } } ) await client.addCollection( tracker.class.Issue, project._id, project._id, project._class, 'issues', { title: 'Make coffee', // ... rank: makeRank(lastOne?.rank, undefined) // append after the current last issue }, issueId ) ``` -------------------------------- ### html and markdown factory functions Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/markup.md Convenience constructors for MarkupContent. html('…') is equivalent to new MarkupContent('…', 'html'); markdown('…') to new MarkupContent('…', 'markdown'). ```ts export function html (content: string): MarkupContent export function markdown (content: string): MarkupContent ``` -------------------------------- ### connect Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/api-reference/connect.md Documentation for the `connect` function, the single entry point of the Huly Platform API. ```APIDOC ## connect ### Description Establishes an authenticated session against a Huly deployment and returns a `PlatformClient`. ### Method `connect` ### Signature ```ts export async function connect (url: string, options: ConnectOptions): Promise ``` ### Parameters - `url` (string) - Required - Base URL of the Huly deployment, e.g. `http://localhost:8087`. Used to fetch `{url}/config.json` and to build markup browse/file URLs. - `options` (ConnectOptions) - Required - Authentication (`email`/`password`/`workspace` or `token`/`workspace`) and socket options. ### Return Value `Promise` - A ready-to-use client exposing `findAll`, `findOne`, `createDoc`, `updateDoc`, `addCollection`, markup operations, and more. ### Throws - `Error('Failed to fetch config')` - `{url}/config.json` not reachable or non-OK response. - `Error('Login failed')` - Password auth token is `undefined` after login. - `Error('Workspace ... not found')` - `selectWorkspace` returns `undefined` for the requested workspace. - `PlatformError` - Account-service RPC error. - `Error('The "ws" package is required for NodeWebSocketFactory.')` - `NodeWebSocketFactory` used without the `ws` dependency. - `Error('Connection timeout, and no connection established to ...')` - No connection within `connectionTimeout`. ### Example - basic connection with email/password ```ts import { ConnectOptions, NodeWebSocketFactory, connect } from '@hcengineering/api-client' const url = process.env.HULY_URL ?? 'http://localhost:8087' const options: ConnectOptions = { email: process.env.HULY_EMAIL ?? 'user1', password: process.env.HULY_PASSWORD ?? '1234', workspace: process.env.HULY_WORKSPACE ?? 'ws1', socketFactory: NodeWebSocketFactory, connectionTimeout: 30000 } const client = await connect(url, options) try { const project = await client.findOne(tracker.class.Project, { identifier: 'HULY' }) console.log('project:', project?.identifier) } finally { await client.close() } ``` ### Example - token-based auth and error handling ```ts import { connect, NodeWebSocketFactory } from '@hcengineering/api-client' import contact from '@hcengineering/contact' try { const client = await connect('https://app.huly.app', { token: process.env.HULY_TOKEN!, // obtained via the accounts service, see utils.ts workspace: 'acme', socketFactory: NodeWebSocketFactory, connectionTimeout: 15000 }) try { const persons = await client.findAll(contact.class.Person, {}) console.log('found persons:', persons.length) } finally { await client.close() } } catch (err) { // 'Workspace ... not found' or 'Login failed' or PlatformError land here console.error('cannot connect:', err) process.exit(1) } ``` ``` -------------------------------- ### NotFoundError class definition Source: https://github.com/hcengineering/huly-examples/blob/main/_autodocs/errors.md Defines the NotFoundError class extending StorageError, triggered by HTTP 404 from the files service. Note that StorageClient.stat catches it and returns undefined, while get, partial, and remove propagate it. ```typescript export class NotFoundError extends StorageError { constructor (message = 'Not Found') { super(message); this.name = 'NotFoundError' } } ```