TypeScript SDK for Xata

The Xata SDK supports TypeScript definitions for your Xata database schema. It also works with JavaScript.

It has zero dependencies and can run in Node.js, V8, Deno, and Bun.

We recommend installing the Xata CLI globally and importing the Xata Client from the code generated by the CLI.

When initializing the Xata CLI with the command xata init in a project, the @xata.io/client package is installed locally using the current project's package manager. In the event a package manager can't be determined, the Xata CLI will prompt you to manually install the @xata.io/client package.

There are three ways to use the SDK:

  1. Schema-generated Client: SDK to create/read/update/delete records in a given database following a schema file (with type-safety).
  2. Schema-less Client: SDK to create/read/update/delete records in any database without schema validation (with partial type-safety).
  3. API Client: SDK to interact with the whole Xata API and all its endpoints.

The TypeScript SDK provides helper methods for creating types based off of Xata client-generated types. The following are exported on the @xata.io/client package:

  • SelectableColumn provides a type with the valid columns for a record.
  • SelectedPick returns a subtype of the record with the requested fields.
  • EditableData removes built-in record methods and reserved fields.

In the example below we create the type myUser with a selection of fields from the client-generated UserRecord:

import { getXataClient } from './xata';
import { SelectedPick, SelectableColumn, EditableData } from '@xata.io/client';
import { UserRecord } from './xata';
 
const xata = getXataClient();
 
const fields: SelectableColumn<UserRecord>[] = ['id', 'name', 'address'];
 
type myUser = EditableData<SelectedPick<UserRecord, typeof fields>>;

Systems such as Next.js expect the server to send serialized data-structures to the client. Since the Xata SDK returns records as objects as well as strings, there's a need to serialize content for client-side use. Serialization transforms the data into a structured text format that can be easily sent and rebuilt on the client side.

Xata provides the .toSerializable() method within the record objects to help with serialization. To revert serialized data back to its original types on the client side, you can use the JSONData helper from @xata.io/client.

An example of a common pattern for this flow in a Next.js system:

/app/page.tsx
import { getXataClient } from './xata';
 
const xata = getXataClient();
 
export default async function Page({ params }: { params: { slug: string } }) {
  // Pull records from the "users" table
  const records = await xata.db.users.getMany();
 
  // Serialize that content, turning any objects into strings
  const serializedRecords = records.toSerializable();
 
  // Pass the serialized content to the client component
  return <MyComponent records={serializedRecords} />;
}

The MyComponent client component can then use JSONData from the client to match against Xata's auto-generated types as shown below.

/components/MyComponent.tsx
'use client';
import { FC } from 'react';
import { JSONData } from '@xata.io/client';
import { UserRecord } from './xata';
 
interface MyComponentProps {
  // The data we serialized still matches the UserRecord shape
  records: JSONData<UserRecord>;
}
 
export const MyComponent: FC<MyComponentProps> = ({ records }) => {
  return (
    <>
      {records.map((record) => {
        // Date objects were converted to strings during serialization.
        // We can always revert them to their original form here in the client, as needed
        const date = new Date(record.createdAt);
        const dateFormatted = date.toLocaleString();
        return <p>{dateFormatted}</p>;
      })}
    </>
  );
};

You should not edit your generated file (e.g. ./src/xata.ts) since any changes to your schema will cause the file to rebuild when xata codegen is run. If you need to override the configuration, you can set up a separate file alongside the generated code.

export const getXataClient = () => {
  if (instance) return instance;
 
  instance = new XataClient({
    // Override configuration here
    databaseURL: process.env.XATA_DATABASE_URL,
    apiKey: process.env.XATA_API_KEY,
    fetch: fetch,
    branch: process.env.XATA_BRANCH
    // ... other configuration
  });
  return instance;
};

Some frameworks may not be compatible with parsing credentials from .env, instead requiring to pass your API key manually with the apiKey constructor option:

import { XataClient } from './xata';
const xata = new XataClient({ apiKey: XATA_API_KEY });

In case your runtime does not provide a built-in Fetch API (such as with versions prior to Node 18), you will need to pass a fetch implementation to the fetch constructor parameter of the XataClient in the xata.ts / xata.js file in your project.

The command xata codegen updates the schema for the Xata client. Applying schema changes from the CLI, such as using the command xata schema edit, will automatically run codegen to update your client. However, in case schema changes have been applied from the web UI, you should run xata codegen to update your existing client with the latest schema.

Type recursion is limited to 3 levels in the TypeScript SDK. This is to prevent excessive strain on both the type checker and the language server since each nested level adds computational complexity.

Calls on deeper nested columns will still work, but a TypeScript error may be displayed indicating the type cannot be determined.