Server-Side Guide

Node.js, Express, backend applications, and API development

Express Middleware

Use typed errors for clean middleware and request handling.

import { Either, Left, Right } from 'kotlinify-ts/monads';

// Validate and process request with Either
function validateRequest(req) {
  return req.body ? Right(req.body) : Left({ code: 400, message: 'No body' });
}

validateRequest(req)
  .flatMap(body => authenticate(body))
  .flatMap(user => authorize(user, action))
  .flatMap(user => processRequest(user))
  .fold(
    error => res.status(error.code).json({ error: error.message }),
    data => res.json(data)
  );

// Auth token validation
const token = req.headers.authorization;
(token ? Right(token) : Left('No token'))
  .flatMap(t => verifyToken(t))
  .flatMap(user => checkPermissions(user, action))
  .fold(
    error => res.status(401).json({ error }),
    user => next()
  );

Database Operations

Chain database queries with error recovery and transaction support.

import { tryCatch, also } from 'kotlinify-ts';

tryCatch(() => db.findUser(id))
  .flatMap(user => tryCatch(() => db.findOrders(user.id)))
  .map(orders => calculateTotal(orders))
  .onSuccess(total => db.updateTotal(id, total))
  .fold(
    error => rollback(error),
    total => commit(total)
  );

also(db.transaction(), tx => {
  tryCatch(() => tx.insert(user))
    .flatMap(() => tryCatch(() => tx.insert(profile)))
    .fold(
      error => tx.rollback(),
      () => tx.commit()
    );
});

API Request Handling

Build robust API endpoints with validation and error handling.

import { tryCatch } from 'kotlinify-ts';

app.post("/api/users", (req, res) => {
  tryCatch(() => validateInput(req.body))
    .flatMap(data => tryCatch(() => sanitize(data)))
    .flatMap(data => tryCatch(() => checkDuplicates(data)))
    .flatMap(data => tryCatch(() => hashPassword(data)))
    .flatMap(data => tryCatch(() => saveToDatabase(data)))
    .flatMap(user => tryCatch(() => sendWelcomeEmail(user)))
    .map(user => formatUserResponse(user))
    .fold(
      error => res.status(400).json({ error }),
      user => res.status(201).json(user)
    );
});

tryCatch(() => getRequestData(req))
  .flatMap(data => tryCatch(() => validateSchema(data)))
  .flatMap(valid => tryCatch(() => processData(valid)))
  .map(result => transformResult(result))
  .fold(
    error => res.status(400).json({ error }),
    data => res.json(data)
  );

Background Jobs

Run background tasks with coroutines and structured concurrency.

import { launch, coroutineScope } from 'kotlinify-ts/coroutines';

const queue = createJobQueue();
queue.subscribe(job =>
  launch(() => {
    processJob(job);
  })
    .catch(error => {
      logger.error("Job failed:", error);
      queue.retry(job);
    })
);

await coroutineScope((scope) => {
  const jobs = [
    scope.launch(() => sendEmails(batch)),
    scope.launch(() => processImages(batch)),
    scope.launch(() => generateReports(batch))
  ];

  return Promise.all(jobs.map(j => j.join()))
    .then(results => logResults(results))
    .catch(error => notifyAdmin(error));
});

Stream Processing

Process large datasets efficiently with sequences and flows.

import { asSequence } from 'kotlinify-ts/sequences';

asSequence(db.streamUsers())
  .filter(user => user.isActive)
  .map(user => enrichUserData(user))
  .chunked(100)
  .forEach(batch => processBatch(batch));

asSequence(getLogFiles())
  .flatMap(file => readLines(file))
  .filter(line => line.includes("ERROR"))
  .map(line => parseError(line))
  .groupBy(error => error.type)
  .forEach(([type, errors]) => report(type, errors));

Caching Strategies

Implement caching with scope functions and error recovery.

import { also, let } from 'kotlinify-ts';
import { fromNullable, Some } from 'kotlinify-ts';

const key = getCacheKey(req);
const cached = let(key, k => cache.get(k));

fromNullable(cached)
  .orElse(() => {
    const data = fetchData();
    also(data, d => cache.set(key, d));
    return Some(data);
  })
  .getOrThrow();

tryCatch(() => cache.get(key))
  .recover(() => {
    const data = fetchFromDb(key);
    also(data, d => cache.set(key, d, ttl));
    return data;
  })
  .fold(
    error => res.status(500).json({ error }),
    data => res.json(data)
  );

Rate Limiting

Implement rate limiting with windowed operations and flows.

import { windowed } from 'kotlinify-ts/collections';
import { fromNullable } from 'kotlinify-ts/monads';

const log = getRequestLog(userId);
const windows = windowed(log, 100, 1);
const recentWindows = windows.map(window => window.filter(isRecent));
const exceeded = recentWindows.find(window => window.length > limit);

fromNullable(exceeded)
  .fold(
    () => next(),
    () => res.status(429).json({ error: "Rate limit exceeded" })
  );

requestFlow
  .throttle(100)
  .filter(req => !isRateLimited(req))
  .collect(req => processRequest(req));

Parallel Processing

Process multiple operations in parallel with automatic error handling.

import { coroutineScope } from 'kotlinify-ts/coroutines';

await coroutineScope((scope) => {
  const deferreds = getUserIds().map(id =>
    scope.async(() => fetchUserData(id))
  );

  return Promise.all(deferreds.map(d => d.await()))
    .then(users => users.filter(Boolean))
    .then(users => saveToCache(users))
    .catch(error => logger.error("Batch failed:", error));
});

await coroutineScope((scope) => {
  const tasks = [
    scope.launch(() => updateSearchIndex()),
    scope.launch(() => regenerateSitemap()),
    scope.launch(() => clearCache()),
    scope.launch(() => notifySubscribers())
  ];

  return Promise.all(tasks.map(t => t.join()))
    .then(() => logger.info("Deployment tasks completed"))
    .catch(error => rollbackDeployment(error));
});

Next Steps