This project has been created as part of the 42 curriculum by azinchen, msavelie, ssalorin, jkarhu, elehtone.
A recipe sharing platform with a clean interface, social features and AI integration.
Our ft_transcendence project, Recipe Creating Platform (RCP), is a full-stack web application designed to help users discover, create, and manage recipes with comprehensive social features. The product enables users to share their culinary creations, follow other cooking enthusiasts (cooks), save favourite recipes, leave comments, and explore a rich collection of user-generated culinary content.
- Recipe Discovery & Browsing: Browse and search recipes with filtering and sorting options
- Social Features: Follow other cooks, view their profiles, and see their recipe collections
- User Profiles: Customisable user profiles with avatar support and online status visibility
- Recipe Management: Create, edit, and publish recipes with multilingual support
- Community Interaction: Comment on recipes, rate dishes, and build a community around shared culinary interests
- Favourites: Save favourite recipes for quick access
- Multi-language Support: Support for English, Finnish, and Russian languages
Teams are required to consist of at least 4 and at most 5 members. Ours consisted of 5 members at kick-off.
Our team consists of 5 members:
- Anya Zinchenko (azinchen)
- Nick Saveliev (msavelie)
- Seela Salorinta (ssalorin)
- Jimi Karhu (jkarhu)
- Eric Lehtonen (elehtone)
- Product Owner: Overall project vision, work priority and validation of completed work.
- Anya and Nick shared this role at various stages during the project
- Project Manager: Project planning, communication and deadlines.
- Anya
- Technical Lead: Leads architecture and stack decisions, code quality, and critical reviews.
- Nick
- Developers: Implement features, review code, test, and document contributions.
- All 5 of us were developers to various degrees
This is a rough summary of what each person worked on. See the Git history for full details.
GitHub: greyear
Anya coordinated the project and handled a lot of the backend glue work. She worked on integrating the Core Service and API Gateway, authentication (including Google OAuth and session handling), user profiles and followers, recipe creation and management, search integration, and multilingual support. Also did a fair bit of the deployment config and documentation. Roughly 50 merged PRs across various system-wide stuff.
GitHub: FPyMEHTAPIU
Nick led the frontend work. Handled the React setup, component structure, and routing. Built the core UI bits like grids, pagination, modals, and form inputs. Also did authentication flows on the frontend, styling, and Vite configuration. Around 130 commits to the frontend.
GitHub: SeelaSalorinta
Seela did most of the actual frontend components. Built recipe cards, recipe details pages, user grids, profile views, cook listings, filters, and sorting. Over 150 commits across the frontend—basically built most of what you see on screen.
GitHub: gitenjoyer95
Jimi built the Python microservices, mainly the search service (with RAG/AI for recipe search) and the translation service. Set up the Docker containers, error handling, and integration with the API Gateway.
GitHub: el-kittiwake
Eric built the auth service. Handled user registration, login, session management, Google OAuth, JWT tokens, and permissions. Around 68 commits mainly to the auth service.
Roughly 600 commits across the whole project. Nick and Seela handled the frontend, Eric did auth, Jimi did the Python services, and Anya coordinated the rest and did a lot of the integration work.
Our group tried to follow the subject guidelines regarding project management.
- Regular group meetings. Concise meeting notes available here.
- Task planning using GitHub Issues & Kanban board as well as Discord.
- Work was generally broken into manageable parts and we aimed to have single issue pull requests.
- Peer review was mandatory for all pull requests.
- Mostly ongoing documentation of decisions.
- Discord was used for day-to-day group communication.
Before starting the project, ensure the following tools are installed on your system:
| Tool | Minimum Version | Version Check (bash) |
|---|---|---|
| Docker | 20.10 | docker --version |
| Docker Compose | 1.29 | docker-compose --version |
| GNU Make | Any modern version | make --version |
| Node.js | 20 | node --version |
| npm | 10 | npm --version |
Each service requires environment variable configuration. Copy the env.template files to .env:
# Root project level
cp .env.template .env
# Frontend services
cp frontend/.env.example frontend/.envReplace placeholder values (e.g., JWT_SECRET="your-super-secret-jwt-key-min-32-chars") with appropriate configuration values for your environment.
make upBear in mind sudo is not available on Hive systems.
This command:
- Generates certificates if needed
- Starts all database containers (MongoDB for auth, PostgreSQL for core)
- Launches all backend services (API Gateway, Auth Service, Core Service)
- Configures Traefik reverse proxy for HTTPS routing
- Exposes the application at
https://localhost:8443
Useful management commands:
| Command | Description |
|---|---|
make down |
Stop all services |
make restart |
Restart the entire stack |
make logs |
Watch logs for all services |
make db-status |
View container health status |
make db-reset |
Reset database containers and volumes |
make clean |
Full cleanup including all volumes |
make re |
Full cleanup and restart |
- Frontend:
https://localhost:5173 - API Gateway: External:
https://localhost:8443Internal:http://localhost:3000 - Auth Service:
http://auth-service:3001(internal Docker network) - Core Service:
http://core-service:3002(internal Docker network) - Search Service:
http://search-service:8000(internal Docker network) - Translation Service:
http://translation-service:8001(internal Docker network)
The project uses Jest and Supertest for integration testing.
From the project root:
# Run all tests for all services
make test-jest-all
# Run tests for a specific service using the root Makefile
make test-jest-core
make test-jest-api
# Alternative: run directly in the service directory via npm
cd backend/services/core-service && npm test
cd backend/services/api-gateway && npm test
# Run tests in "watch" mode (re-runs on file changes)
cd backend/services/core-service && npm run test:watch
cd backend/services/api-gateway && npm run test:watch- Express.js Documentation
- React 19 Documentation
- React Router v7 Documentation
- PostgreSQL Database Documentation
- MongoDB Documentation
- TypeScript Handbook
- Traefik Reverse Proxy Documentation
- Docker & Docker Compose Documentation
AI tools (such as GitHub Copilot and Claude Code) were used in the following ways:
- Researching new concepts or tools efficiently
- Writing documentation drafts and templates
- Assistance with mundane code generation
- Initial code review and sanity checking
- Test evaluation and test testing, to make sure testy tests test testily
A practical example of AI usage is this here README. Initial work was done by passing the README requirements section of the subject PDF to the AI and asking it to generate a readme for our project. We worked that into the end result you see here.
All AI-generated code passes before human eyes prior to any use in the project.
- Framework: React 19 with React Router v7
- Language: TypeScript 5.9+
- Build Tool: Vite 7.1
- Styling: SCSS (Dart Sass) — component-scoped stylesheets with shared mixins and variables
- Internationalisation: react-i18next with remix-i18next
- Form Validation: Zod 4.3
- Component Library: Icons library
Rationale: React Router v7 provides modern, performant routing with server-side rendering capabilities. Vite ensures fast development build cycles and optimised production bundles. TypeScript provides type safety across the frontend layer.
- API Gateway: Express.js 5
- Service Architecture: Microservices (five independent services)
- Language: TypeScript 5.9+
- Authentication Service: Node.js with Express
- Core Service: Node.js with Express
- Form Validation: Zod 4.3 for schema validation
- Testing: Jest 30.3 with Supertest
Rationale: Express provides a lightweight, flexible HTTP server foundation. Microservices architecture enables independent scaling and deployment of different concerns (api-gateway, auth-service, core-service, search-service, translation-service).
-
Auth Database: MongoDB 6 (stores user authentication credentials and sessions)
- Port: 27017
- Name:
auth_db
-
Core Database: PostgreSQL 15 (stores recipes, users, followers, ratings, comments)
- Port: 5433
- Name:
core_db
Rationale: PostgreSQL selected for relational data (recipes, users, followers) with ACID compliance. MongoDB selected for flexible authentication session storage. Separate databases enable independent scaling and failure isolation.
- Containerisation: Docker & Docker Compose
- Reverse Proxy & Load Balancing: Traefik v3.0
- SSL/TLS: Self-signed certificates for HTTPS
- Service Orchestration: Docker Compose (development/staging)
Rationale: Docker ensures consistency across development and production environments. Traefik provides automatic HTTPS routing and service discovery within the containerised environment.
| Feature | Description | Developer |
|---|---|---|
| User Registration & Authentication | Secure user account creation and login | Eric, Anya, Nick |
| User Profiles | Customisable user profiles with avatar support | Anya |
| Recipe Creation | Users can create and publish recipes | Nick, Anya |
| Recipe Discovery | Browse and search recipes with filtering | Nick, Jimi |
| Favourites | Save recipes to favourites list | Anya |
| Follow System | Follow other cooks, view follower lists | Nick, Anya |
| Comments | Leave comments on recipes | Anya |
| Recipe Ratings | Rate recipes (1–5 stars) | Anya |
| Multilingual Support | Interface in English, Finnish, Russian | Nick, Seela, Anya |
| Responsive Design | Mobile and desktop compatibility | Nick, Seela |
| AI Features | Recipe recommendations and intelligent search | Nick, Jimi |
| Documentation | README and other internal documentation | All members |
Due to the time pressure we had for this project we agreed on a module selection that gave us some leeway for change but should easily allow us to meet the required minimum. Our plan consisted of modules totalling 18 points. This gives us ample flexibility should problems or unseen issues arrive. This also gives us some flexibility should an evaluator encounter a critical failure.
| Module | Type | Points | Notes |
|---|---|---|---|
| Use a framework for both the frontend and backend | Major | 2 | React 19 (frontend) and Express.js (backend) implemented |
| Server-Side Rendering (SSR) for improved performance and SEO | Minor | 1 | React Router v7 with server-side rendering capabilities |
| Custom-made design system with reusable components, including a proper colour palette, typography, and icons (minimum: 10 reusable components) | Minor | 1 | Reusable components with consistent colour palette, typography, and Iconoir icons |
| Implement advanced search functionality with filters, sorting, and pagination | Minor | 1 | Filtering, sorting, and pagination implemented across recipes and users |
| Module Name | Type | Points | Notes |
|---|---|---|---|
| Complete accessibility compliance (WCAG 2.1 AA) with screen reader support, keyboard navigation, and assistive technologies | Major | 2 | Screen reader support, keyboard navigation, and assistive technology support |
| Support for multiple languages (at least 3 languages) | Minor | 1 | Support for English, Finnish, and Russian using react-i18next |
| Support for additional browsers | Minor | 1 | Responsive design and cross-browser compatibility |
| Module Name | Type | Points | Notes |
|---|---|---|---|
| Standard user management and authentication | Major | 2 | Secure registration, login, and JWT-based authentication |
| Implement remote authentication with OAuth 2.0 (Google, GitHub, 42, etc.) | Minor | 1 | Google OAuth integration for single sign-on |
| Module Name | Type | Points | Notes |
|---|---|---|---|
| Implement a complete RAG (Retrieval-Augmented Generation) system | Major | 2 | Complete RAG implementation for intelligent content retrieval |
| Module Name | Type | Points | Notes |
|---|---|---|---|
| Backend as microservices | Major | 2 | Backend implemented as microservices (Auth, Core, Search, Translation services) |
Total Points: 18
erDiagram
USERS {
integer **id**
varchar username
varchar avatar
varchar status
varchar role
timestamptz last_seen_at
timestamptz created_at
timestamptz updated_at
}
FOLLOWERS {
integer **user_id**
integer **followed_id**
timestamptz created_at
}
RECIPES {
integer **id**
jsonb title
jsonb description
jsonb instructions
integer servings
smallint spiciness
integer *author_id*
varchar status
numeric rating_avg
integer rating_count
timestamptz created_at
timestamptz updated_at
}
INGREDIENTS {
integer **id**
varchar name
timestamptz created_at
}
UNITS {
varchar **code**
varchar kind
}
RECIPE_INGREDIENTS {
integer **recipe_id**
integer **ingredient_id**
numeric amount
varchar unit
}
RECIPE_CATEGORY_TYPES {
integer **id**
varchar code
varchar name
timestamptz created_at
}
RECIPE_CATEGORIES {
integer **id**
integer *category_type_id*
varchar code
timestamptz created_at
}
RECIPE_CATEGORY_MAP {
integer **recipe_id**
integer **category_id**
}
INGREDIENT_CATEGORIES {
integer **id**
varchar code
timestamptz created_at
}
INGREDIENT_CATEGORY_CORRESPONDENCE {
integer **ingredient_id**
integer **category_id**
}
ALLERGENS {
integer **id**
varchar code
timestamptz created_at
}
ALLERGEN_CATEGORIES {
integer **allergen_id**
integer **category_id**
}
USER_ALLERGENS {
integer **user_id**
integer **allergen_id**
timestamptz created_at
}
DIETS {
integer **id**
varchar code
varchar name
timestamptz created_at
}
DIET_RESTRICTED_CATEGORIES {
integer **diet_id**
integer **category_id**
}
USER_DIETS {
integer **user_id**
integer **diet_id**
timestamptz created_at
}
INGREDIENT_UNIT_CONVERSIONS {
integer **ingredient_id**
varchar **unit**
numeric grams
timestamptz created_at
}
NUTRITION_FACTS {
integer **ingredient_id**
numeric calories
numeric protein
numeric fat
numeric carbs
varchar base_unit
timestamptz created_at
}
INGREDIENT_PORTIONS {
integer **id**
integer *ingredient_id*
varchar name
numeric weight_in_grams
timestamptz created_at
}
FAVORITES {
integer **user_id**
integer **recipe_id**
timestamptz created_at
}
RECIPE_SHARES {
integer **user_id**
integer **recipe_id**
timestamptz created_at
}
RECIPE_MEDIA {
integer **id**
integer *recipe_id*
varchar type
varchar url
integer position
timestamptz created_at
}
RECIPE_REVIEWS {
integer **id**
integer *recipe_id*
integer *author_id*
text body
boolean is_deleted
timestamptz created_at
timestamptz updated_at
}
RECIPE_RATINGS {
integer **user_id**
integer **recipe_id**
smallint rating
timestamptz created_at
timestamptz updated_at
}
USERS ||--o{ FOLLOWERS : "follows"
USERS ||--o{ RECIPES : "authors"
USERS ||--o{ RECIPE_RATINGS : "rates"
USERS ||--o{ RECIPE_REVIEWS : "writes"
USERS ||--o{ FAVORITES : "saves"
USERS ||--o{ RECIPE_SHARES : "shares"
USERS ||--o{ USER_ALLERGENS : "has"
USERS ||--o{ USER_DIETS : "follows"
RECIPES ||--o{ RECIPE_INGREDIENTS : "contains"
RECIPES ||--o{ RECIPE_CATEGORY_MAP : "belongs_to"
RECIPES ||--o{ RECIPE_MEDIA : "has"
RECIPES ||--o{ RECIPE_REVIEWS : "receives"
RECIPES ||--o{ RECIPE_RATINGS : "receives"
INGREDIENTS ||--o{ RECIPE_INGREDIENTS : "used_in"
INGREDIENTS ||--o{ INGREDIENT_CATEGORY_CORRESPONDENCE : "maps_to"
INGREDIENTS ||--o{ NUTRITION_FACTS : "has"
INGREDIENTS ||--o{ INGREDIENT_UNIT_CONVERSIONS : "converts"
INGREDIENTS ||--o{ INGREDIENT_PORTIONS : "has"
RECIPE_INGREDIENTS ||--o{ UNITS : "uses"
RECIPE_CATEGORIES ||--o{ RECIPE_CATEGORY_MAP : "categorizes"
RECIPE_CATEGORIES ||--o{ RECIPE_CATEGORY_TYPES : "types"
INGREDIENT_CATEGORIES ||--o{ INGREDIENT_CATEGORY_CORRESPONDENCE : "categorizes"
INGREDIENT_CATEGORIES ||--o{ ALLERGEN_CATEGORIES : "maps"
INGREDIENT_CATEGORIES ||--o{ DIET_RESTRICTED_CATEGORIES : "restricts"
ALLERGENS ||--o{ ALLERGEN_CATEGORIES : "restricts"
ALLERGENS ||--o{ USER_ALLERGENS : "restricted_by"
DIETS ||--o{ DIET_RESTRICTED_CATEGORIES : "restricts"
DIETS ||--o{ USER_DIETS : "followed_by"
UNITS ||--o{ INGREDIENT_UNIT_CONVERSIONS : "converts"
UNITS ||--o{ NUTRITION_FACTS : "base_unit"
Stores registered users' data.
| Field | Type | Required | Unique |
|---|---|---|---|
| _id | ObjectId | ✓ | ✓ |
| id | Number | ✓ | ✓ |
| String | ✓ | ✓ | |
| passwordHash | String | ✓ | |
| googleID | String | ✓ |
Used in the function to generate the next userId.
| Field | Type | Required | Unique | Default |
|---|---|---|---|---|
| _id | ObjectId | ✓ | ✓ | |
| name | String | ✓ | ✓ | "CounterDB" |
| seq | Number | ✓ | 1 |
We use two databases because they solve different problems well.
PostgreSQL for the core service. This handles recipes, users, followers, and social interactions.
These entities have lots of relationships and constraints that need to stay consistent—a user can't favourite a recipe that doesn't exist, followers need proper cascading deletes, ingredients link to multiple recipes via join tables. PostgreSQL gives us ACID transactions and strong foreign key support without boilerplate. The strong schema also catches mistakes early.
MongoDB for the auth service. This approach keeps credentials and session data separate.
Authentication doesn't need complex relationships; it just needs to quickly check if a user exists and verify passwords. MongoDB's flexibility means we can evolve the auth schema without migrations. Keeping auth data isolated also means a compromise of the auth database doesn't expose recipe or additional user data, and vice versa.
Each microservice owns its own database, so they can scale independently and don't step on each other's data.
We split the system into three services that each handle one thing:
API Gateway — Routes requests to the right service. Handles JWT validation, logs requests, and aggregates responses if needed. It's thin on purpose: no business logic, no database access.
Auth Service — Handles user registration, login, password hashing, token generation, and OAuth with Google. Stores credentials in MongoDB. Keeps this concern isolated so we can patch or scale it independently.
Core Service — Manages recipes, user profiles, followers, ratings, comments, and everything else. Talks to PostgreSQL. The bulk of the app logic lives here.
Search Service — AI-powered recipe search using retrieval-augmented generation (RAG). Built in Python. Handles intelligent content retrieval and recipe recommendations.
Translation Service — On-demand recipe translation using LLM capabilities. Built in Python. Translates recipes and content between supported languages.
Each service scales independently, so we can deploy auth changes without touching the recipe service, or add search without redeploying core.