DataFn
Concepts

Resources

Define data resources with names, versions, and field configurations.

DatafnResourceSchema

A resource represents a collection of records (analogous to a database table). Each resource has a name, version, fields, optional indices, and optional permissions.

type DatafnResourceSchema = {
  name: string;
  version: number;
  idPrefix?: string;
  isRemoteOnly?: boolean;
  fields: DatafnFieldSchema[];
  indices?:
    | { base?: string[]; search?: string[]; vector?: string[] }
    | string[];
  permissions?: DatafnPermissionsPolicy;
};
PropertyTypeDescription
namestringUnique resource name. Used in queries, mutations, and API paths.
versionnumberInteger schema version. Increment when making breaking changes.
idPrefixstringOptional prefix for generated IDs. An idPrefix of "tsk" produces IDs like tsk_abc123.
isRemoteOnlybooleanWhen true, the resource is not synced to the client offline store. Queries always go to the server.
fieldsDatafnFieldSchema[]Array of field definitions. Max 200 fields per resource. See Fields.
indicesobject or string[]Index configuration for query optimization.
permissionsDatafnPermissionsPolicyField-level read/write access control.

Defining a Resource

import { defineSchema } from "@datafn/core";

const schema = defineSchema({
  resources: [
    {
      name: "articles",
      version: 1,
      idPrefix: "art",
      fields: [
        { name: "title", type: "string", required: true, maxLength: 500 },
        { name: "body", type: "string", required: true },
        { name: "publishedAt", type: "date", required: false },
        { name: "viewCount", type: "number", required: false, default: 0 },
        { name: "tags", type: "array", required: false },
        { name: "metadata", type: "json", required: false },
      ],
      indices: {
        base: ["title", "publishedAt"],
        search: ["body"],
      },
    },
  ],
});

Indices

Indices control how the database optimizes queries for the resource. There are three categories:

CategoryPurpose
baseStandard B-tree indices for equality and range filters.
searchFull-text search indices.
vectorVector similarity indices for embedding-based queries.

You can use the shorthand array syntax to define base indices only:

// Shorthand -- all entries become base indices
indices: ["title", "publishedAt"]

// Equivalent expanded form
indices: {
  base: ["title", "publishedAt"],
  search: [],
  vector: [],
}

All referenced field names must match declared fields on the resource. A type error (with defineSchema) or a validation error (with validateSchema) is raised for unknown field references.

Permissions

The permissions property defines field-level access control on the server.

type DatafnPermissionsPolicy = {
  read?: { fields: string[] };
  write?: { fields: string[] };
  ownerField?: string;
};
PropertyTypeDescription
read.fieldsstring[]Fields that can be selected and used in filters.
write.fieldsstring[]Fields that can be set via mutations.
ownerFieldstringOptional field name for owner-scoped authorization checks.
{
  name: "users",
  version: 1,
  fields: [
    { name: "email", type: "string", required: true, unique: true },
    { name: "displayName", type: "string", required: true },
    { name: "role", type: "string", required: true, enum: ["admin", "member"] },
    { name: "internalNotes", type: "string", required: false },
  ],
  permissions: {
    read: { fields: ["email", "displayName", "role"] },
    write: { fields: ["displayName"] },
    ownerField: "email",
  },
}

In this example, internalNotes is excluded from both read and write, making it inaccessible through the DataFn API. The ownerField enables owner-scoped authorization so users can only modify their own records.

Remote-Only Resources

Setting isRemoteOnly: true tells the client not to sync this resource to the local offline store. All queries for this resource go directly to the server.

{
  name: "auditLogs",
  version: 1,
  isRemoteOnly: true,
  fields: [
    { name: "action", type: "string", required: true },
    { name: "actor", type: "string", required: true },
    { name: "timestamp", type: "date", required: true },
    { name: "payload", type: "json", required: false },
  ],
  indices: { base: ["action", "actor", "timestamp"] },
}

This is useful for large or append-only datasets that do not benefit from client-side caching.