Maleku System is organised into five discrete tiers that flow in a single direction: a presentation layer (Nuxt.js 3 on Vercel) communicates with an API gateway (FastAPI on Railway) over HTTPS and REST. The gateway passes validated requests down to a service layer containing domain-specific business logic, which in turn queries a repository / data layer built on async SQLAlchemy 2.0. Finally, the infrastructure layer provides durable storage (PostgreSQL), ephemeral caching (Redis), and media delivery (Cloudinary). This separation of concerns ensures that each layer can evolve, scale, or be replaced independently without cascading changes.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/IvanchoDev89/maleku-system/llms.txt
Use this file to discover all available pages before exploring further.
Frontend
Nuxt.js 3 with Vue 3 Composition API, file-based routing, Pinia state management, and built-in i18n for ES, EN, and FR.
Backend
FastAPI with a middleware stack, Pydantic v2 validation, RBAC dependency injection, and async SQLAlchemy service and repository layers.
Infrastructure
PostgreSQL 16 with GIN full-text indexes and JSONB, Redis 7 for rate limiting and session caching, and Cloudinary for image CDN.
Backend Layers
Every inbound HTTP request traverses a fixed middleware stack before reaching any route handler. The stack is registered inbackend/app/main.py and runs in the following order:
Middleware Stack
FastAPI uses Starlette’s LIFO (last-in, first-out) middleware stack — the last middleware registered wraps all previously registered ones, so it runs first on incoming requests. The table below lists middleware in execution order (outermost → innermost) as registered inbackend/app/main.py.
| Order | Middleware | Responsibility |
|---|---|---|
| 1 | FeatureFlagMiddleware | Checks maintenance mode flag; short-circuits requests with a 503 when the platform is under maintenance. |
| 2 | TrustedHostMiddleware | Blocks host-header injection attacks by validating against an explicit allowed-hosts list. |
| 3 | GZipMiddleware | Compresses responses larger than 1 KB to reduce bandwidth. |
| 4 | MetricsMiddleware | Instruments each request for Prometheus scraping at /metrics. |
| 5 | ErrorHandlerMiddleware | Catches all unhandled exceptions and returns a sanitised JSON error — prevents stack traces from leaking to clients. |
| 6 | RequestIDMiddleware | Generates a UUID X-Request-ID header on every request for distributed tracing. |
| 7 | CORSMiddleware | Enforces an explicit origin allowlist (innermost). Wildcard origins (*) are rejected at startup in production. |
@limiter.limit decorator (Redis-backed, 60 requests per minute per IP by default) rather than as a stack middleware. Exceeding the limit returns HTTP 429.
API Layer
After the middleware stack, FastAPI’s router dispatches requests to endpoint functions underapp/api/v1/. Each endpoint uses dependency injection to:
- Resolve a database session from the async connection pool.
- Validate and deserialise the request body with Pydantic v2 schemas.
- Enforce role-based access control by asserting the caller’s JWT-embedded role against the required permission level.
/docs and /redoc respectively, but only when DEBUG=True — they are disabled in production to avoid exposing the API surface.
Service Layer
Business logic lives exclusively inapp/services/. Services are responsible for:
- Orchestrating multi-step operations (e.g. creating a
PENDINGbooking → initiating a Stripe Checkout Session → updating toCONFIRMEDon webhook receipt). - Calling external APIs: Stripe for payments, Cloudinary for image uploads, Resend/SMTP for transactional email.
- Coordinating transactions so that partial failures roll back atomically.
Repository / Data Layer
app/models/ contains SQLAlchemy 2.0 async ORM models. Key patterns applied uniformly across all models:
- Soft delete — every model has a
deleted_attimestamp; queries filterdeleted_at IS NULLby default. - Audit fields —
created_atandupdated_atare set automatically on every write. - JSONB columns — flexible fields (amenities, location coordinates, availability calendars, verification documents) use PostgreSQL JSONB to avoid rigid schema migrations for structured-but-variable data.
- TSVector columns — full-text search vectors are maintained by PostgreSQL triggers and indexed with GIN for fast weighted search across name, description, and location fields.
Frontend Architecture
The frontend is a Nuxt.js 3 application located infrontend/. It uses Vue 3 with the Composition API exclusively — no Options API components exist in the codebase.
File-Based Routing
Pages infrontend/pages/ map directly to URL routes via Nuxt’s automatic router. Dynamic segments use bracket notation:
frontend/middleware/auth.ts, frontend/middleware/admin.ts) guards protected routes on the client side, complementing the backend’s RBAC enforcement.
Composition API with <script setup>
Every component and page uses the <script setup> syntax with TypeScript. Shared reactive logic — API calls, authentication state, search filters — is extracted into composables under frontend/composables/:
Pinia Stores
Global application state is managed with Pinia stores infrontend/stores/. The three core stores are:
| Store | File | Responsibility |
|---|---|---|
| Auth | stores/auth.ts | JWT tokens (access token in memory, refresh token in httpOnly cookie), user profile, role |
| Properties | stores/properties.ts | Paginated listing cache, active filters, map-ready GeoJSON data |
| Search | stores/search.ts | Full-text query string, type filter, search results, loading state |
Internationalisation (i18n)
Translation files for Spanish (es), English (en), and French (fr) live in frontend/i18n/. The Nuxt i18n module handles locale detection from the browser, cookie persistence, and URL-based locale switching without requiring separate deployments per language.
Data Storage
PostgreSQL 16
PostgreSQL is the single source of truth for all persistent data. The schema is managed entirely through Alembic migrations inbackend/alembic/versions/.
Key database design decisions:
- GIN indexes on
tsvectorcolumns — full-text search across property names, tour descriptions, and destination guides uses PostgreSQL’s nativetsvectorwith weighted fields (name →A, description →B, city →C). GIN indexes make these queries sub-millisecond at scale. - JSONB for flexible fields — amenities arrays, location objects
{lat, lng, address, city, region}, availability calendars, and vendor verification documents are stored as JSONB. This avoids schema churn for product-catalog data that evolves frequently. - Soft deletes — the
deleted_attimestamp on every model means no record is ever permanently removed by application code. This preserves referential integrity in booking history and enables full audit trails. - Audit logs — change events are written to a dedicated audit table and exposed at
GET /api/v1/superadmin/auditfor Super Admin review. - Connection pooling — SQLAlchemy’s async engine is configured with a pool of 20 connections, preventing exhaustion under concurrent load.
Redis 7
Redis serves two purposes in the application:- Rate limiting — the SlowAPI middleware stores per-IP request counters with a 60-second TTL. Exceeding 60 requests per minute returns HTTP 429.
- Token blacklist — on logout, the user’s current JWT is added to a Redis set so it is rejected by subsequent requests before the token’s natural expiry.
costarica_redis Docker container is reachable at redis://localhost:6381.
Cloudinary
All user-uploaded images (property photos, vendor logos, tour imagery) are stored and served through Cloudinary. The backend uploads via the Cloudinary Python SDK and stores only the resulting asset URL in PostgreSQL. Cloudinary handles:- Automatic format optimisation (WebP, AVIF where supported)
- Responsive transformations (resize, crop, quality) via URL parameters
- Global CDN delivery to minimise latency for international visitors
CLOUDINARY_FOLDER_PREFIX (default: costaricatravel).
Technology Stack
Backend
| Technology | Purpose | Version |
|---|---|---|
| FastAPI | Web framework, OpenAPI generation | 0.109.0 |
| SQLAlchemy | Async ORM and query builder | 2.0.25 |
| Pydantic | Request / response validation and serialisation | 2.5.3 |
| PostgreSQL | Relational database | 16 |
| Redis | Rate limiting and token blacklist | 7 |
| Alembic | Database schema migrations | 1.13.1 |
| PyJWT | JWT signing and verification | 2.8.0 |
| Stripe | Payment processing and webhook handling | 7.10.0 |
Frontend
| Technology | Purpose | Version |
|---|---|---|
| Nuxt.js | Full-stack Vue framework (SSR/SSG) | 3.15.0 |
| Vue 3 | Reactive UI library | 3.4.15 |
| TypeScript | Static typing | 5.3.3 |
| Tailwind CSS | Utility-first styling | 3.4.19 |
| Pinia | Client-side state management | 2.1.7 |
| Leaflet | Interactive maps for property/search results | 1.9.4 |
| Chart.js | Vendor and admin analytics dashboards | 4.4.1 |
Deployment Topology
Maleku System is designed for a split-cloud deployment where the compute-heavy backend runs on container infrastructure and the frontend benefits from a global edge network.Backend — Railway
The FastAPI application, PostgreSQL database, and Redis instance are all hosted on Railway. Railway runs the backend inside a Docker container built frombackend/Dockerfile. Key operational features:
- Zero-downtime deploys via Railway’s rolling restart strategy.
- Automatic health checks against
/health/ready, which verifies live database and Redis connectivity before routing traffic. - Environment variable management through the Railway project dashboard — no secrets are baked into the image.
Frontend — Vercel
The Nuxt.js application is deployed to Vercel using thefrontend/vercel.json configuration. Vercel’s edge network provides:
- Global CDN with automatic asset caching and invalidation on each deploy.
- SSR at the edge for fast initial page loads and SEO-friendly HTML responses.
- Automatic preview deployments for every pull request branch.
NUXT_PUBLIC_API_URL environment variable, making it trivial to point different deployment environments at different backend instances.
Swagger UI is available at http://localhost:8000/docs and ReDoc at http://localhost:8000/redoc when the backend is started with
DEBUG=True (the default in docker-compose.yml for local development). Both endpoints are disabled in production builds to avoid exposing the full API schema publicly.