Conversation
🚀 Package Preview Available!Install this PR's preview build with npm: npm i @base44-preview/cli@0.0.47-pr.406.4b9c508Prefer not to change any import paths? Install using npm alias so your code still imports npm i "base44@npm:@base44-preview/cli@0.0.47-pr.406.4b9c508"Or add it to your {
"dependencies": {
"base44": "npm:@base44-preview/cli@0.0.47-pr.406.4b9c508"
}
}
Preview published to npm registry — try new features instantly! |
04e8a12 to
f8afa4a
Compare
11c2912 to
f7b1fa4
Compare
| /** @public - called by Commander internally via command dispatch */ | ||
| override action( | ||
| // biome-ignore lint/suspicious/noExplicitAny: must match Commander.js action() signature | ||
| // biome-ignore lint/suspicious/noConfusingVoidType: must match Commander.js action() signature |
There was a problem hiding this comment.
Not related to my PR, but started to fail for me.
Ignoring
| const result = validator.validate({ name: "Alice" }, schema); | ||
|
|
||
| expect(result.hasError).toBe(false); | ||
| expect(() => validator.validate({ name: "Alice" }, schema)).not.toThrow(); |
There was a problem hiding this comment.
Validator is now throwing an error, instead of returning object that describes the error.
Updating tests to handle it.
Here and below.
| ); | ||
| if (requiredFieldsResponse.hasError) { | ||
| return requiredFieldsResponse; | ||
| throw new EntityValidationError(requiredFieldsResponse.error); |
There was a problem hiding this comment.
I found that it's more convenient to throw the error, rather than return an object that describes the error.
So I decided to refactor it a bit.
44675f2 to
2d12f4c
Compare
| private normalizeName(entityName: string): string { | ||
| return entityName.toLowerCase(); | ||
| } |
There was a problem hiding this comment.
Behind the scenes production is treating entity name as case-insensitive: "User" and "user" are the same.
kfirstri
left a comment
There was a problem hiding this comment.
Looks good, i'm not sure i understand some stuff in the entities-user-router..
Also, can we resolve the user with the auth token and not with readAuth somehow? I see we added a check to assert the api calls has a Bearer.. so maybe we can parse the token?
| return; | ||
| } | ||
| next(); | ||
| }); |
There was a problem hiding this comment.
So every request to the dev-server must have an authorization header. Is this also how the apper/ backend works? It won't allow anonymous calls at all?
There was a problem hiding this comment.
Right, this is overkill here. Currently it's needed only for /User
| let result: Record<string, unknown> | undefined; | ||
|
|
||
| if (req.params.id === "me") { | ||
| const userInfo = await readAuth(); |
There was a problem hiding this comment.
Wont the request at this point will have a authorization token that we can read some user data from? then we don't need the readAuth call
| const now = getNowISOTimestamp(); | ||
|
|
||
| // Production is not allowing to add user entity directly. | ||
| // In case developer tries to do it - backend silently fails. |
There was a problem hiding this comment.
I don't understand this whole POST / endpoint .. we're validating the developing (since we're using readAuth), and if we found the developer account we just return an object, but not really save it? so basically it's just a mock.. but production also does not allow it?
There was a problem hiding this comment.
This is exactly how productions behaves.
It returns updated object, while not updating Entries list.
| router.post("/bulk", async (_req, res) => { | ||
| // not supported in direct call: NO-OP | ||
| res.json({}); | ||
| }); |
There was a problem hiding this comment.
Is this a noop endpoint because we just don't want to support it? can't we just delete it and it will return 404?
There was a problem hiding this comment.
This is not how production behaves - in production it's no-op
| const userInfo = await readAuth(); | ||
| const collection = db.getCollection(USER_COLLECTION); | ||
| const userRecord = await collection?.findOneAsync({ | ||
| email: userInfo.email, | ||
| }); |
There was a problem hiding this comment.
If this is really how we will get the developer account (raising it again - do we have it as an auth header maybe?), then this is the 3rd time we're doing this in this file, so maybe refactor to another function
| return; | ||
| } | ||
| logger.error( | ||
| `Error in PUT /${USER_COLLECTION}/${req.params.id}:`, |
There was a problem hiding this comment.
This error looks weird, no need to use USER_COLLECTION inside the url that we're printing
There was a problem hiding this comment.
Why? Can you elaborate, what is weird about it?
Can you give example of an error that you have in mind?
| res.status(404).json({ error: `User record not found` }); | ||
| } | ||
| } else { | ||
| res.json({}); |
There was a problem hiding this comment.
IF the user can only update himself and not others, shouldn't we return some errors? this is how apper behaves? :S
There was a problem hiding this comment.
Right. Also admin is able to update any user. So I will remove this part of the statement.
| router.delete("/:id", async (_req, res) => { | ||
| // not supported in direct call: NO-OP | ||
| res.json({}); | ||
| }); |
There was a problem hiding this comment.
same question like in /bulk route, maybe we can just remove it? what does "direct call" mean?
There was a problem hiding this comment.
| return { | ||
| name: "User", | ||
| type: "object", | ||
| properties: { ...builtInFields, role: { type: "string" } }, |
There was a problem hiding this comment.
"role" is also a built-in field https://docs.base44.com/developers/backend/resources/entities/user-schema#built-in-fields
There was a problem hiding this comment.
Yes, I know, but this field is not validated like full_name and email and error is not thrown when user tries to override it
2d12f4c to
3439aa4
Compare
Note
Description
This PR adds local User entity support to the
base44 devserver, replacing the previous behavior of proxying all/Userrequests to production. The current authenticated user is automatically seeded into an in-memory User collection on startup, and a new JWT-aware router handles CRUD operations for the User entity locally. Entity names are now normalized to lowercase for consistent lookup.Related Issue
None
Type of Change
Changes Made
entities-user-router.tsthat handlesGET /User/:id(includingme),POST /User/,PUT /User/:idwith JWT-based auth via awithAuthwrapperDatabase.load()is now async and automatically seeds aUsercollection with the current authenticated user's info (fromreadAuth())buildUserSchema()to merge built-in User fields (full_name,email) with any custom User entity schema, and validates that built-in fields are not overriddenentities.ts→routes/entities/entities-router.tsand removed theremoteProxyparameter (no longer needed for User routes)stripInternalFieldsutility toutils.tsand entity name normalization (.toLowerCase()) added toDatabaseValidatorto throwEntityValidationErrorinstead of returning a validation response object; callers updated to catch and return HTTP 422jsonwebtokendependency for decoding Bearer tokens in the local dev serverTesting
npm test)Checklist
docs/(AGENTS.md) if I made architectural changesAdditional Notes
The User collection is pre-populated with the logged-in developer's info (email, name, role=admin) so that local apps behave similarly to production when reading the current user. Restricted fields (
full_name,email,role) cannot be updated via the entities API, mirroring production backend constraints. ThePOST /User/bulkendpoint is a no-op, consistent with production behavior.🤖 Generated by Claude | 2026-03-22 00:00 UTC