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
];