Global App Search

Six back-office methods that operate across every user of your app: list them, search them, and reach all of their records at once. All are scoped by the configured appId.

Secret key only — server side

getAllUsersFromApp returns your user table. These routes are account-level and require a secret key; called with a publishable key they return InsufficientScopeError, so a decompiled app cannot enumerate your users.

That protection only holds if the secret key never leaves your server. Never pass one through --dart-define in a shipped build — defines are compiled into the binary in plain text.

Why these are POST requests

All six are reads, but they are sent as POST because the backend expects the filters in a request body. This matches the JavaScript and Python SDKs exactly, so every client talks to the API identically.

Methods

getAllUsersFromApp

POST /app/global/:appId/all-app-users

Future<Page<T>> getAllUsersFromApp<T>(GetAllAppUsersParams params, {ItemParser<T>? itemParser})
ParamTypeRequiredDescription
limitint?NoPage size. Defaults to 20, capped at 100 — a larger value is clamped, not rejected.
cursorString?NonextCursor from the previous page.
itemParserItemParser<T>?NoMaps each user into your own model.

Returns: Page<T>

Dart
1final page = await authix.getAllUsersFromApp(
2  const GetAllAppUsersParams(limit: 50),
3);
4
5page.length;   // users in this page
6page.hasMore;  // true when more exist
7
8for (final user in page) {
9  print(user['email']);
10}

Newest first. Omitted arguments are left out of the body entirely rather than sent as null.

iterateAllUsers

POST /app/global/:appId/all-app-users (repeated)

Stream<T> iterateAllUsers<T>(GetAllAppUsersParams params, {ItemParser<T>? itemParser, int? maxPages})
ParamTypeRequiredDescription
paramsGetAllAppUsersParamsYesPage size. Any cursor you set is ignored — the traversal manages its own.
itemParserItemParser<T>?NoMaps each user into your own model.
maxPagesint?NoStop after this many pages. Use it — a full sweep of a large app is a lot of requests.

Returns: Stream<T>

Dart
1await for (final user in authix.iterateAllUsers(
2  const GetAllAppUsersParams(limit: 100),
3  maxPages: 50,
4)) {
5  print(user['email']);
6}

searchInAllAppUsers

POST /app/global/:appId/all-app-users/search

Future<Page<T>> searchInAllAppUsers<T>(SearchAllAppUsersParams params, {ItemParser<T>? itemParser})
ParamTypeRequiredDescription
qString?NoSubstring match across user fields.
keysMap<String, dynamic>?NoExact field match. Restricted to the allowlist below.
limitint?NoPage size. Defaults to 20, capped at 100.
cursorString?NonextCursor from the previous page.

Returns: Page<T>

Dart
1// Substring match across all users
2final matches = await authix.searchInAllAppUsers(
3  const SearchAllAppUsersParams(q: 'john'),
4);
5
6// Exact field match
7final admins = await authix.searchInAllAppUsers(
8  const SearchAllAppUsersParams(
9    keys: {'role': 'admin', 'isActive': true},
10  ),
11);

Throws: ValidationError when keys names a field outside the allowlist.

Only id, username, name, email, phone, address, role, isVerified and isActive may be filtered on. Credential columns are deliberately unreachable — filtering on 'password' is rejected, not silently ignored.

getAllUsersDataFromApp

POST /app/global/:appId/all-app-users-data

Future<Page<T>> getAllUsersDataFromApp<T>(GetAllAppUsersDataParams params, {ItemParser<T>? itemParser})
ParamTypeRequiredDescription
categoryString?NoRestrict to one data category.
limitint?NoPage size. Defaults to 20, capped at 100.
cursorString?NonextCursor from the previous page.
itemParserItemParser<T>?NoMaps each record into your own model.

Returns: Page<T>

Dart
1final page = await authix.getAllUsersDataFromApp(
2  const GetAllAppUsersDataParams(category: 'orders', limit: 100),
3);
4
5print('${page.length} orders in this page');

Reaches across users — the same records getUserData returns one user at a time.

searchInAllAppUsersData

POST /app/global/:appId/all-app-users-data/search

Future<Page<T>> searchInAllAppUsersData<T>(SearchAllAppUsersDataParams params, {ItemParser<T>? itemParser})
ParamTypeRequiredDescription
qString?NoSubstring match across the whole record.
categoryString?NoRestrict to one data category.
keysMap<String, dynamic>?NoExact field match, e.g. {'status': 'pending'}.
limitint?NoPage size. Defaults to 20, capped at 100.
cursorString?NonextCursor from the previous page.

Returns: Page<T>

Dart
1final pending = await authix.searchInAllAppUsersData(
2  const SearchAllAppUsersDataParams(
3    category: 'orders',
4    keys: {'status': 'pending'},
5  ),
6);

The cross-user counterpart of searchInUserData. keys becomes a jsonb containment query and q a trigram substring match, both index-backed in Postgres — nothing is scanned in Node or on the device.

iterateAllUsersData

POST /app/global/:appId/all-app-users-data[/search] (repeated)

Stream<T> iterateAllUsersData<T>({String? category, String? q, Map<String, dynamic>? keys, int? limit, ItemParser<T>? itemParser, int? maxPages})
ParamTypeRequiredDescription
categoryString?NoRestrict to one data category.
qString?NoSubstring match. Supplying it switches to the search route.
keysMap<String, dynamic>?NoExact field match. Supplying it switches to the search route.
limitint?NoPage size.
maxPagesint?NoStop after this many pages.

Returns: Stream<T>

Dart
1// Lists or searches depending on the arguments you give it.
2await for (final record in authix.iterateAllUsersData(
3  category: 'orders',
4  keys: {'status': 'pending'},
5  maxPages: 20,
6)) {
7  print(record['id']);
8}

Picks the list or the search route for you depending on whether q or keys is given, then follows the cursor.

Paginating

Every method here returns the same Page as getUserData. There is no unbounded mode: 20 records by default, 100 maximum.

MemberTypeMeaning
lengthintRecords in this page.
hasMoreboolWhether another page exists.
nextCursorString?Pass back as cursor for the next page.
totalintRecords in this page as the API reported it — not how many exist overall.
dataList<T>The records — raw maps unless itemParser is given. Iterating the page yields these directly.
rawMap<String, dynamic>The undecoded response body.
Dart
1// One page at a time
2var page = await authix.getAllUsersFromApp(
3  const GetAllAppUsersParams(limit: 100),
4);
5
6while (page.hasMore) {
7  page = await authix.getAllUsersFromApp(
8    GetAllAppUsersParams(limit: 100, cursor: page.nextCursor),
9  );
10  process(page);
11}
12
13// Or stream every user, following the cursor for you
14await for (final user in authix.iterateAllUsers(
15  const GetAllAppUsersParams(limit: 100),
16)) {
17  print(user['email']);
18}

Typed results

Dart
1class AppUser {
2  final String id;
3  final String email;
4
5  const AppUser({required this.id, required this.email});
6
7  factory AppUser.fromJson(Map<String, dynamic> json) => AppUser(
8        id: json['id']?.toString() ?? '',
9        email: json['email']?.toString() ?? '',
10      );
11}
12
13final page = await authix.getAllUsersFromApp<AppUser>(
14  const GetAllAppUsersParams(limit: 50),
15  itemParser: AppUser.fromJson,
16);
17
18for (final user in page) {   // a Page is an Iterable of its records
19  print(user.email);
20}

Rate and volume

These endpoints reach across your whole dataset. Prefer a search with keys over a full sweep, bound every traversal with maxPages, and cache on your side — a full sweep on every launch will burn through your request quota.

Related