Skip to content

Backend

FastAPI backend for Nexus by McGuire Technology.

Development

sh
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
npm run backend:dev

The API will be available at http://127.0.0.1:8000.

Architecture

The backend follows a hexagonal layout:

  • backend/app/main.py is the FastAPI composition root.
  • backend/app/api/ contains the HTTP API composition router and focused FastAPI route modules.
  • backend/app/application/ contains use-case services such as authentication and tenancy rules.
  • backend/app/domain/ contains request and response schemas used by the API boundary.
  • backend/app/infrastructure/ contains infrastructure adapters, including SQL session helpers and row mappers.
  • backend/app/core/ contains runtime configuration and platform constants.

Route handlers should stay thin and delegate business rules to application modules. Domain-specific API modules should live under backend/app/api/ and be included by the HTTP composition router. Database access helpers and SQL row mapping should stay behind infrastructure so future persistence changes do not leak into the HTTP layer.

System, project, education, asset, census, configuration, and location endpoints are split into focused backend/app/api/ modules. backend/app/api/router.py is now a composition router that includes those domain-specific route adapters.

Project workflows live in backend/app/application/projects.py. Asset workflows such as manufacturers, asset types, asset models, assets, assignments, and circulations now live in backend/app/application/assets/. Issue workflows live in backend/app/application/service.py, with asset repairs implemented as a project issue subtype that keeps asset-specific fields in the asset area. HTTP routes pass simple boundary inputs such as the current user and selected tenant filter into those use cases instead of resolving tenant scope, enforcing asset rules, and writing SQL inline.

Asset API routes live under backend/app/api/assets/, including manufacturers, asset types, asset models, inventory, assignments, circulations, and repair subtype compatibility. Configuration item API routes live under backend/app/api/configurations/. Project, issue, and project-scoped /me API routes live under backend/app/api/projects/.

Person and identity workflows now live in backend/app/application/census.py with matching route adapters under backend/app/api/census/. Location workflows live in backend/app/application/locations.py with route adapters in backend/app/api/.

Education workflows now live in backend/app/application/schools/ with matching entity-focused route adapters under backend/app/api/education/.

Tenant workflows now live in backend/app/application/tenants.py with matching route adapters and tenant-scoped /me routes under backend/app/api/system/.

User, group, membership, auth, session, preference, and user-scoped /me workflows are System module concerns. Their application services live in backend/app/application/users.py and related auth/tenant application services with matching route adapters under backend/app/api/system/.

Auth concerns now live in backend/app/application/auth/ and tenancy helpers now live in backend/app/application/tenancy/. SQL row mappers are split by domain under backend/app/infrastructure/mappers/.

Docker

The backend can also run behind Traefik in the Compose stack:

sh
docker compose up --build backend postgres traefik

By default, the API is routed at http://api.localhost.

The container receives DATABASE_URL from Compose and waits for PostgreSQL to become healthy before starting. The default local connection string is:

txt
postgresql://nexus:nexus_dev_password@postgres:5432/nexus

Database Migrations

The backend uses Alembic for database migrations. The backend Docker container runs migrations automatically before starting the API:

sh
alembic upgrade head

For local development, set DATABASE_URL and run the same command before starting the backend.

You can also run migrations through npm:

sh
npm run backend:migrate

System

System is a Nexus module. The backend uses OAuth2 password bearer tokens. Users can sign in with either their username or email address plus a password. User IDs and token IDs are ULIDs, and passwords are hashed with Argon2id before they are stored. Users are global accounts; tenant access comes from group memberships, with current and default tenant selection stored in user_sessions and user_preferences.

Default API routes:

  • POST /me/signup creates a user and returns a bearer token.
  • POST /me/signin signs in with OAuth2 form fields username and password.
  • POST /me/signout revokes the current bearer token.
  • GET /me returns the authenticated user.
  • GET /me/tenants returns tenants available to the authenticated user.
  • GET /me/groups returns the authenticated user's current-tenant groups.
  • GET /me/projects returns projects available in the authenticated user's current tenant context.
  • GET /me/current-project returns the authenticated user's selected project.
  • POST /me/current-project changes the authenticated user's selected project.
  • GET /me/issues returns issues for the authenticated user's selected project.
  • GET /me/sessions returns the authenticated user's active sessions.
  • DELETE /me/sessions/{session_id} deletes and revokes one of the authenticated user's sessions.
  • GET /me/current-tenant returns the authenticated user's selected tenant.
  • POST /me/current-tenant changes the authenticated user's selected tenant.
  • GET /me/preferences returns the authenticated user's preferences.
  • PATCH /me/preferences updates the authenticated user's preferences.
  • GET /tenants returns tenant objects for authenticated requests.
  • POST /tenants creates a tenant.
  • GET /tenants/{tenant_id} returns a tenant.
  • PATCH /tenants/{tenant_id} updates a tenant.
  • DELETE /tenants/{tenant_id} deletes a tenant.
  • POST /tenants/{tenant_id}/archive archives a tenant.
  • POST /tenants/{tenant_id}/restore restores an archived tenant.
  • GET /tenants/{tenant_id}/groups returns tenant groups.
  • GET /users returns user objects for authenticated requests.
  • POST /users creates a global user and adds them to the selected tenant's users group.
  • GET /users/{user_id} returns a user.
  • PATCH /users/{user_id} updates a user.
  • DELETE /users/{user_id} deletes a user.
  • POST /users/{user_id}/deactivate deactivates a user.
  • POST /users/{user_id}/restore restores a deactivated user.
  • GET /users/{user_id}/groups returns the groups a user belongs to.
  • GET /groups returns group objects for authenticated requests.
  • POST /groups creates a group.
  • GET /groups/{group_id} returns a group.
  • PATCH /groups/{group_id} updates a group.
  • DELETE /groups/{group_id} deletes a group.
  • POST /groups/{group_id}/archive archives a group.
  • POST /groups/{group_id}/restore restores an archived group.
  • GET /groups/{group_id}/memberships returns user and nested group membership objects for a group.
  • POST /groups/{group_id}/memberships adds a user or nested group membership to a group.
  • DELETE /groups/{group_id}/memberships/{membership_id} removes a membership from a group.

The initial Alembic migration creates the users and revoked_tokens tables. Primary keys and token IDs use ULIDs.

Assets

Assets is a Nexus module. Asset models are authenticated API resources with an ULID id, name, and tracking_type. Tracking type is either serialized or countable.

Assets are authenticated API resources with an ULID id, required tenant_id, required model_id, optional project_id, name, nullable serial_number, quantity, lifecycle status, and lifecycle timestamps. Serialized assets require a serial number and always use quantity 1. Countable assets do not use serial numbers and can store quantities greater than one.

Asset assignments are authenticated API resources with an ULID id, asset_id, either person_id or location_id, start_date, and nullable end_date.

Asset circulations are authenticated API resources with an ULID id, asset_id, circulation_type, optional person_id, optional location_id, occurred_at, and optional notes. Supported circulation types include issuing an asset to a person, returning it, moving it to a location, receiving it at a location, and location confirmation.

Most object list endpoints include records from the selected tenant scope plus shared root-tenant catalog records where those records can be referenced by tenant-owned objects.

Default asset routes:

  • GET /asset-models returns asset model objects for authenticated requests.
  • POST /asset-models creates an asset model with name and tracking_type.
  • GET /assets returns asset objects for authenticated requests.
  • POST /assets creates an asset with model_id, optional project_id, name, and either serial_number or quantity depending on the model tracking type.
  • GET /assets/{asset_id} returns an asset.
  • PATCH /assets/{asset_id} updates an asset.
  • DELETE /assets/{asset_id} deletes an asset.
  • POST /assets/{asset_id}/archive archives an asset.
  • POST /assets/{asset_id}/restore restores an asset to active status.
  • POST /assets/{asset_id}/retire marks an asset retired.
  • POST /assets/{asset_id}/dispose marks an asset disposed.
  • POST /assets/{asset_id}/mark-lost marks an asset lost.
  • POST /assets/{asset_id}/mark-stolen marks an asset stolen.
  • GET /assets/{asset_id}/assignments returns assignments for an asset.
  • POST /assets/{asset_id}/assignments creates an assignment for an asset.
  • GET /asset-assignments returns asset assignment objects for authenticated requests.
  • POST /asset-assignments creates an assignment for a person or location.
  • GET /assignments/{assignment_id} returns an assignment.
  • PATCH /assignments/{assignment_id} updates an assignment.
  • POST /assignments/{assignment_id}/close closes an assignment.
  • GET /asset-circulations returns asset circulation objects for authenticated requests.
  • POST /asset-circulations creates an asset circulation event.
  • GET /asset-repairs returns asset repair objects for authenticated requests.
  • POST /asset-repairs creates a parent issue with repair subtype details.

Projects

Projects is a Nexus module. Projects are authenticated API resources with an ULID id, name, optional description, nullable archived_at, and tenant ownership. Projects are the core organizing object for issue work and asset grouping. Issues and assets can be associated to a project through project_id.

The shared tenant seeds shared Technology Service and Maintenance Service projects. Existing non-repair issues are associated to Technology Service during migration, while repair issues are associated to Maintenance Service.

Default project routes:

  • GET /projects returns project objects for authenticated requests.
  • POST /projects creates a project with name and optional description.
  • GET /projects/{project_id} returns a project.
  • PATCH /projects/{project_id} updates a project.
  • DELETE /projects/{project_id} deletes a project.
  • POST /projects/{project_id}/archive archives a project.
  • POST /projects/{project_id}/restore restores an archived project.
  • GET /projects/{project_id}/groups returns groups scoped to a project.
  • GET /projects/{project_id}/issues returns issues associated to a project.
  • POST /projects/{project_id}/issues creates an issue associated to a project.

Projects Issues

Project issues are part of the Projects module. Issues are authenticated API resources with an ULID id, optional project_id, issue_type, title, optional description, priority, status, opened_at, and nullable closed_at. Supported issue subtypes are service requests, known problems, incidents, post-incident analysis, changes, and repairs. Asset repairs are repair subtype resources with an ULID id, parent issue_id, asset_id, optional location_id, and inherited issue title, description, priority, status, reported/opened time, and resolved/closed time.

Configurations

Configurations is a Nexus module.

Configuration items are authenticated API resources with an ULID id, tenant ownership, name, optional ci_type, optional description, and nullable archived_at. Relationships link two configuration items within the same tenant using a relationship_type.

Default project issue routes:

  • GET /issues returns issue objects across issue types.
  • POST /issues creates an issue based on issue_type.
  • GET /issues/{issue_id} returns an issue.
  • PATCH /issues/{issue_id} updates an issue.
  • DELETE /issues/{issue_id} deletes an issue.
  • POST /issues/{issue_id}/archive archives an issue.
  • POST /issues/{issue_id}/restore restores an archived issue.
  • GET /issues/{issue_id}/transitions returns available issue transitions.
  • POST /issues/{issue_id}/transition transitions an issue.
  • GET /issues/{issue_id}/comments returns issue comments.
  • POST /issues/{issue_id}/comments creates an issue comment.
  • GET /issues/{issue_id}/attachments returns issue attachments.
  • POST /issues/{issue_id}/attachments creates an issue attachment reference.
  • GET /issues/{issue_id}/links returns issue links.
  • POST /issues/{issue_id}/links creates an issue link.
  • GET /issues/{issue_id}/history returns issue history.
  • GET /issues/{issue_id}/activity returns issue activity.
  • GET /issues/{issue_id}/watchers returns issue watchers.
  • POST /issues/{issue_id}/watchers adds an issue watcher.
  • DELETE /issues/{issue_id}/watchers/{user_id} removes an issue watcher.
  • GET /issue-types, POST /issue-types, GET /issue-types/{issue_type_id}, PATCH /issue-types/{issue_type_id}, and DELETE /issue-types/{issue_type_id} manage issue types.
  • GET /issue-statuses, POST /issue-statuses, GET /issue-statuses/{issue_status_id}, PATCH /issue-statuses/{issue_status_id}, and DELETE /issue-statuses/{issue_status_id} manage issue statuses.
  • GET /issue-priorities, POST /issue-priorities, GET /issue-priorities/{issue_priority_id}, PATCH /issue-priorities/{issue_priority_id}, and DELETE /issue-priorities/{issue_priority_id} manage issue priorities.
  • GET /issue-workflows, POST /issue-workflows, GET /issue-workflows/{issue_workflow_id}, PATCH /issue-workflows/{issue_workflow_id}, and DELETE /issue-workflows/{issue_workflow_id} manage issue workflows.
  • GET /issue-workflow-statuses, POST /issue-workflow-statuses, GET /issue-workflow-statuses/{issue_workflow_status_id}, PATCH /issue-workflow-statuses/{issue_workflow_status_id}, and DELETE /issue-workflow-statuses/{issue_workflow_status_id} manage workflow statuses.
  • GET /issue-workflow-transitions, POST /issue-workflow-transitions, GET /issue-workflow-transitions/{issue_workflow_transition_id}, PATCH /issue-workflow-transitions/{issue_workflow_transition_id}, and DELETE /issue-workflow-transitions/{issue_workflow_transition_id} manage workflow transitions.
  • GET /issue-link-types, POST /issue-link-types, GET /issue-link-types/{issue_link_type_id}, PATCH /issue-link-types/{issue_link_type_id}, and DELETE /issue-link-types/{issue_link_type_id} manage issue link types.
  • GET /issue-fields, POST /issue-fields, GET /issue-fields/{issue_field_id}, PATCH /issue-fields/{issue_field_id}, and DELETE /issue-fields/{issue_field_id} manage issue fields.
  • GET /issue-screens, POST /issue-screens, GET /issue-screens/{issue_screen_id}, PATCH /issue-screens/{issue_screen_id}, and DELETE /issue-screens/{issue_screen_id} manage issue screens.
  • GET /issue-screen-fields, POST /issue-screen-fields, GET /issue-screen-fields/{issue_screen_field_id}, PATCH /issue-screen-fields/{issue_screen_field_id}, and DELETE /issue-screen-fields/{issue_screen_field_id} manage issue screen fields.

Default configuration routes:

  • GET /configuration-items returns configuration item objects for authenticated requests.
  • POST /configuration-items creates a configuration item.
  • GET /configuration-items/{ci_id} returns a configuration item.
  • PATCH /configuration-items/{ci_id} updates a configuration item.
  • DELETE /configuration-items/{ci_id} deletes a configuration item.
  • POST /configuration-items/{ci_id}/archive archives a configuration item.
  • POST /configuration-items/{ci_id}/restore restores an archived configuration item.
  • GET /configuration-items/{ci_id}/relationships returns relationships for a configuration item.
  • POST /configuration-items/{ci_id}/relationships creates a relationship from a configuration item.
  • DELETE /relationships/{relationship_id} deletes a configuration item relationship.

Locations

Locations is a Nexus module. Location types are authenticated API resources with an ULID id and name.

Locations are authenticated API resources with an ULID id, nullable parent_id, name, and required type_id.

Default location routes:

  • GET /location-types returns location type objects for authenticated requests.
  • POST /location-types creates a location type with name.
  • GET /locations returns location objects for authenticated requests.
  • POST /locations creates a location with parent_id, name, and type_id.

Census

Census is a Nexus module. Persons are authenticated API resources with an ULID id, nullable current_identity_id, and nullable archived_at.

Identities are authenticated API resources with an ULID id, person_id, last_name, first_name, optional middle_name, nullable deactivated_at, and nullable archived_at. Contacts store person or identity contact details with type, value, optional label, and sort order.

Default Census routes:

  • GET /persons returns person objects for authenticated requests.
  • POST /persons creates a person.
  • GET /persons/{person_id}, PATCH /persons/{person_id}, and DELETE /persons/{person_id} manage a person.
  • POST /persons/{person_id}/archive archives a person.
  • POST /persons/{person_id}/restore restores an archived person.
  • GET /persons/{person_id}/current-identity returns the current identity for a person.
  • POST /persons/{person_id}/current-identity sets the current identity for a person.
  • GET /persons/{person_id}/identities and POST /persons/{person_id}/identities list and create identities for a person.
  • GET /persons/{person_id}/identities/{identity_id}, PATCH /persons/{person_id}/identities/{identity_id}, and DELETE /persons/{person_id}/identities/{identity_id} manage a person identity.
  • POST /persons/{person_id}/identities/{identity_id}/activate and POST /persons/{person_id}/identities/{identity_id}/deactivate manage identity activation.
  • POST /persons/{person_id}/identities/{identity_id}/archive and POST /persons/{person_id}/identities/{identity_id}/restore manage identity archival.
  • GET /identities, GET /identities/{identity_id}, and PATCH /identities/{identity_id} provide identity lookup.
  • GET /persons/{person_id}/contacts, POST /persons/{person_id}/contacts, GET /persons/{person_id}/contacts/{contact_id}, PATCH /persons/{person_id}/contacts/{contact_id}, and DELETE /persons/{person_id}/contacts/{contact_id} manage person contacts.
  • PUT /persons/{person_id}/contacts/order updates person contact order.
  • GET /persons/{person_id}/identities/{identity_id}/contacts and POST /persons/{person_id}/identities/{identity_id}/contacts manage identity contacts.
  • GET /contacts, GET /contacts/{contact_id}, PATCH /contacts/{contact_id}, and DELETE /contacts/{contact_id} provide contact lookup.

Education

Education is a Nexus module. Schools are authenticated API resources with an ULID id, tenant ownership, name, and nullable archived_at.

School years are authenticated API resources with an ULID id, tenant ownership, name, start, end, nullable archived_at, and is_current.

School calendars are authenticated API resources with an ULID id, school_id, calendar_id, and name. The calendar_id references a school year.

School enrollments are authenticated API resources with an ULID id, person_id, calendar_id, status, and lifecycle timestamps. The calendar_id references a school calendar.

Education routes:

  • GET, POST, GET by id, PATCH, and DELETE manage schools, school years, school enrollments, school employments, school employment assignments, school grade levels, and employment assignment types.
  • POST /schools/{school_id}/archive and POST /schools/{school_id}/restore manage school lifecycle.
  • POST /school-years/{school_year_id}/archive, POST /school-years/{school_year_id}/restore, and POST /school-years/{school_year_id}/make-current manage school year lifecycle.
  • GET /schools/{school_id}/calendars, POST /schools/{school_id}/calendars, and GET /school-years/{school_year_id}/calendars expose calendar relationships.
  • POST /school-enrollments/{school_enrollment_id}/withdraw, /reenroll, /graduate, and /transfer manage enrollment status.
  • GET /persons/{person_id}/school-enrollments, GET /school-calendars/{school_calendar_id}/enrollments, POST /school-calendars/{school_calendar_id}/enrollments, GET /schools/{school_id}/enrollments, and GET /school-years/{school_year_id}/enrollments expose enrollment scopes.
  • POST /school-employments/{school_employment_id}/hire, /terminate, /reactivate, and /retire manage employment status.
  • POST /school-employment-assignments/{assignment_id}/start, /end, and /reactivate manage assignment status.
  • GET /school-employments/{school_employment_id}/assignments, POST /school-employments/{school_employment_id}/assignments, GET /schools/{school_id}/employment-assignments, GET /school-calendars/{school_calendar_id}/employment-assignments, and GET /persons/{person_id}/school-employment-assignments expose assignment scopes.

Nexus by McGuire Technology