Skip to content

Courses API

Francisca Portugal edited this page Oct 27, 2026 · 2 revisions

Read-only course catalog and the course → course-unit edge. Status: current. Part of the API Catalog.

All three routes require a session; anonymous callers get 401 unauthorized.

Status Method Path Description
✅ GET /v1/courses All courses; optional ?year= and ?faculty=
✅ GET /v1/courses/:id One course
✅ GET /v1/courses/:id/units Course units offered by a course

A course is a degree programme in a specific academic year (L.EIC 2025, MEIC 2025). A course exists per year, so the same programme has a different id in a different year.

GET /v1/courses

GET /v1/courses?year=2025&faculty=FEUP
→ 200 [Course]
Query Required Notes
year no Academic year as an integer
faculty no Faculty acronym, matched against the offering faculties
  • Both filters are optional and combine (AND).
  • Ordered by acronym ascending.
  • An unknown faculty matches nothing and returns [] — not a 404.
  • A year with no courses returns [].

This is looser than GET /v1/faculties/:acronym/courses, which checks the faculty exists first.

Status code When
200 — Matches, possibly []
401 unauthorized No session
422 validation-error ?year=abc

GET /v1/courses/:id

GET /v1/courses/22841
→ 200 Course
Status code When
200 — Found
401 unauthorized No session
404 course-not-found The id does not exist
422 validation-error :id is not an integer

GET /v1/courses/:id/units

GET /v1/courses/22841/units
→ 200 [CourseUnit]
  • The course is checked first: an unknown id is 404 course-not-found.
  • A course that exists but offers no units returns [].
  • Ordered by unit acronym.

CourseUnit is the abstract, year-agnostic template. The concrete teaching instance — with ECTS, classes and schedule slots — is an occurrence; see Course units API.

Course

{
  "id": 22841,
  "acronym": "MEIC",
  "name": "Mestrado em Engenharia Informática e Computação",
  "courseType": "M",
  "year": 2025,
  "url": "https://sigarra.up.pt/feup/pt/cur_geral.cur_view?pv_curso_id=22841",
  "faculties": ["FEUP"]
}
Field Type Notes
id number SIGARRA course id; already per academic year
acronym string e.g. L.EIC
name string Full name
courseType string Raw SIGARRA code (L, M, P, …)
year number Academic year, e.g. 2025
url string SIGARRA course page
faculties string[] All faculties that offer it

faculties is an array because courses can be shared (["FCUP", "FEUP"]). It is never null; a course with no join row gets [].

courseType is not a reliable "degree type". The raw SIGARRA code is inconsistent across courses and is being investigated in #891. Do not branch on it for planner logic. See the glossary for the naming traps.

Implementation

Concern File
Routes apps/backend/src/interface/routes/courses.routes.ts
Response schemas apps/backend/src/interface/routes/catalog.schemas.ts
Ports domain/ports/course.repository.ts, domain/ports/course-unit.repository.ts
Prisma repositories infrastructure/database/repositories/prisma-course.repository.ts, prisma-course-unit.repository.ts
Shared course read / faculty join infrastructure/database/repositories/course-mapper.ts
Tests apps/backend/tests/interface/routes/catalog/courses.test.ts

Prisma has no implicit many-to-many in Prisma 8, so the offering faculties come from an explicit FacultyCourse join table (one extra query, grouped into courseId → acronyms).

See also


Last verified: 2026-10-27

Clone this wiki locally