Skip to main content

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.

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.

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 in backend/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 in backend/app/main.py.
OrderMiddlewareResponsibility
1FeatureFlagMiddlewareChecks maintenance mode flag; short-circuits requests with a 503 when the platform is under maintenance.
2TrustedHostMiddlewareBlocks host-header injection attacks by validating against an explicit allowed-hosts list.
3GZipMiddlewareCompresses responses larger than 1 KB to reduce bandwidth.
4MetricsMiddlewareInstruments each request for Prometheus scraping at /metrics.
5ErrorHandlerMiddlewareCatches all unhandled exceptions and returns a sanitised JSON error — prevents stack traces from leaking to clients.
6RequestIDMiddlewareGenerates a UUID X-Request-ID header on every request for distributed tracing.
7CORSMiddlewareEnforces an explicit origin allowlist (innermost). Wildcard origins (*) are rejected at startup in production.
Rate limiting is enforced per-endpoint via the SlowAPI @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 under app/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.
Swagger UI and ReDoc are served at /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 in app/services/. Services are responsible for:
  • Orchestrating multi-step operations (e.g. creating a PENDING booking → initiating a Stripe Checkout Session → updating to CONFIRMED on 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_at timestamp; queries filter deleted_at IS NULL by default.
  • Audit fieldscreated_at and updated_at are 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 in frontend/. It uses Vue 3 with the Composition API exclusively — no Options API components exist in the codebase.

File-Based Routing

Pages in frontend/pages/ map directly to URL routes via Nuxt’s automatic router. Dynamic segments use bracket notation:
pages/
├── index.vue                  → /
├── properties/
│   ├── index.vue              → /properties
│   └── [id].vue               → /properties/:id
├── tours/
├── vehicles/
├── boats/
├── flights/
├── auth/
│   ├── login.vue              → /auth/login
│   ├── register.vue           → /auth/register
│   └── forgot-password.vue
├── vendor/
│   └── dashboard.vue          → /vendor/dashboard
└── admin/
Route middleware (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/:
// Example: useApi.ts composable pattern
const { data, pending, error } = await useApi('/api/v1/properties', {
  query: { page: 1, page_size: 20 }
})

Pinia Stores

Global application state is managed with Pinia stores in frontend/stores/. The three core stores are:
StoreFileResponsibility
Authstores/auth.tsJWT tokens (access token in memory, refresh token in httpOnly cookie), user profile, role
Propertiesstores/properties.tsPaginated listing cache, active filters, map-ready GeoJSON data
Searchstores/search.tsFull-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 in backend/alembic/versions/. Key database design decisions:
  • GIN indexes on tsvector columns — full-text search across property names, tour descriptions, and destination guides uses PostgreSQL’s native tsvector with 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_at timestamp 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/audit for 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:
  1. Rate limiting — the SlowAPI middleware stores per-IP request counters with a 60-second TTL. Exceeding 60 requests per minute returns HTTP 429.
  2. 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.
In production the Redis instance is hosted on Railway. In local development the 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
The Cloudinary folder namespace is controlled by CLOUDINARY_FOLDER_PREFIX (default: costaricatravel).

Technology Stack

Backend

TechnologyPurposeVersion
FastAPIWeb framework, OpenAPI generation0.109.0
SQLAlchemyAsync ORM and query builder2.0.25
PydanticRequest / response validation and serialisation2.5.3
PostgreSQLRelational database16
RedisRate limiting and token blacklist7
AlembicDatabase schema migrations1.13.1
PyJWTJWT signing and verification2.8.0
StripePayment processing and webhook handling7.10.0

Frontend

TechnologyPurposeVersion
Nuxt.jsFull-stack Vue framework (SSR/SSG)3.15.0
Vue 3Reactive UI library3.4.15
TypeScriptStatic typing5.3.3
Tailwind CSSUtility-first styling3.4.19
PiniaClient-side state management2.1.7
LeafletInteractive maps for property/search results1.9.4
Chart.jsVendor and admin analytics dashboards4.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 from backend/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 the frontend/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.
The frontend communicates with the backend exclusively through the 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.

Build docs developers (and LLMs) love