WARNING

Starting from drizzle-orm@1.0.0-beta.15, drizzle-arktype has been deprecated in favor of first-class schema generation support within Drizzle ORM itself

You can still use drizzle-arktype package but all new update will be added to Drizzle ORM directly

arktype

Install the dependencies

npm
yarn
pnpm
bun
npm i arktype

Select schema

Defines the shape of data queried from the database - can be used to validate API responses.

import { int, singlestoreTable, text } from 'drizzle-orm/singlestore-core';
import { createSelectSchema } from 'drizzle-orm/arktype';

const users = singlestoreTable('users', {
  id: int().primaryKey().autoincrement(),
  name: text().notNull(),
  age: int().notNull()
});

const userSelectSchema = createSelectSchema(users);

const rows = await db.select({ id: users.id, name: users.name }).from(users).limit(1);
const parsed: { id: number; name: string; age: number } = userSelectSchema(rows[0]); // Error: `age` is not returned in the above query

const rows = await db.select().from(users).limit(1);
const parsed: { id: number; name: string; age: number } = userSelectSchema(rows[0]); // Will parse successfully

Column enums are supported through the SingleStore table schema and can be validated with the generated table schemas.

Insert schema

Defines the shape of data to be inserted into the database - can be used to validate API requests.

import { int, singlestoreTable, text } from 'drizzle-orm/singlestore-core';
import { createInsertSchema } from 'drizzle-orm/arktype';

const users = singlestoreTable('users', {
  id: int().primaryKey().autoincrement(),
  name: text().notNull(),
  age: int().notNull()
});

const userInsertSchema = createInsertSchema(users);

const user = { name: 'John' };
const parsed: { name: string, age: number } = userInsertSchema(user); // Error: `age` is not defined

const user = { name: 'Jane', age: 30 };
const parsed: { name: string, age: number } = userInsertSchema(user); // Will parse successfully
await db.insert(users).values(parsed);

Update schema

Defines the shape of data to be updated in the database - can be used to validate API requests.

import { int, singlestoreTable, text } from 'drizzle-orm/singlestore-core';
import { createUpdateSchema } from 'drizzle-orm/arktype';
import { parse } from 'arktype';

const users = singlestoreTable('users', {
  id: int().primaryKey().autoincrement(),
  name: text().notNull(),
  age: int().notNull()
});

const userUpdateSchema = createUpdateSchema(users);

const user = { id: 5, name: 'John' };
const parsed: { name?: string | undefined, age?: number | undefined } = userUpdateSchema(user); // Error: `id` is a generated column, it can't be updated

const user = { age: 35 };
const parsed: { name?: string | undefined, age?: number | undefined } = userUpdateSchema(user); // Will parse successfully
await db.update(users).set(parsed).where(eq(users.name, 'Jane'));

Refinements

Each create schema function accepts an additional optional parameter that you can used to extend, modify or completely overwite a field’s schema. Defining a callback function will extend or modify while providing a arktype schema will overwrite it.

import { int, json, singlestoreTable, text } from 'drizzle-orm/singlestore-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import { parse, pipe, maxLength, object, string } from 'arktype';

const users = singlestoreTable('users', {
  id: int().primaryKey().autoincrement(),
  name: text().notNull(),
  bio: text(),
  preferences: json()
});

const userSelectSchema = createSelectSchema(users, {
  name: (schema) => pipe(schema, maxLength(20)), // Extends schema
  bio: (schema) => pipe(schema, maxLength(1000)), // Extends schema before becoming nullable/optional
  preferences: object({ theme: string() }) // Overwrites the field, including its nullability
});

const parsed: {
  id: number;
  name: string,
  bio?: string | undefined;
  preferences: {
    theme: string;
  };
} = userSelectSchema(...);

Data type reference

singlestore.boolean();

// Schema
type.boolean;
singlestore.date({ mode: 'date' });
singlestore.datetime({ mode: 'date' });
singlestore.timestamp({ mode: 'date' });

// Schema
type.Date;
singlestore.binary();
singlestore.date({ mode: 'string' });
singlestore.datetime({ mode: 'string' });
singlestore.decimal();
singlestore.time();
singlestore.timestamp({ mode: 'string' });
singlestore.varbinary();

// Schema
type.string;
singlestore.char({ length: ... });

// Schema
type.string.exactlyLength(length);
singlestore.varchar({ length: ... });

// Schema
type.string.atMostLength(length);
singlestore.text();

// Schema
type.string.atMostLength(65_535); // unsigned 16-bit integer limit
singlestore.text({ enum: ... });
singlestore.char({ enum: ... });
singlestore.varchar({ enum: ... });
singlestore.singlestoreEnum(..., ...);

// Schema
type.enumerated(...enum);
singlestore.tinyint();

// Schema
type.keywords.number.integer.atLeast(-128).atMost(127); // 8-bit integer lower and upper limit
singlestore.tinyint({ unsigned: true });

// Schema
type.keywords.number.integer.atLeast(0).atMost(255); // unsigned 8-bit integer lower and upper limit
singlestore.smallint();

// Schema
type.keywords.number.integer.atLeast(-32_768).atMost(32_767); // 16-bit integer lower and upper limit
singlestore.smallint({ unsigned: true });

// Schema
type.keywords.number.integer.atLeast(0).atMost(65_535); // unsigned 16-bit integer lower and upper limit
singlestore.float();

// Schema
type.number.atLeast(-8_388_608).atMost(8_388_607); // 24-bit integer lower and upper limit
singlestore.mediumint();

// Schema
type.keywords.number.integer.atLeast(-8_388_608).atMost(8_388_607); // 24-bit integer lower and upper limit
singlestore.float({ unsigned: true });

// Schema
type.number.atLeast(0).atMost(16_777_215); // unsigned 24-bit integer lower and upper limit
singlestore.mediumint({ unsigned: true });

// Schema
type.keywords.number.integer.atLeast(0).atMost(16_777_215); // unsigned 24-bit integer lower and upper limit
singlestore.int();

// Schema
type.keywords.number.integer.atLeast(-2_147_483_648).atMost(2_147_483_647); // 32-bit integer lower and upper limit
singlestore.int({ unsigned: true });

// Schema
type.keywords.number.integer.atLeast(0).atMost(4_294_967_295); // unsgined 32-bit integer lower and upper limit
singlestore.double();
singlestore.real();

// Schema
type.number.atLeast(-140_737_488_355_328).atMost(140_737_488_355_327); // 48-bit integer lower and upper limit
singlestore.double({ unsigned: true });

// Schema
type.number.atLeast(0).atMost(281_474_976_710_655); // unsigned 48-bit integer lower and upper limit
singlestore.bigint({ mode: 'number' });

// Schema
type.keywords.number.integer.atLeast(-9_007_199_254_740_991).atMost(9_007_199_254_740_991); // Javascript min. and max. safe integers
singlestore.serial();

// Schema
type.keywords.number.integer.atLeast(0).atMost(9_007_199_254_740_991); // Javascript max. safe integer
singlestore.bigint({ mode: 'bigint' });

// Schema
type.bigint.narrow(
  (value, ctx) => value < -9_223_372_036_854_775_808n ? ctx.mustBe('greater than') : value > 9_223_372_036_854_775_807n ? ctx.mustBe('less than') : true
); // 64-bit integer lower and upper limit
singlestore.bigint({ mode: 'bigint', unsigned: true });

// Schema
type.bigint.narrow(
  (value, ctx) => value < 0n ? ctx.mustBe('greater than') : value > 18_446_744_073_709_551_615n ? ctx.mustBe('less than') : true
); // unsigned 64-bit integer lower and upper limit
singlestore.year();

// Schema
type.keywords.number.integer.atLeast(1_901).atMost(2_155);
singlestore.json();

// Schema
type('string | number | boolean | null').or(type('unknown.any[] | Record<string, unknown.any>'));