Skip to main content

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),
});
};
note

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.

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โ€‹

MessageSourceWhat it meansHow to fix
you should send your id with token key in req headersetTokensMissing token header.Add -H "token: <jwt>".
Invalid or expired tokensetTokensJWT didn't verify.Re-login.
Invalid or missing token datasetUserToken payload had no _id.Re-login.
user not existsetUserThe token's user was deleted.Use a different account.
activeRoleId is requiredgrantAccessset.activeRoleId was omitted.Always send activeRoleId in set.
Active role not foundgrantAccessThe activeRoleId doesn't match any role on the user.Use a roleId from the login response.
You cant do thisgrantAccessActive role is not Manager or Admin.Use a manager/admin role, or the ghost superuser.
user not foundremoveUserFnNo 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)ODMAnother 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.