Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/TheSerchCp/SEAM-API/llms.txt

Use this file to discover all available pages before exploring further.

SEAM API uses a database-driven Role-Based Access Control (RBAC) model. Every authenticated user carries a roleId in their JWT payload. Before a protected controller runs, roles.middleware.js looks up whether that role has been granted permission to call the exact route being requested. If no matching permission is found, the request is rejected with a 403 Forbidden error — even if the token itself is valid.

How the permission model works

Permissions are stored in a permissions table. Each permission has a nameUri column that identifies the route it covers. Roles are stored in a roles table, and the many-to-many relationship between roles and permissions lives in permissionXRole. When a request arrives, roles.middleware.js constructs a nameUri string from the HTTP method and the Express route pattern, then queries the database to see whether the user’s role has been granted that URI:
// roles.middleware.js
const routePath = req.route.path === '/' ? '' : req.route.path;
const nameUri = `${req.method} ${req.baseUrl}${routePath}`;

const [rows] = await pool.query(
  `SELECT 1
   FROM permissionXRole pr
   INNER JOIN permissions p ON pr.permissionId = p.idPermission
   WHERE pr.roleId = ?
     AND LOWER(p.nameUri) = LOWER(?)
   LIMIT 1`,
  [roleId, nameUri],
);

if (rows.length === 0) {
  return next(new ForbiddenError('No tienes permisos para acceder a este recurso'));
}
The comparison uses LOWER() on both sides, so nameUri values are case-insensitive. A route-root path (/) is treated as an empty string to avoid double slashes (e.g., /api/v1/users instead of /api/v1/users/).

The nameUri pattern format

The nameUri is always METHOD /api/v1/<module>[/:param]. It must match exactly what the middleware constructs at runtime — including the param placeholder (e.g., :id), not an actual ID value.
RouteConstructed nameUri
GET /api/v1/usersGET /api/v1/users
GET /api/v1/users/:idGET /api/v1/users/:id
PUT /api/v1/users/:idPUT /api/v1/users/:id
DELETE /api/v1/users/:idDELETE /api/v1/users/:id
POST /api/v1/rolesPOST /api/v1/roles
PUT /api/v1/roles/:idRolePUT /api/v1/roles/:idRole
DELETE /api/v1/roles/:idRoleDELETE /api/v1/roles/:idRole
POST /api/v1/permission/registerPOST /api/v1/permission/register
POST /api/v1/permission/assignPOST /api/v1/permission/assign
DELETE /api/v1/permission/unassignDELETE /api/v1/permission/unassign
DELETE /api/v1/permission/:idDELETE /api/v1/permission/:id
POST /api/v1/sidebarPOST /api/v1/sidebar
PUT /api/v1/sidebar/:idItemPUT /api/v1/sidebar/:idItem
DELETE /api/v1/sidebar/:idItemDELETE /api/v1/sidebar/:idItem
POST /api/v1/sidebar/:idItem/role/:idRolePOST /api/v1/sidebar/:idItem/role/:idRole

Auth-only vs. auth + roles routes

Not every protected route requires a permission check. Some read-only or utility routes only validate the JWT — they skip roles.middleware.js entirely. All mutation routes (create, update, delete, assign) require both.
RouteMiddleware chain
GET /api/v1/rolesauth only
GET /api/v1/permissionauth only
GET /api/v1/permission/getByRoleIdauth only
GET /api/v1/permission/getByNameUriauth only
All other protected routesauthroles
The middleware chain is wired at the route level:
// auth only — useful for populating dropdowns in any authenticated UI
router.get('/', auth, rolesController.getAllRoles);

// auth + roles — mutation requires an explicit permission
router.post('/', auth, roles, validate(addRoleSchema), rolesController.addRole);
router.put('/:idRole', auth, roles, validate(idRoleSchema, 'params'), rolesController.editRole);
router.delete('/:idRole', auth, roles, validate(idRoleSchema, 'params'), rolesController.deleteRole);

Setting up permissions for a new role

1

Create the role

Call POST /api/v1/roles with a roleName and optional description. Save the idRole returned in the response.
curl -X POST http://localhost:3000/api/v1/roles \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"roleName": "editor", "description": "Can manage content"}'
2

Register each permission

For every route the new role should be allowed to call, register a permission record using POST /api/v1/permission/register. The nameUri must match the pattern shown in the table above exactly.
curl -X POST http://localhost:3000/api/v1/permission/register \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "nameUri": "GET /api/v1/users",
    "description": "List all users"
  }'
Repeat this step for each route the role needs (PUT /api/v1/users/:id, DELETE /api/v1/users/:id, etc.). Save the idPermission returned from each call.
3

Assign permissions to the role

Link each permission to the role using POST /api/v1/permission/assign. Supply the roleId and the permissionId together.
curl -X POST http://localhost:3000/api/v1/permission/assign \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"roleId": 3, "permissionId": 7}'
Repeat this step for every permission you want to grant to the role.
4

Verify access

Log in as a user with the new role and attempt to call one of the assigned routes. The middleware will query permissionXRole and allow the request through. Attempting an unassigned route returns 403 Forbidden.

Removing a permission from a role

To revoke access without deleting the permission record, call DELETE /api/v1/permission/unassign with the same roleId and permissionId pair used when assigning:
curl -X DELETE http://localhost:3000/api/v1/permission/unassign \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"roleId": 3, "permissionId": 7}'
Permission checks are always evaluated live against the database. There is no in-memory cache. Changes to permissionXRole take effect on the very next request — no server restart is required.

Build docs developers (and LLMs) love