Clawdy
Api reference

@clawdyup/core

Core package API reference

createClawdy

Creates a file route builder. This is the main entry point for defining upload routes.

import { createClawdy } from "@clawdyup/core";

const f = createClawdy();

The returned function f accepts a file type configuration object and returns a chainable builder.

f(fileTypes: FileRouterInput)
  .input(zodSchema)
  .middleware(fn)
  .onUploadComplete(fn)

File type configuration

The fileTypes parameter accepts the following shape:

{
  image?: { maxFileSize?: FileSize; maxFileCount?: number };
  video?: { maxFileSize?: FileSize; maxFileCount?: number };
  audio?: { maxFileSize?: FileSize; maxFileCount?: number };
  pdf?: { maxFileSize?: FileSize; maxFileCount?: number };
  archive?: { maxFileSize?: FileSize; maxFileCount?: number };
  raw?: { maxFileSize?: FileSize; maxFileCount?: number };
  blob?: { maxFileSize?: FileSize; maxFileCount?: number };
}

Builder chain

MethodSignatureDescription
.input().input(zodSchema: ZodSchema)Define a Zod schema for client-supplied metadata. The validated data is available in middleware.
.middleware().middleware((opts: { input, req }) => metadata)Run server-side logic before the upload starts. Return an object that becomes metadata in onUploadComplete. Throw a ClawdyError to reject the upload.
.onUploadComplete().onUploadComplete((opts: { metadata, file }) => serverData)Called after the file has been uploaded. The return value is sent back to the client as serverData.

Example

import { createClawdy, type FileRouter } from "@clawdyup/core";
import { z } from "zod";

const f = createClawdy();

export const uploadRouter = {
  profileImage: f({ image: { maxFileSize: "4MB", maxFileCount: 1 } })
    .input(z.object({ userId: z.string() }))
    .middleware(({ input, req }) => {
      // Authenticate and authorize
      return { userId: input.userId };
    })
    .onUploadComplete(({ metadata, file }) => {
      console.log("Upload complete for user:", metadata.userId);
      console.log("File URL:", file.url);
      return { uploadedBy: metadata.userId };
    }),
} satisfies FileRouter;

ClawdyError

A custom Error class. Throw it inside .middleware() to reject an upload with a meaningful message.

import { ClawdyError } from "@clawdyup/core";

.middleware(({ req }) => {
  const user = getUser(req);
  if (!user) throw new ClawdyError("Unauthorized");
  return { userId: user.id };
})

createRouteHandler

@clawdyup/core/server

Creates Next.js App Router route handlers for your upload router.

import { createRouteHandler } from "@clawdyup/core/server";
import { uploadRouter } from "./clawdy";

export const { GET, POST } = createRouteHandler({
  router: uploadRouter,
  config: {
    apiUrl: "https://your-clawdy-server.com",
    apiKey: process.env.CLAWDY_API_KEY, // optional
  },
});

Parameters

ParameterTypeDescription
routerFileRouterYour upload router object.
config.apiUrlstringThe URL of your Clawdy server.
config.apiKeystring | undefinedOptional API key for authenticated requests.

Returns

{ GET, POST } — Export these directly from a Next.js App Router route file (e.g. app/api/clawdy/route.ts).


extractRouterConfig

@clawdyup/core/server

Extracts a serializable route configuration from your upload router. Use this for SSR hydration so the client knows the allowed file types and size limits without an extra network request.

import { extractRouterConfig } from "@clawdyup/core/server";
import { uploadRouter } from "./clawdy";

const routeConfig = extractRouterConfig(uploadRouter);

Returns

A RouteConfig object describing every route's allowed file types, max file size, and max file count.


uploadFiles

@clawdyup/core/client

Client-side function for uploading files programmatically, outside of the React components.

import { uploadFiles } from "@clawdyup/core/client";

const response = await uploadFiles({
  endpoint: "profileImage",
  files: selectedFiles,
  input: { userId: "user_123" },
  url: "/api/clawdy", // optional, this is the default
  onProgress: (percent) => console.log(`${percent}%`),
});

Files larger than 4MB are automatically chunked (7MB per chunk) with SHA-256 integrity validation and retry logic (3 attempts with exponential backoff).

Parameters

ParameterTypeDescription
endpointstringThe name of the route in your FileRouter.
filesFile[]An array of File objects to upload.
inputobject | undefinedOptional metadata that matches the .input() Zod schema on the route.
urlstring | undefinedThe URL of the upload API endpoint. Defaults to "/api/clawdy".
onProgress(percent: number) => voidOptional callback with upload progress (0-100).

Returns

UploadFileResponse[] — An array of upload results containing name, size, key, url, and serverData.


extractVideoThumbnail

@clawdyup/core/client

Extracts a single frame from a video file and returns it as an image File. Uses the HTML5 Canvas API — runs entirely in the browser with no server processing.

import { extractVideoThumbnail } from "@clawdyup/core/client";

const thumbnailFile = await extractVideoThumbnail(videoFile, {
  seekTo: 1,
  maxWidth: 480,
  quality: 0.8,
  format: "image/webp",
});

Parameters

ParameterTypeDescription
fileFileA video File object.
options.seekTonumberTime in seconds to capture. Default: 1. Clamped to video duration for short videos.
options.maxWidthnumberMaximum pixel width. Height scales proportionally. Default: 480.
options.qualitynumberImage quality (0–1). Default: 0.8.
options.formatstringOutput: "image/webp", "image/jpeg", or "image/png". Default: "image/webp". Falls back to JPEG if WebP is unsupported.

Returns

Promise<File> — An image file named {originalName}_thumb.{ext}.


isVideoFile

@clawdyup/core/client

Checks whether a File is a video based on its MIME type.

import { isVideoFile } from "@clawdyup/core/client";

if (isVideoFile(file)) {
  const thumb = await extractVideoThumbnail(file);
}

Returns

booleantrue if file.type starts with "video/".


Types

ExtractThumbnailOptions

type ExtractThumbnailOptions = {
  seekTo?: number;
  maxWidth?: number;
  quality?: number;
  format?: "image/webp" | "image/jpeg" | "image/png";
};

Options for extractVideoThumbnail. All fields are optional with sensible defaults.

FileRouter

type FileRouter = Record<string, UploadRoute>;

A record mapping route names to upload route definitions created by the builder chain.

UploadedFile

type UploadedFile = {
  key: string;
  url: string;
  name: string;
  size: number;
};

Represents a file that has been uploaded and stored.

UploadFileResponse

type UploadFileResponse = {
  name: string;
  size: number;
  key: string;
  url: string;
  serverData: unknown;
};

The response returned to the client after a successful upload, including any data returned from onUploadComplete.

FileSize

type FileSize = `${number}${"KB" | "MB" | "GB"}`;

A template literal type for expressing file size limits (e.g. "4MB", "512KB", "1GB").

FileRouterInput

type FileRouterInput = {
  [key in "image" | "video" | "audio" | "pdf" | "archive" | "raw" | "blob"]?: {
    maxFileSize?: FileSize;
    maxFileCount?: number;
  };
};

The configuration object passed to f() to define which file types a route accepts.

KeyAccepted MIME types
imageJPEG, PNG, WebP, GIF, SVG
videoMP4, MOV, WebM
audioAudio types
pdfPDF documents
archiveZIP, 7z
rawCamera RAW formats (Canon, Nikon, DNG)
blobCatch-all for any file type

RouteConfig

type RouteConfig = {
  [key: string]: {
    fileTypes: string[];
    maxFileSize: string;
    maxFileCount: number;
  };
};

A serializable representation of your upload router's configuration, returned by extractRouterConfig.

ChunkedUploadProgress

type ChunkedUploadProgress = {
  phase: "hashing" | "init" | "uploading" | "completing";
  chunksUploaded: number;
  chunksTotal: number;
  bytesUploaded: number;
  bytesTotal: number;
  percent: number;
};

Describes the progress of a chunked upload. Files larger than 4MB are automatically chunked.

On this page