Skip to content

Other Server Frameworks

The handlers accept standard Request objects or getter/payload interfaces, making them compatible with any JavaScript HTTP server.

Cloudflare Workers

Pass the incoming Request directly:

ts
import { createLitetics, createPingResponse } from 'litetics';

const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: (data) => {
    /* KV, D1, or Durable Object */
  },
  update: (data) => {
    /* update duration */
  },
});

export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === '/ping' && request.method === 'GET') {
      const result = await handlePingRequest(request);
      return createPingResponse(result);
    }

    if (url.pathname === '/event' && request.method === 'POST') {
      await handleEventRequest(request);
      return new Response(null, { status: 204 });
    }

    return new Response('Not found', { status: 404 });
  },
};

Bun

Bun's fetch handler receives a standard Request:

ts
import { createLitetics, createPingResponse } from 'litetics';

const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: logEvent,
  update: logDuration,
});

Bun.serve({
  port: 3000,
  async fetch(request) {
    const url = new URL(request.url);

    if (url.pathname === '/ping') {
      const result = await handlePingRequest(request);
      return createPingResponse(result);
    }

    if (url.pathname === '/event') {
      await handleEventRequest(request);
      return new Response(null, { status: 204 });
    }

    return new Response('Not found', { status: 404 });
  },
});

Deno

Deno's serve provides a standard Request:

ts
import { createLitetics, createPingResponse } from 'npm:litetics';

const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: logEvent,
  update: logDuration,
});

Deno.serve({ port: 3000 }, async (request) => {
  const url = new URL(request.url);

  if (url.pathname === '/ping') {
    const result = await handlePingRequest(request);
    return createPingResponse(result);
  }

  if (url.pathname === '/event') {
    await handleEventRequest(request);
    return new Response(null, { status: 204 });
  }

  return new Response('Not found', { status: 404 });
});

Fastify

Fastify's request object does not implement the Web API Request interface, so use the options overload:

ts
import Fastify from 'fastify';
import { createLitetics, createPingResponse } from 'litetics';

const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: logEvent,
  update: logDuration,
});
const app = Fastify();

app.get('/ping', async (request, reply) => {
  const result = await handlePingRequest({
    requestHeaders: { 'if-modified-since': request.headers['if-modified-since'] },
  });
  const response = createPingResponse(result);
  reply.status(response.status).headers(Object.fromEntries(response.headers)).send(response.body);
});

app.post('/event', async (request, reply) => {
  await handleEventRequest({
    requestBody: request.body,
    requestHeaders: {
      'user-agent': request.headers['user-agent'],
      'accept-language': request.headers['accept-language'],
    },
  });
  reply.status(204).send();
});

app.listen({ port: 3000 });

Express

Express doesn't use the Web API Request object — use the payload overload:

ts
import express from 'express';
import { createLitetics, createPingResponse } from 'litetics';

const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: logEvent,
  update: logDuration,
});
const app = express();

app.use(express.json());

app.get('/ping', async (req, res) => {
  const result = await handlePingRequest({
    requestHeaders: { 'if-modified-since': req.headers['if-modified-since'] },
  });
  const response = createPingResponse(result);
  res.status(response.status).set(Object.fromEntries(response.headers)).send(response.body);
});

app.post('/event', async (req, res) => {
  await handleEventRequest({
    requestBody: req.body,
    requestHeaders: {
      'user-agent': req.headers['user-agent'],
      'accept-language': req.headers['accept-language'],
    },
  });
  res.status(204).send();
});

app.listen(3000);

Nuxt Nitro

Use Nitro's readBody and getHeader helpers with the getter overload:

ts
// server/api/event.post.ts
import { createLitetics } from 'litetics';

const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: logEvent,
  update: logDuration,
});

export default defineEventRequestHandler(async (event) => {
  await handleEventRequest({
    getRequestBody: () => readBody(event),
    getRequestHeader: (name) => getHeader(event, name) ?? null,
  });
  setResponseStatus(event, 204);
  return null;
});
ts
// server/api/ping.get.ts
import { createLitetics, createPingResponse } from 'litetics';

const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: logEvent,
  update: logDuration,
});

export default defineEventRequestHandler(async (event) => {
  const result = await handlePingRequest({
    getRequestHeader: (name) => getHeader(event, name) ?? null,
  });
  return createPingResponse(result);
});

Persistence Patterns

Since Litetics has no built-in storage, custom persist and update implementations connect it to your data layer:

ts
// In-memory (development)
const events: EventData[] = [];
const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: (data) => {
    events.push(data);
  },
  update: ({ bid, durationMs }) => {
    const e = events.find((ev) => ev.bid === bid);
    if (e) e.durationMs = durationMs;
  },
});

// PostgreSQL (Drizzle)
const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: async (data) => {
    await db.insert(events).values({
      /* ...map fields... */
    });
  },
  update: async ({ bid, durationMs }) => {
    await db.update(events).set({ durationMs }).where(eq(events.bid, bid));
  },
});

// Multiple destinations
const { handleEventRequest, handlePingRequest } = createLitetics({
  persist: async (data) => {
    await Promise.all([db.insert(data), analytics.send(data)]);
  },
  update: async (data) => {
    await Promise.all([db.update(data), analytics.update(data)]);
  },
});