Documentation

API Reference

Complete reference for Loly Framework APIs.

ServerLoader

Server loaders are defined in page.server.hook.ts for pages, and layout.server.hook.ts for layouts.

import type { ServerLoader } from "@lolyjs/core";

// app/page.server.hook.ts
export const getServerSideProps: ServerLoader = async (ctx) => {
  const { req, res, params, pathname, locals } = ctx;

  // Fetch data
  const data = await fetchData();

  // Redirect
  return {
    redirect: {
      destination: "/new-path",
      permanent: true, // true for 308, false for 307
    },
  };

  // Not found
  return { notFound: true };

  // Return props with metadata
  return {
    props: { data },
    metadata: {
      title: "Page Title",
      description: "Page description",
      canonical: "https://example.com/page",
      openGraph: {
        title: "Page Title",
        description: "Page description",
        type: "website",
        url: "https://example.com/page",
        image: {
          url: "https://example.com/og-image.jpg",
          width: 1200,
          height: 630,
          alt: "Image description",
        },
      },
      twitter: {
        card: "summary_large_image",
        title: "Page Title",
        description: "Page description",
        image: "https://example.com/twitter-image.jpg",
      },
    },
  };
};

Layout Server Hooks: Use layout.server.hook.ts in the same directory as layout.tsx to provide stable props shared across all pages. Layout props are automatically combined with page props.

ApiContext

import type { ApiContext } from "@lolyjs/core";

export async function GET(ctx: ApiContext) {
  // Access request
  const { body, query, params, headers } = ctx.req;
  
  // Access locals (from middlewares)
  const user = ctx.locals.user;
  
  // Send response
  return ctx.Response({ data: "value" });
}

export async function POST(ctx: ApiContext) {
  return ctx.Response({ created: true }, 201);
}

export async function DELETE(ctx: ApiContext) {
  return ctx.Response({ deleted: true }, 204);
}

WssContext

import type { WssContext } from "@lolyjs/core";

export const events = [
  {
    name: "custom-event",
    handler: (ctx: WssContext) => {
      const { socket, data, actions } = ctx;

      // Emit to all clients
      actions.emit("response", { message: "Hello" });

      // Broadcast to all except sender
      actions.broadcast("notification", data);

      // Emit to specific socket
      actions.emitTo(socketId, "private", data);

      // Emit to current client
      actions.emitToClient("ack", { received: true });
    },
  },
];

Client Hooks

import { revalidate, revalidatePath } from "@lolyjs/core/client-cache";
import { useBroadcastChannel } from "@lolyjs/core/hooks";

export default function Page({ 
  params, 
  props 
}: { 
  params: Record<string, string>;
  props: Record<string, any>;
}) {
  const handleRefresh = async () => {
    await revalidate(); // Refresh current page data
    await revalidatePath("/other-page"); // Refresh specific path
  };

  // BroadcastChannel hook for cross-tab communication
  const { send, lastMessage } = useBroadcastChannel("my-channel");
  
  return <div>{/* Your UI */}</div>;
}

Components

import { Link } from "@lolyjs/core/components";

export default function Navigation() {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
      <Link href="/blog/[slug]" params={{ slug: "my-post" }}>
        My Post
      </Link>
    </nav>
  );
}

Utilities

// Validation
import { validate, safeValidate, ValidationError } from "@lolyjs/core";
import { z } from "zod";

const schema = z.object({ email: z.string().email() });
try {
  const data = validate(schema, req.body);
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(error.format());
  }
}

// Sanitization
import { sanitizeString, sanitizeObject, sanitizeCodeInput } from "@lolyjs/core";
const clean = sanitizeString(userInput);
const cleanCode = sanitizeCodeInput(codeInput);

// Logging
import { logger, createModuleLogger, getRequestLogger } from "@lolyjs/core";
const moduleLogger = createModuleLogger("my-module");
moduleLogger.info("Message", { context: "data" });

// Request logger (in handlers)
const reqLogger = getRequestLogger(ctx.req);
reqLogger.info("Request processed", { userId: user.id });

// Cache
import { withCache } from "@lolyjs/core";
export const getServerSideProps = withCache(
  async (ctx) => {
    const data = await expensiveOperation();
    return { props: { data } };
  },
  { ttl: 3600 } // Cache for 1 hour
);

// Client Cache
import { revalidate, revalidatePath } from "@lolyjs/core/client-cache";
await revalidate(); // Refresh current page
await revalidatePath("/posts"); // Refresh specific path

// Rate Limiting
import { strictRateLimiter, defaultRateLimiter, createRateLimiter } from "@lolyjs/core";
export const beforeApi: ApiMiddleware[] = [
  strictRateLimiter, // 5 requests / 15 min
];