Platform Services

Maravilla bundles a complete set of built-in services — identity, data, real-time, async logic, media, and notifications — all reachable through one entry point and identical between development and production.

Every Maravilla project gets its own isolated set of all of these — a private database, a private KV store, a private object store, its own auth setup, its own push keypair, its own channels and rooms. Nothing is shared across projects. There’s nothing to provision; you just call the API and the project’s services are there.

Accessing Platform Services

All services hang off the getPlatform() helper from @maravilla-labs/platform:

import { getPlatform } from '@maravilla-labs/platform';
const platform = getPlatform();

await platform.auth.login({ email, password });             // Authentication
await platform.KV.sessions.put('user:abc', token);          // KV Store
await platform.DB.find('users', { active: true });          // Database
await platform.STORAGE.put('uploads/photo.jpg', data);      // Storage
await platform.realtime.publish('chat:lobby', { text });    // Realtime Channels
await platform.media.createRoom('standup');                 // Media Rooms
await platform.push.send({ topic: 'waitlist' }, { ... });   // Push Notifications

Services

Identity & Access

Authentication

Email/password sign-up and sign-in, OAuth providers (Google, GitHub, Okta, generic OIDC), session management, and refresh tokens. No tables to create, no JWT plumbing to write.

const session = await platform.auth.login({
  email: 'jane@example.com',
  password: 'securePassword123',
});
// session.access_token — short-lived JWT
// session.refresh_token — single-use refresh token

Authorization

Per-resource policies that decide who can read, write, or act on your data. Attach a rule to a resource and Maravilla evaluates it on every KV/DB/channel/room access — no if (user.id === doc.owner) scattered through your handlers.

// On the "documents" resource, set a policy in the admin UI:
//   auth.user_id == node.owner || auth.is_admin
// That rule now runs on every kv.get / kv.put / db.find on that resource.

Data

KV Store

A namespaced key-value store for fast reads and writes. Ideal for session data, feature flags, caching, and any data that maps naturally to key-value pairs. Supports optional TTL-based expiration and prefix-based listing with cursor pagination.

await platform.KV.sessions.put('user:abc', JSON.stringify({ role: 'admin' }));
const session = JSON.parse(await platform.KV.sessions.get('user:abc'));

Database

A document database with a MongoDB-style query API. Comparison, logical, and element operators for filtering; sort, limit, and skip for pagination; secondary indexes; TTL; and native vector search — write queries once, the platform handles everything.

await platform.DB.insertOne('products', {
  name: 'Widget',
  price: 29.99,
  inStock: true,
});

const affordable = await platform.DB.find('products', {
  price: { $lte: 50 },
  inStock: true,
}, { sort: { price: 1 }, limit: 20 });

Semantic similarity built into the database. Store embeddings alongside your documents, declare a vector index, and query by similarity — optionally combined with regular metadata filters in a single call. Bring your own embeddings from any provider.

await platform.DB.createVectorIndex('products', {
  field: 'embedding',
  dimensions: 1536,
  metric: 'cosine',
});

const similar = await platform.DB.find('products',
  { inStock: true },
  { vector: { field: 'embedding', value: queryEmbedding, k: 10 } },
);

Storage

Object storage for files of any size. Direct server uploads, presigned URLs for browser-to-storage uploads, download URL generation, and file metadata.

await platform.STORAGE.put('reports/q1.pdf', pdfBuffer, {
  contentType: 'application/pdf',
  metadata: { generatedBy: 'reporting-service' },
});

const { url } = await platform.STORAGE.generateDownloadUrl('reports/q1.pdf', 3600);

Async & Logic

Event Handlers

Drop a file into your events/ folder and Maravilla wires it up on deploy. React to user sign-ups, database changes, queue messages, or cron schedules — no subscribe/unsubscribe calls, no background worker to keep alive.

// events/hello.ts
import { onAuth, onDbChange } from '@maravilla-labs/platform/events';

export const sendWelcome = onAuth({ op: 'registered' }, async (event) => {
  console.log(`Welcome ${event.data?.email}!`);
});

export const auditUsers = onDbChange({ collection: 'users' }, async (event) => {
  console.log(`[${event.op}] user ${event.id}`);
});

Workflows

Multi-step business logic written as a plain async function — and made durable for you. A workflow can sleep for a week, wait for a webhook, retry a flaky API call, and resume exactly where it left off after a crash or restart.

import { defineWorkflow } from '@maravilla-labs/platform/workflows';

export const onboarding = defineWorkflow(
  { id: 'onboarding', options: { retries: 5, timeoutSecs: 86400 } },
  async (input, step, ctx) => {
    const user = await step.run('fetch-user', () =>
      ctx.database.findOne('users', { _id: input.userId }),
    );
    await step.sleep('cool-off', '1 day');
    await step.run('send-tips', () => sendTips(user));
  },
);

Real-time

Real-Time Events (REN)

A Server-Sent Events stream of resource mutations — KV writes, DB inserts, storage uploads — pushed to connected browsers. Build live dashboards or collaborative UIs without polling. In multi-node deployments, events fan out across all nodes automatically.

import { RenClient } from '@maravilla-labs/platform/ren';

const ren = new RenClient({ subscriptions: ['kv', 'db'] });

ren.on((event) => {
  console.log(event.t, event.k);  // e.g. "kv.put", "todo:abc123"
});

Realtime Channels

Bidirectional pub/sub messaging plus presence tracking over WebSocket. Your server publishes through platform.realtime; browsers connect to /_rt/ws and subscribe.

await platform.realtime.publish('chat:lobby', {
  text: 'Hello everyone!',
}, { userId: 'alice' });

const members = await platform.realtime.presence.members('chat:lobby');

Media Rooms

Add video and audio calling to your app. Your server creates rooms and mints participant tokens; clients connect directly to the media server with any WebRTC-compatible library.

const room = await platform.media.createRoom('standup', { maxParticipants: 10 });
const { token, url } = await platform.media.generateToken('standup', {
  identity: 'alice',
  name: 'Alice',
});

Media & Outbound

Media Transforms

Turn anything users upload into the shape you actually want to serve. Transcode a wobbly phone-recorded webm into clean mp4, pull a poster frame, resize photos, OCR a scanned PDF, convert Word docs, or fill a .docx template. Your code asks for the transform; the platform queues, runs, and stores the result.

const job = await platform.media.transforms.thumbnail('uploads/videos/clip.webm', {
  at: '1s',
  width: 640,
  format: 'jpg',
});
// Serve the derived file at /api/v/{job.outputKey}

Push Notifications

Real Web Push notifications — the kind that appear even with the tab closed. Send immediately or schedule for later (an hour before an event, every Monday at 9am). VAPID keys, service worker, encryption, subscription storage, and scheduling all handled.

// Browser
import { registerPush } from '@maravilla-labs/platform/push';
await registerPush({ topics: ['waitlist'] });

// Server
await platform.push.send(
  { topic: 'waitlist' },
  { title: 'Doors open!', body: 'Your spot is ready.', url: '/event/123' },
);

Development vs Production

In development, the CLI runs the full platform locally. In production, Maravilla Cloud handles everything. Your code works identically in both environments.

await platform.DB.find('users', { age: { $gte: 18 }, active: true });

You never need to worry about the underlying infrastructure — write your code once and it runs everywhere.

Multi-Layer Caching

In production, both the KV Store and Database benefit from an integrated multi-layer cache:

  1. L1 (in-process) — fast hot lookups within a single runtime instance.
  2. L2 (distributed) — shared across all processes and nodes for cluster-wide coherence.

Caching uses a versioned invalidation strategy: writes bump a version counter, making all previously cached entries instantly unreachable. This guarantees no stale data is ever served after a write. If the distributed cache is unavailable, the system operates without it transparently. Other services (Storage, Channels, REN, Media) have their own per-service performance characteristics documented on their respective pages.

Next Steps

Identity & Access

Data

  • KV Store — key-value operations
  • Database — document query, mutation, indexing
  • Vector Search — semantic similarity with embeddings
  • Storage — file uploads, downloads, presigned URLs

Async & Logic

Real-time

Media & Outbound