Conversation
Add `base44 entities records <list|get|create|update|delete>` commands
for managing entity records via the CLI. Uses the new admin entities
router (/api/apps/{app_id}/admin/entities) which authenticates via
AppAdminRouter and bypasses RLS.
Includes:
- Core API layer (records-api.ts, records-schema.ts)
- CLI commands for list, get, create, update, delete
- Refactored entities command to support subcommands (push + records)
- Integration tests and API mocks
Co-authored-by: Cursor <cursoragent@cursor.com>
e553740 to
3bd687f
Compare
🚀 Package Preview Available!Install this PR's preview build with npm: npm i @base44-preview/cli@0.0.34-pr.259.6bdf9f0Prefer not to change any import paths? Install using npm alias so your code still imports npm i "base44@npm:@base44-preview/cli@0.0.34-pr.259.6bdf9f0"Or add it to your {
"dependencies": {
"base44": "npm:@base44-preview/cli@0.0.34-pr.259.6bdf9f0"
}
}
Preview published to npm registry — try new features instantly! |
|
README check ran. Found discrepancies and attempted to update README.md, but edits require approval. Issues found: (1) Missing commands: eject, connectors pull/push, entities records, types generate; (2) Outdated descriptions for create, deploy, link, logout, whoami, agents pull/push. Manual update needed. |
- Add KyResponse type annotation to let response declarations - Remove unused export from ListRecordsOptions interface - Replace console.log with process.stdout.write for JSON output so test framework's stdout spy can capture it - Apply Biome formatting fixes Co-authored-by: Cursor <cursoragent@cursor.com>
|
README check ran. 7 issue(s) found and applied: updated create/deploy/link/logout/whoami/agents descriptions to match CLI code, added eject command, added connectors pull/push commands, added entities records command, added types generate command. README.md has been updated in this branch. |
Add eject, connectors pull/push, entities records CRUD, and types generate. Fix descriptions to match actual CLI --help output. Co-authored-by: Cursor <cursoragent@cursor.com>
| updated_date: z.string(), | ||
| created_by: z.string().optional(), | ||
| }) | ||
| .passthrough(); |
There was a problem hiding this comment.
use looseObject, passthough is obsolete in zod4
| } | ||
|
|
||
| if (options.file) { | ||
| const content = await readTextFile(options.file); |
There was a problem hiding this comment.
we have a readJsonFile you can also use
| async function parseRecordData( | ||
| options: CreateRecordCommandOptions, | ||
| ): Promise<Record<string, unknown>> { | ||
| if (options.data) { |
There was a problem hiding this comment.
maybe we can add a hook to the commander to check wether --data or --file but not both? currently if a user passes both just the --data will actually be taken into consideration
| export function getRecordsGetCommand(context: CLIContext): Command { | ||
| return new Command("get") | ||
| .description("Get a single entity record by ID") | ||
| .argument("<entity-name>", "Name of the entity (e.g. Users, Products)") |
There was a problem hiding this comment.
Do we want any validation for this thing? I guess the backend will throw if entity does not exists
| file?: string; | ||
| } | ||
|
|
||
| async function parseUpdateData( |
There was a problem hiding this comment.
I see we use this both here and in create.ts , maybe it's worth extracting? (notice we also need to use the readJsonFile method)
| log.success(`Record created with ID: ${record.id}`); | ||
| process.stdout.write(`${JSON.stringify(record, null, 2)}\n`); |
There was a problem hiding this comment.
Let's use normal logs and stuff
you should do
return { outroMessage: `Record created with ID: ${record.id}`}
to make it end nicely.
and also lets just log the JSON with the log.success or log.info or soemthing.
This is relevant for all the new commands
| return new Command("list") | ||
| .description("List entity records") | ||
| .argument("<entity-name>", "Name of the entity (e.g. Users, Products)") | ||
| .option("-f, --filter <json>", "JSON query filter") |
There was a problem hiding this comment.
maybe we need to give a bit mor information here. we also need this to be in the base44 cli SKILL so it will have examples, but lets just say what is expected?
|
Claude finished @kfirstri's task in 5m 43s —— View job Review Changes Applied
Summary of changes (commit
|
…k output patterns - Replace z.object().passthrough() with z.looseObject() in records-schema.ts (zod4 compat) - Extract shared parseRecordData() utility used by both create and update commands - Uses readJsonFile() for --file (handles JSON5, FileNotFoundError, ConfigInvalidError) - Adds mutual exclusion check: cannot use both --data and --file simultaneously - Replace process.stdout.write with log.info() for JSON output across all record commands - Move success messages to outroMessage return value (create, update, list, delete) - Improve --filter, --sort, --skip, --fields help text in list command with examples - Update test assertions to match new "Found N <entity> record(s)" outroMessage format Co-authored-by: Kfir Stri <kfirstri@users.noreply.github.com>
|
|
||
| export async function parseRecordData( | ||
| options: RecordDataOptions, | ||
| exampleHint: string, |
There was a problem hiding this comment.
@claude lets make this optional and only use in the exception if available
There was a problem hiding this comment.
Claude finished @kfirstri's task in 1m 55s —— View job
Task Complete
- Read
parseRecordData.tsto understand what needs to be made optional - Make the change (
exampleHintis now optional, only used in error hints when provided) - Run typecheck and tests (all 172 pass)
- Commit and push
Summary
Made exampleHint optional in parseRecordData.ts (exampleHint?: string). The two error throws that use it now conditionally include the hints only when exampleHint is provided:
// Before
throw new InvalidInputError("Invalid JSON in --data flag.", {
hints: [{ message: `Example: --data '${exampleHint}'` }],
});
// After
throw new InvalidInputError(
"Invalid JSON in --data flag.",
exampleHint
? { hints: [{ message: `Example: --data '${exampleHint}'` }] }
: undefined,
);Same pattern applied to the "Provide record data with --data or --file flag" error. Callers (create.ts, update.ts) continue to pass their example hints as before — no changes needed there.
… if provided Co-authored-by: Kfir Stri <kfirstri@users.noreply.github.com>
|
This PR is ready and waiting for backend changes to be merged first |
Exchange the platform token for an app-user token via the existing GET /auth/token endpoint, then call the standard RuntimeRouter entity endpoints with X-Bypass-RLS instead of the custom admin entities router. This eliminates the need for backend changes (admin_entities_router.py) and gives the CLI access to the full RuntimeRouter surface for future features.
|
@netanelgilad @kfirstri — heads up on the latest changes: Following Netanel's suggestion, I refactored the entity records API to use a token exchange approach instead of the custom admin entities router in the backend:
This means no backend changes are needed — the apper PR #3852 has been closed. The CLI commands and their behavior remain exactly the same; only the internal auth/routing layer changed. Changed files:
All tests pass. Ready for review! |
# Conflicts: # tests/cli/testkit/Base44APIMock.ts
Note
Description
This PR adds full CRUD support for entity records via a new
entities recordssubcommand group. Users can now list, get, create, update, and delete records for any entity directly from the CLI, authenticated via a token exchange from the platform token to an app-user token with RLS bypass headers for admin-level access.The
entities pushcommand was also refactored: previously it was registered as the top-levelentitiescommand; it is now a proper subcommand under a newentitiesparent, enabling therecordssubcommand to be added alongside it.Related Issue
None
Type of Change
Changes Made
entities records listcommand with--filter,--sort,--limit,--skip, and--fieldsoptionsentities records getcommand to fetch a single record by IDentities records createcommand accepting record data via--data(inline JSON/JSONC) or--fileentities records updatecommand accepting update fields via--dataor--fileentities records deletecommand with an interactive confirmation prompt and--yesflag to skip itgetAppUserClient()inbase44-client.tsthat exchanges the platform token for an app-user token and attachesX-Bypass-RLS/X-Bypass-Entities-Filter-Limitheadersrecords-api.tswith corelistRecords,getRecord,createRecord,updateRecord,deleteRecordfunctions with Zod validation on all responsesrecords-schema.tswithEntityRecordSchema(loose object) andDeleteRecordResponseSchemaentities pushfrom a top-level command into a subcommand under a newentitiesparent command groupBase44APIMockwith helpers for mocking all record endpoints and the token-exchange endpointtoContainInStdoutassertion helper toCLIResultMatcherentities recordscommandsTesting
npm test)Checklist
docs/(AGENTS.md) if I made architectural changesAdditional Notes
The
deletecommand usesCLIExitError(0)for a clean exit when the user cancels the confirmation prompt, consistent with the project's error handling patterns. TheEntityRecordSchemausesz.looseObjectto allow arbitrary dynamic data fields beyond the required system fields (id,created_date,updated_date,created_by).🤖 Generated by Claude | 2026-02-23 00:00 UTC