The trainee app needs the list of food categories and the foods inside them to render the food preferences picker, both on the profile screen and inside forms that have a food preferences field. One endpoint returns it. Mount: /v1/trainee/foods, router traineeFoodsRouter in src/modules/foods/foods.routes.ts. The handler is traineeCatalog in foods.controller.ts, which calls service.catalog and buildFoodCatalog in food-catalog.ts. Lane: trainee. The router mounts authenticateTrainee only, without the app access guard. The single route is a GET, so preview tokens work. See Authentication. This endpoint returns ids and names only. For macros, serving sizes and portion values use the food search on Trainee nutrition.

Endpoints

GET /v1/trainee/foods/catalog

Returns every food category that holds at least one food, and every food with its home category. Auth: trainee token. No parameters. There is no pagination and no search. The whole catalog is returned in one response. How the catalog is built (repo.catalogSource, then buildFoodCatalog):
  1. Rows. All system foods (studioId null) and the studio’s own foods, except plan-only rows (source equal to ai-private).
  2. Overrides. System foods are shared by every studio and cannot be edited in place. When a studio edits one, the change is stored as a FoodLibraryOverride row for that studio. Here the override’s name, category and categories replace the base values, so each studio sees its own version of a system food.
  3. Home category. A food carries a bag of dietary tags (categories) and one home category (category). The home category is category when set, else the first entry of categories. Foods with neither are left out of the catalog.
  4. Categories. Only categories that still hold at least one food are listed. Known slugs come first in their canonical order. Any other value follows, sorted by name in Hebrew collation.
  5. Foods. Sorted by category order, then by name in Hebrew collation.
The result does not depend on which trainee asks, only on the studio. The trainee’s own exclusions are not applied here: this is the list to pick exclusions from. The response sets no cache headers. Response: 200.
string
The category value as stored on the food. For the built-in categories this is a slug.
string
The Hebrew label from FOOD_CATEGORY_SLUG_TO_HE for a known slug. For any other value the id itself. The names in the example are illustrative.
string
The food’s home category. Always one of the returned categories[].id values.

Built-in category slugs

FOOD_CATEGORY_SLUGS in src/modules/foods/food-enums.ts, in canonical order: Errors: none beyond the lane errors.

How the catalog is used

  • Food preferences. GET and PUT /v1/trainee/food-preferences store excludedCategoryIds, excludedFoodIds and includedExceptionIds. Category ids are the categories[].id values from this catalog and food ids are the foods[].id values. A food is excluded when its id is listed, or when its home category is excluded and the food is not listed as an exception. The home category rule is the same primaryCategoryOf function used to build this catalog.
  • Forms. A food_preferences form field renders the same catalog. The field’s config.hiddenCategoryIds lets a coach hide categories from that field. The answer is stored as ids, so renaming a food or category later does not break old answers.
  • Shopping. The shopping list has its own, narrower food list. See Trainee shopping list.