') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - Geovanni-dev/Evo-api: Backend do Evo, app de nutrição com IA em construção (WIP). · GitHub
Skip to content

Repository files navigation

🇧🇷 Português

Evo

Backend API for Evo, a mobile-first nutrition app powered by AI that will be released on the app stores (App Store / Google Play).

🚧 Actively under development. Features, routes, and database schema are still subject to frequent changes. Not recommended for production use.

About the project

Evo helps users log meals, track nutritional goals (calories, macros), and receive AI-generated meal plans (Google Gemini), all from a mobile app. This repository contains only the API that powers the app.

Features

  • 🍽️ Meals — create, list, update, and delete meals and items, with a daily summary
  • 🎯 Nutritional goals (TDEE) — calculation and update of calorie and macronutrient targets
  • 🤖 AI chat — nutrition-focused conversation powered by Gemini
  • 📋 Meal plan — automatic meal plan generation via AI, respecting user restrictions and preferences
  • ⚙️ Preferences and dietary restrictions — diet type configuration, excluded foods, restrictions (gluten, lactose, etc.)
  • 🥗 Food reference database — nutritional reference for foods (calories and macros per 100g)
  • Redis caching — caching for goals, meals, meal plan, and preferences

⚠️ User authentication (login, JWT) is not implemented yet — routes currently operate with a default user (DEFAULT_USER_ID) for development purposes.

Tech stack

Project structure

src/
├── app.ts # Express setup and route registration
├── server.ts # entry point (starts the server)
└── lib/
├── AI/ # AI chat (Gemini)
├── meals/ # meals and items
├── meal-plan/ # meal plan generation and management
├── nutrition-goals/ # nutritional goals (TDEE)
├── user-preferences/ # dietary preferences and restrictions
├── prisma/ # Prisma client
└── redis/ # Redis client
prisma/
├── schema.prisma # database schema
└── seed.ts # initial database seed

Each module under src/lib follows the controller/, routes/, services/, and (when applicable) schemas/ pattern.

Environment requirements

Environment variables

VariableDescription
DATABASE_URLPostgreSQL connection string
REDIS_URLRedis connection string
GEMINI_API_KEYGoogle Gemini API key
GOOGLE_GENAI_API_KEYGoogle GenAI API key
DEFAULT_USER_IDDefault user ID used while there's no login
ADMIN_NAMEAdmin user name (seed)
ADMIN_EMAILAdmin user email (seed)
ADMIN_PASSWORDAdmin user password (seed)
PORTServer port (optional, defaults to 3000)

Database

yarn prisma:generate # generates the Prisma Client
yarn prisma:migrate # applies migrations
yarn seed # seeds the database with initial data

Running the server

yarn dev # development mode (hot reload)
yarn build # production build
yarn start # runs the production build

The server runs on http://localhost:3000 by default.

Main endpoints

MethodRouteDescription
GET/Health check
POST/mealsCreate a meal
GET/meals/dailyList today's meals
GET/meals/daily/:mealTypeGet meal details by type
GET/meals/summaryDaily nutritional summary
PUT/meals/daily/:mealIdUpdate a meal
DELETE/meals/daily/:mealIdDelete a meal
DELETE/meals/daily/:mealId/:itemIdDelete an item from a meal
GET/TDEEGet nutritional goal
PUT/TDEECreate/update nutritional goal
POST/ai/chatChat with the nutrition AI
GET/meal-plan/activeGet the active meal plan
POST/meal-plan/generateGenerate a new meal plan via AI
PUT/meal-planUpdate the meal plan
GET/preferencesGet dietary preferences
PATCH/preferencesUpdate dietary preferences
GET/preferences/restrictionsGet dietary restrictions
PATCH/preferences/restrictionsUpdate dietary restrictions

Postman collection available in postman/.

Available scripts

CommandDescription
yarn devStarts the server in development mode
yarn buildGenerates the Prisma Client and compiles TS
yarn startRuns the production build
yarn prisma:generateGenerates the Prisma Client
yarn prisma:migrateRuns Prisma migrations
yarn seedSeeds the database with initial data
yarn lintRuns ESLint
yarn lint:fixRuns ESLint and fixes what it can
yarn formatFormats the code with Prettier

Roadmap

  • User authentication (JWT / Google and Apple OAuth)
  • Mobile app (front-end)
  • Meal photo upload (AI recognition)
  • Weight and hydration history in the API
  • App store release

License

Proprietary project — restricted-use code, no open source license. All rights reserved.

Author

Geovani Rodrigues

About

Backend do Evo, app de nutrição com IA em construção (WIP).

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages