Remove User
removeUser deletes a user document by _id. It belongs to the user model and is the destructive end of the CRUD pair with addUser. In this app it's a hard delete โ reserved for Manager/Admin โ because other models reference users by relation snapshots rather than foreign keys.
The act lives in src/user/removeUser/.
The validator (removeUser.val.ts)โ
set spreads activeRoleMixin and requires _id as an objectIdValidation. get is selectStruct("user", 1) โ the framework projects whatever you ask for onto the deleted document returned by deleteOne.
import { object, objectIdValidation } from "lesan";
import { selectStruct } from "../../../mod.ts";
import { activeRoleMixin } from "@lib";
export const removeUserValidator = () => {
return object({
set: object({
...activeRoleMixin,
_id: objectIdValidation,
}),
get: selectStruct("user", 1),
});
};
Runtime-agnostic imports
"lesan" is this repo's path alias for the framework source. On npm/Bun you'd import from @hemedani/lesan, and on Deno from jsr:@hemedani/lesan. The @lib / ../../../mod.ts aliases stay as they are in your project (see Project Layout).
The registration (mod.ts)โ
Deletion is gated hard: setTokens โ setUser โ grantAccess([{ roles: ["Manager", "Admin"] }]). Unit-level roles and regular employees cannot delete users.
import { grantAccess, setTokens, setUser } from "@lib";
import { coreApp } from "../../../mod.ts";
import { removeUserFn } from "./removeUser.fn.ts";
import { removeUserValidator } from "./removeUser.val.ts";
export const removeUserSetup = () =>
coreApp.acts.setAct({
schema: "user",
actName: "removeUser",
preAct: [setTokens, setUser, grantAccess([{ roles: ["Manager", "Admin"] }])],
validator: removeUserValidator(),
fn: removeUserFn,
});
The implementation (removeUser.fn.ts)โ
Wrap _id in new ObjectId(...) and call user.deleteOne({ filter: { _id } }). If nothing was deleted (the user didn't exist), throw "user not found"; otherwise return the deleted document.
import { type ActFn, ObjectId } from "lesan";
import { user } from "../../../mod.ts";
import { throwError } from "@lib";
export const removeUserFn: ActFn = async (body) => {
const {
set: { _id },
get,
} = body.details;
const removed = await user.deleteOne({
filter: { _id: new ObjectId(_id as string) },
});
!removed && throwError("user not found");
return removed;
};
Note that get is destructured but the delete path doesn't need it โ deleteOne returns the deleted document, and the projection is applied by the ODM to that return value. Deleting a user also triggers Lesan's relation cleanup on any other document that embeds this user, so reverse snapshots don't dangle.
In the workflowโ
removeUser is the delete end of the user CRUD cycle. Before removing a user who owns content, consider whether their relations (e.g. as a head of a unit) make them still necessary โ deletion here removes the user and their embedded snapshots.
- addUser โ the create side.
- getUser โ confirm the user before deleting.
- Auth Utilities โ
grantAccess. - User model โ how other models reference users.
- Overview โ where this series starts.
Run itโ
Needs token, activeRoleId, and the target user's _id:
curl -X POST http://localhost:1380/lesan \
-H "Content-Type: application/json" \
-H "token: <jwt>" \
-d '{
"model": "user",
"act": "removeUser",
"details": {
"set": {
"activeRoleId": "<roleId>",
"_id": "<userId>"
},
"get": {
"_id": 1,
"first_name": 1,
"email": 1
}
}
}'
On success the body is the deleted user document. Deleting the ghost admin is not advised โ it's the seeded superuser the whole workflow depends on.
Errors & fixesโ
| Message | Source | What it means | How to fix |
|---|---|---|---|
you should send your id with token key in req header | setTokens | Missing token header. | Add -H "token: <jwt>". |
Invalid or expired token | setTokens | JWT didn't verify. | Re-login. |
Invalid or missing token data | setUser | Token payload had no _id. | Re-login. |
user not exist | setUser | The token's user was deleted. | Use a different account. |
activeRoleId is required | grantAccess | set.activeRoleId was omitted. | Always send activeRoleId in set. |
Active role not found | grantAccess | The activeRoleId doesn't match any role on the user. | Use a roleId from the login response. |
You cant do this | grantAccess | Active role is not Manager or Admin. | Use a manager/admin role, or the ghost superuser. |
user not found | removeUserFn | No user matches the _id (or it was already deleted). | Check the _id (24-char hex ObjectId); a deleted user simply won't exist anymore. |
| Blocked-delete error (framework error) | ODM | Another document's relation blocks deleting this user. | The relation engine prevents deletion while live back-references exist โ remove those relations first, or adjust the model's delete behavior. |