Search from your backend
This guide shows a search from your backend with the Node client, page by page. It is for backend developers building a search box or a filtered list; what search matches is on Search, every parameter on Search query.
files:managefiles:readA search, page by page
Section titled “A search, page by page”import type { SearchHit } from '@central-code-station/ccs-files-client';import { files } from './files-client';
// Every PDF tagged 2026 under a folder and its subfolders, newest first, page by page.export async function brochures(folderId: string): Promise<SearchHit[]> { const hits: SearchHit[] = []; let after: string | undefined; do { const page = await files.search({ q: 'brochure', // whole words (name, title, description, tags, custom values); else names starting with it in: ['name', 'keywords'], kind: ['pdf'], tags: ['2026'], folder_id: folderId, recursive: true, custom: { campaign: 'spring' }, // exact match on your own metadata sort: 'updated_at', order: 'desc', after, limit: 100, }); hits.push(...page.items); after = page.next_cursor ?? undefined; } while (after); return hits;}files.search(query) sends GET P/search. List parameters (kind, tags, status, …) take arrays; custom takes an object and becomes custom.<key>=<value>. The answer is { items, next_cursor, total, provider }; each item is a file, with highlights when q matched.
Searching inside documents
Section titled “Searching inside documents”Add content to in to match the text of PDFs, Office and OpenDocument files, text, HTML and JSON files; each hit that matched there carries a content highlight. See Searching inside documents.
Searching for one user
Section titled “Searching for one user”Your machine client searches the whole application. To show a user only what they may see, either:
- let the user’s app call search with the user’s own token (User tokens): the service applies the user’s permissions inside the query, so paging and
totalare exact; or - filter the hits in your backend with
files.files.effectivePermissionsBatch(userId, ids)before answering. Paging is then yours to manage.
Tags and managed lists
Section titled “Tags and managed lists”const { items } = await files.tags({ prefix: 'be', limit: 10 }); // [{ tag: 'beach', count: 42 }, ...]const departments = await files.taxonomies.get('departments'); // { values: [{ value, label, active }], enforced }GET P/tags answers the application’s tags with their counts, for autocomplete. GET P/taxonomies/:name answers the managed list of categories or departments; setting one needs files:config.manage (PUT P/taxonomies/:name).
Errors
Section titled “Errors”| Code | Cause |
|---|---|
invalid_request |
An unknown sort, sort=relevance without q, more than five custom. filters, an in other than name, keywords, content |
invalid_cursor |
after is not the next_cursor of the same search |
too_many_scopes |
A user token whose access spans too many folders: add folder_id |
rate_limited |
More than 120 per minute searches |