Build Full-StackDeveloper Beta
Libraries

Build framework-agnostic libraries that embed backend and frontend logic in your users' applications

Frontend
Backend
Database

Libraries

Traditional libraries target either the frontend or backend.

Fragno Fragments

With Fragno, you build libraries as a full-stack package: Fragments.

Application

Developers use Fragments to quickly build full-fledged applications in their framework of choice.

Frameworks

These frameworks and more are already supported

React
Vue
Svelte
SolidJS
Astro
Next.js
Nuxt
Node.js
Go to Docs

With Fragno you build

Full-Stack Libraries

Traditional libraries integrate on either the frontend or the backend, and the user is responsible for the glue-code.

A Fragment does both, it's a full-stack library. Users integrate with only a couple lines of code. No glue.

10x the developer experience.

Define your API routes with full type safety. Routes are embedded directly in your user's application.

import { defineRoute } from "@fragno-dev/core";import { z } from "zod";export const route = defineRoute({  method: "POST",  path: "/ai-chat",  inputSchema: z.string(),  outputSchema: z.array(/* ... */),  handler: async ({ input }, { jsonStream }) => {    const message = await input.valid();    const eventStream = await openai.responses.create({ /* ... */ });    return jsonStream(async (stream) => {      // ...      await stream.write( /* ... */ );    });  },});

Embed Routes

HTTP Routes with automatic frontend bindings

State Management

Reactive client-side stores with invalidation built in

Streaming Support

Real-time newline-delimited JSON streaming

Middleware Support

Users can intercept and process requests before they reach your handlers

Database Layer

Agnostic Data Persistence

Define schemas, query with type safety, and write directly to the user's database

Define Your Schema

Type-safe tables with automatic migrations and support for indexes & relations

import { schema, idColumn, column }  from "@fragno-dev/db/schema";export const commentSchema = schema((s) => {  return s    .addTable("comment", (t) => {      return t        .addColumn("id", idColumn())        .addColumn("content", column("string"))        .addColumn("userId", column("string"))        .addColumn("postId", column("string"))        .addColumn(          "createdAt",          column("timestamp")            .defaultTo((b) => b.now())        )        .createIndex("idx_post", ["postId"]);    })    .addTable("user", (t) => {      return t        .addColumn("id", idColumn())        .addColumn("name", column("string"));    })    .addReference("author", {      type: "one",      from: { table: "comment", column: "userId" },      to: { table: "user", column: "id" }    });});

Query with Type Safety

Joins, filtering, and cursor-based pagination

// Find comments with author dataconst comments = await orm.find(  "comment",  (b) =>    b      .whereIndex("idx_post", (eb) =>        eb("postId", "=", postId)      )      .orderByIndex("idx_created", "desc")      .join((j) =>        j.author((authorBuilder) =>          authorBuilder.select(["name"])        )      )      .pageSize(20));// Fully typed resultsfor (const comment of comments) {  console.log(comment.content);  console.log(comment.author?.name);  //                     ^? { name: string } | null}

Atomic Transactions

Optimistic concurrency control with version checking

const uow = orm.createUnitOfWork();// Phase 1: Retrieve with version infouow.find("user", (b) =>  b.whereIndex("primary", (eb) =>    eb("id", "=", userId)  ));const [users] = await uow.executeRetrieve();// Phase 2: Update with optimistic lockconst user = users[0];uow.update("user", user.id, (b) =>  b    .set({ balance: user.balance + 100 })    .check() // Fails if version changed);const { success } = await uow.executeMutations();if (!success) {  // Concurrent modification detected  console.error("Retry transaction");}

Test Everything

In-memory database for fast tests

import { createDatabaseFragmentForTest }  from "@fragno-dev/test";describe("auth fragment", async () => {  const { fragment, test } =    await createDatabaseFragmentForTest(      authFragmentDefinition,      { adapter: { type: "drizzle-sqlite" } }    );  afterAll(async () => {    await test.cleanup();  });  it("creates user and session", async () => {    const response = await fragment.handler(      signUpRoute,      {        body: {          email: "test@test.com",          password: "password"        }      }    );    expect(response.data).toMatchObject({      sessionId: expect.any(String),      userId: expect.any(String)    });  });});
Works with
Kysely
Drizzle
Learn More

Documentation

Choose your path depending on whether you're a user or a library author

Blog Post

Introduction to Fragno

Understand the philosophy and vision behind Fragno. Learn why we think a framework-agnostic approach to building full-stack libraries is a great choice.

Read Introduction