getProduct
Fetches a single product by its _id, with the full depth-2 projection available (parent, tags). Open to every authenticated role. This is the detail view behind a product listing.
Import alias
The code below imports the framework as lesan โ that's the path alias this repo's app uses (see Project Layout). In your own project, import from @hemedani/lesan (npm/Bun) or jsr:@hemedani/lesan (Deno) instead.
Registration (mod.ts)โ
import { grantAccess, setTokens, setUser } from "@lib";
import { coreApp } from "../../../mod.ts";
import { getProductFn } from "./getProduct.fn.ts";
import { getProductValidator } from "./getProduct.val.ts";
export const getProductSetup = () =>
coreApp.acts.setAct({
schema: "product",
actName: "getProduct",
preAct: [setTokens, setUser, grantAccess([{ roles: ["Manager", "Admin", "OrgHead", "UnitHead", "Employee", "Ordinary"] }])],
validator: getProductValidator(),
fn: getProductFn,
});
The validator (getProduct.val.ts)โ
import { object, objectIdValidation } from "lesan";
import { selectStruct } from "../../../mod.ts";
import { activeRoleMixin } from "@lib";
export const getProductValidator = () => {
return object({
set: object({
...activeRoleMixin,
_id: objectIdValidation,
}),
get: selectStruct("product", 2),
});
};
setisactiveRoleId+_id.getis depth 2, so you can projectparent(itself a product) and thetagsarray (each a tag snapshot).
The implementation (getProduct.fn.ts)โ
import { type ActFn, ObjectId } from "lesan";
import { product } from "../../../mod.ts";
import { throwError } from "@lib";
export const getProductFn: ActFn = async (body) => {
const {
set: { _id },
get,
} = body.details;
const foundedProduct = await product.findOne({
filters: { _id: new ObjectId(_id as string) },
projection: get,
});
!foundedProduct && throwError("product not found");
return foundedProduct;
};
- The string
_idis converted withnew ObjectId(_id). product.findOnereturns the doc shaped by the client'sgetprojection. Because relations are embedded snapshots,parentandtagscome back directly โ no additional queries.!foundedProduct && throwError("product not found")turns a miss into an error.
In the workflowโ
The single-item view used when a purchase order or inventory entry references a product. Pair it with getProducts for lists, and updateProductRelations for adjusting its relations.
- product model
- Sibling acts: addProduct, getProducts, removeProduct, updateProductRelations
Run itโ
curl http://localhost:1380/lesan \
-H "Content-Type: application/json" \
-H "token: <jwt>" \
-d '{
"model": "product",
"act": "getProduct",
"details": {
"set": { "activeRoleId": "ghost-role", "_id": "<productId>" },
"get": { "_id": 1, "name": 1, "price": 1, "parent": { "_id": 1, "name": 1 }, "tags": { "_id": 1, "name": 1 } }
}
}'
Errors & fixesโ
| Error | Meaning | Fix |
|---|---|---|
product not found | No product has that _id. | Check the id โ get it from getProducts. |
activeRoleId is required | No activeRoleId in set. | Add it (ghost: any string, e.g. "ghost-role"). |
Active role not found | activeRoleId isn't one of the user's roles. | Pass a real roleId. |
You cant do this | Active role not in the allowed list. | Use an allowed role or the ghost. |
Invalid or expired token / you should send your id with token key in req header | Auth header problem. | Send token: <jwt>; re-login if expired. |
| superstruct "expected ObjectId-like string" | _id isn't a valid ObjectId. | Send the 24-hex id string. |