Skip to content

KosmoJS

the composable meta-framework Multiple source folders. Directory-based routing. Cascading middleware. Nested layouts. End-to-end validation. Fetch clients. OpenAPI spec. Koa, Hono, SolidJS, React, Vue, MDX and more.

Validation

Define types in TypeScript. Get runtime validation, type-safe clients, and OpenAPI specs for free. No schema DSLs. Just pure TypeScript with superpowers. One source of truth, zero maintenance overhead.
Details ➜
api/users/[id]/{activity}/index.ts
ts
import { defineRoute } from "_/api";

export default defineRoute<
  "users/[id]/{activity}",
  [
    // validate id param as number
    number,
    // activity, if given, should be one of
    "posts" | "comments" | "likes",
  ]
>(({ GET }) => [
  GET((ctx) => {
    // ctx.validated.params is typed and validated at runtime
    const { id, activity } = ctx.validated.params
  })
]);

Payload Validation

Validation targets provides control over what gets validated.
Body targets: json / form / raw.
Metadata targets: query, headers, cookies.
Use VRefine to specify JSON Schema constraints.
Details ➜
api/users/index.ts
ts
import { defineRoute } from "_/api";

type Payload = {
  email: VRefine<string, { format: "email" }>;
  password: VRefine<string, { pattern: "^(?=.*[a-zA-Z0-9])$" }>;
  name: VRefine<string, { minLength: 5; maxLength: 50 }>;
  dateOfBirth: VRefine<string, { format: "date" }>;
  agreeToTerms: boolean;
  marketingOptIn?: boolean;
}

export default defineRoute<"users">(({ POST }) => [
  POST<{
    json: Payload,
  }>((ctx) => {
    // ctx.validated.json is typed and validated at runtime
    const { email } = ctx.validated.json;
  })
]);

Response Validation

Just as you validate incoming request data, you can validate outgoing response data by providing a tuple with status code, content type and expected schema.
Also an union of tuples can be used to specify multiple variants.
Enabled by default on dev, disabled in production.
Details ➜
api/pages/[id]/index.ts
ts
import { defineRoute } from "_/api";

type Page = {
  id: VRefine<string, { format: "uuid" }>;
  title: VRefine<string, { minLength: 1; maxLength: 255 }>;
  content: string;
  tags: string[];
  status: "draft" | "published" | "scheduled";
}

export default defineRoute<"pages/[id]">(({ GET }) => [
  GET<{
    response: [200, "json", Page],
  }>(async (ctx) => {
    // response should match defined schema
  })
]);

Composable Middleware

Global middleware added to api/use.ts will run on every route. Routes can use slot option to override global middleware.
Custom slots like logger should be added to api/env.d.ts file.
Details ➜
ts
import { use } from "_/api";

export default [
  use(
    (ctx, next) => {
      // global logger
      return next();
    },
    { slot: "logger", },
  ),
];
ts
import { defineRoute } from "_/api";

export default defineRoute<"example">(({ use, GET }) => [
  use(
    async (ctx, next) => {
      // custom logger
      return next();
    },
    { slot: "logger", },
  ),

  GET(async (ctx) => {
    // ...
  }),
]);
ts
export declare module "@kosmojs/api" {
  interface UseSlots {
    logger: string;
  }
}

Cascading Middleware

Create a use.ts file in any folder, and its default exported middleware automatically wraps all routes in that folder and its subfolders - no imports or manual wiring required.
Details ➜
api/users/use.ts
ts
import { use } from "_/api";

export default [
  use(async (ctx, next) => {
    // any route inside users/ folder and its subfolders
    // will wire this middleware automatically
    return next();
  })
];


KosmoJS is opinionated about organization, unopinionated about implementation.

It gives your full-stack project a consistent structure - multiple source folders, directory-based routing, end-to-end validation, cascading middleware, nested layouts, fetch clients etc. Everything works the same consistent way across every framework combination, backend or frontend.

Separation of concerns isn't something you have to remember; it's built into the core.

No proprietary abstractions. No new paradigms. Just structure on top of tools you already know.

πŸ“˜ Learn more


πŸ’‘ Why Source Folders? ​

Most projects start as one app and quietly become three - a marketing site, a customer app, an admin dashboard - each needing different routing, auth, and deploy strategies.

Source folders make this explicit from the start. Each gets its own api/ and pages/ directories, its own framework stack, its own base URL - while sharing types, validation logic, and infrastructure across a single project. The directory structure enforces boundaries that code review alone can't.

πŸ“˜ Getting started Β· Directory-based routing


πŸ›‘οΈ One Source of Truth ​

KosmoJS converts your TypeScript types into runtime validators, typed fetch clients, client-side validation, and OpenAPI schemas - all from the same definitions. No duplication, no drift.

πŸ“˜ Type safety Β· Validation Β· Fetch clients


βš™οΈ API Development ​

What you build locally is what deploys.

APIs run inside Vite's dev server with full hot-reload. Slot-based middleware gives you surgical control - override globals per-route or per-subtree, compose request handling without the usual middleware spaghetti.

πŸ“˜ Dev workflow Β· Middleware


πŸš€ Production Ready ​

pnpm build produces a bundled API server, optimized frontend assets, and an optional SSR bundle - each deployable independently. Node.js, Deno, Bun, containers, serverless, or edge. Same build output, pick your runtime.

πŸ“˜ Production build


🧠 Philosophy ​

Structure that scales. Choices that stay yours.

You pick the frontend framework, state management, styling, database, deploy target - everything. KosmoJS provides the organizational layer: routing conventions, validation pipeline, middleware composition, build orchestration. The kind of structure that's tedious to set up yourself and easy to let erode over time.

πŸ“˜ About KosmoJS Β· Features


Released under the MIT License.