Generate ArkType schemas from your Prisma schema
npm install prisma-arktypeGenerate ArkType validation schemas from your Prisma schema.
This package is heavily inspired by and based on the structure of prismabox, which generates TypeBox schemas from Prisma schemas.
- 🎯 Type-safe validation - Generate ArkType schemas that match your Prisma models
- 🔄 Automatic generation - Schemas are generated automatically when you run prisma generate
- 📦 Comprehensive coverage - Generates schemas for models, relations, where clauses, select, include, orderBy, and more
- 🎨 Customizable - Control schema generation with annotations
- 🚀 Zero config - Works out of the box with sensible defaults
``bash`
npm install prisma-arktype arktypeor
pnpm add prisma-arktype arktypeor
yarn add prisma-arktype arktype
Add the generator to your schema.prisma file:
`prisma
generator prisma-arktype {
provider = "prisma-arktype"
output = "./generated/validators"
}
model User {
id String @id @default(cuid())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
`
Then run:
`bash`
npx prisma generate
Configure the generator in your schema.prisma:
`prisma`
generator prisma-arktype {
provider = "prisma-arktype"
output = "./generated/validators"
arktypeImportDependencyName = "arktype"
ignoredKeysOnInputModels = ["id", "createdAt", "updatedAt"]
}
#### Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| output | string | "./prisma/generated/validators" | Output directory for generated schemas |arktypeImportDependencyName
| | string | "arktype" | The package name to import from |ignoredKeysOnInputModels
| | string[] | ["id", "createdAt", "updatedAt"] | Fields to exclude from input models |
For each model, the generator creates multiple schema types:
- ModelPlain - Scalar fields only (strings, numbers, dates, enums) - no relations
- ModelRelations - Relationship fields only, referencing related model Plain types
- Model - Complete composite schema combining Plain & Relations
- ModelWhere - Where clause schema for filtering
- ModelWhereUnique - Unique where clause schema for finding specific records
- ModelCreate - Input schema for creating records
- ModelUpdate - Input schema for updating records
- ModelSelect - Schema for selecting specific fields
- ModelInclude - Schema for including relations
- ModelOrderBy - Schema for ordering results
Enums are generated as separate reusable types that are imported and referenced by models that use them.
`typescript
import { type } from "arktype";
import { User, UserCreate, UserWhere } from "./generated/validators";
// Validate a user object
const userResult = User(someUserData);
if (userResult instanceof type.errors) {
console.error(userResult.summary);
} else {
// userResult is validated user data
console.log(userResult);
}
// Validate create input
const createData = {
email: "user@example.com",
name: "John Doe"
};
const createResult = UserCreate(createData);
// ...
// Validate where clauses
const whereClause = {
email: "user@example.com"
};
const whereResult = UserWhere(whereClause);
// ...
`
Where clauses support advanced filtering through dedicated filter types. Fields can accept either direct values or filter objects with comparison operators.
#### String Filters
String fields can use StringFilter for advanced text filtering:
`typescript
import { UserWhere } from "./generated/validators";
// Direct value
const result1 = UserWhere({ email: "user@example.com" });
// Filter object
const result2 = UserWhere({
email: {
contains: "example", // Contains substring
startsWith: "user", // Starts with prefix
endsWith: ".com", // Ends with suffix
equals: "user@example.com", // Exact match
not: "admin@example.com", // Not equal to
in: ["user1@example.com", "user2@example.com"], // In array
notIn: ["banned@example.com"], // Not in array
gt: "a", // Greater than (lexicographic)
gte: "a", // Greater than or equal
lt: "z", // Less than
lte: "z" // Less than or equal
}
});
`
Available operations: contains, startsWith, endsWith, equals, not, in, notIn, gt, gte, lt, lte
#### Number Filters
Integer fields (Int, BigInt) use IntFilter, while floating-point fields (Float, Decimal) use NumberFilter:
`typescript
import { PostWhere } from "./generated/validators";
// Direct value
const result1 = PostWhere({ views: 100 });
// Filter object for integers
const result2 = PostWhere({
views: {
equals: 100,
gt: 50, // Greater than
gte: 50, // Greater than or equal
lt: 200, // Less than
lte: 200, // Less than or equal
in: [100, 200, 300], // In array
notIn: [0], // Not in array
not: 0 // Not equal to
}
});
// Filter object for floats/decimals
const result3 = PostWhere({
rating: {
gte: 4.5,
lte: 5.0
}
});
`
Available operations: equals, gt, gte, lt, lte, in, notIn, not
#### Boolean Filters
Boolean fields use BooleanFilter:
`typescript
import { PostWhere } from "./generated/validators";
// Direct value
const result1 = PostWhere({ published: true });
// Filter object
const result2 = PostWhere({
published: {
equals: true,
not: false
}
});
`
Available operations: equals, not
#### Enum Filters
Enum fields use the generic enumFilter:
`typescript
import { PaymentWhere } from "./generated/validators";
// Direct enum value
const result1 = PaymentWhere({ currency: "USD" });
// Filter object
const result2 = PaymentWhere({
currency: {
equals: "USD",
in: ["USD", "EUR", "GBP"],
notIn: ["JPY"],
not: "CAD"
}
});
`
Available operations: equals, in, notIn, not
#### DateTime Filters
DateTime fields use DateTimeFilter:
`typescript
import { PostWhere } from "./generated/validators";
// Direct Date value
const result1 = PostWhere({ createdAt: new Date("2024-01-01") });
// Filter object
const result2 = PostWhere({
createdAt: {
equals: new Date("2024-01-01"),
gt: new Date("2024-01-01"), // After
gte: new Date("2024-01-01"), // On or after
lt: new Date("2024-12-31"), // Before
lte: new Date("2024-12-31"), // On or before
in: [new Date("2024-01-01"), new Date("2024-06-01")],
notIn: [new Date("2024-07-04")],
not: new Date("2024-01-01")
}
});
`
Available operations: equals, gt, gte, lt, lte, in, notIn, not
#### Array Filters
Array fields use specialized array filters with operations for list matching:
`typescript
import { TagWhere } from "./generated/validators";
// String arrays
const result1 = TagWhere({
labels: {
isEmpty: false, // Array is empty
has: "important", // Array contains value
hasEvery: ["tag1", "tag2"], // Array contains all values
hasSome: ["tag1", "tag2"], // Array contains at least one value
equals: ["exact", "match"] // Array exactly matches
}
});
// Number arrays
const result2 = ScoresWhere({
values: {
isEmpty: false,
has: 100,
hasEvery: [90, 95, 100],
hasSome: [100, 200],
equals: [90, 95, 100]
}
});
// Enum arrays
const result3 = PermissionsWhere({
roles: {
isEmpty: false,
has: "ADMIN",
hasEvery: ["USER", "ADMIN"],
hasSome: ["ADMIN", "MODERATOR"],
equals: ["USER"]
}
});
`
Available array filter types:
- StringArrayFilter - for String[] fieldsNumberArrayFilter
- - for Int[], Float[], Decimal[] fieldsBigIntArrayFilter
- - for BigInt[] fieldsarrayFilter(EnumType)
- - for enum array fields
Available operations: isEmpty, has, hasEvery, hasSome, equals
You can combine multiple filters in a single where clause:
`typescript
import { PostWhere } from "./generated/validators";
const complexQuery = PostWhere({
title: { contains: "TypeScript" },
views: { gte: 100 },
published: true,
rating: { gte: 4.0 },
createdAt: {
gte: new Date("2024-01-01"),
lt: new Date("2024-12-31")
}
});
`
#### Enum Generation
For a Prisma enum like:
`prisma`
enum Currency {
USD
EUR
GBP
}
The generator creates a separate reusable type:
`typescript
// Currency.ts
import { type } from "arktype";
export const Currency = type("'USD' | 'EUR' | 'GBP'");
`
Which is then imported and used in models:
`typescript
// PaymentPlain.ts
import { type } from "arktype";
import { Currency } from "./Currency";
export const PaymentPlain = type({
"id": "string",
"amount": "number",
"currency": Currency, // Required enum
"status?": Currency.or("null") // Optional enum
});
`
#### Relation Generation
For Prisma models with relations like:
`prisma
model User {
id String @id
email String
posts Post[]
}
model Post {
id String @id
title String
author User @relation(fields: [authorId], references: [id])
authorId String
}
`
The generator creates Plain types (without relations):
`typescript
// UserPlain.ts
export const UserPlain = type({
"id": "string",
"email": "string"
});
// PostPlain.ts
export const PostPlain = type({
"id": "string",
"title": "string",
"authorId": "string"
});
`
And Relations types that reference the Plain types:
`typescript
// UserRelations.ts
import { PostPlain } from "./PostPlain";
export const UserRelations = type({
"posts": PostPlain.array() // Array of Post objects
});
// PostRelations.ts
import { UserPlain } from "./UserPlain";
export const PostRelations = type({
"author": UserPlain // Single User object
});
`
The combined model merges both:
`typescript
// User.ts
import { UserPlain } from "./UserPlain";
import { UserRelations } from "./UserRelations";
export const User = type(() => UserPlain.and(UserRelations));
`
Control schema generation using annotations in your Prisma schema. All annotations are added as documentation comments (///).
| Annotation | Scope | Description |
|------------|-------|-------------|
| @prisma-arktype.hide | Model or Field | Completely hide from all generated schemas |@prisma-arktype.input.hide
| | Field | Hide from Create and Update input schemas |@prisma-arktype.create.input.hide
| | Field | Hide from Create input schema only |@prisma-arktype.update.input.hide
| | Field | Hide from Update input schema only |@prisma-arktype.schema="
| | Field | Custom ArkType schema (inline or external) |@prisma-arktype.typeOverwrite="
| | Field | Override the generated ArkType type |
Completely exclude models or fields from all generated schemas:
`prisma
/// @prisma-arktype.hide
model InternalModel {
id String @id
secret String
}
model User {
id String @id
email String
/// @prisma-arktype.hide
passwordHash String
}
`
Control which fields appear in Create and Update schemas:
`prisma
model User {
id String @id
email String
/// @prisma-arktype.input.hide
/// Hidden from both Create and Update
computedField String
/// @prisma-arktype.create.input.hide
/// Only appears in Update schema
lastModified DateTime
/// @prisma-arktype.update.input.hide
/// Only appears in Create schema
initialStatus String
}
`
Override the default type mapping with custom ArkType type strings:
`prisma`
model User {
id String @id
/// @prisma-arktype.typeOverwrite="string.email"
email String
/// @prisma-arktype.typeOverwrite="string.url"
website String
/// @prisma-arktype.typeOverwrite="string.numeric"
phone String
}
This allows you to use any ArkType type definition, including built-in refinements like string.email, string.url, number.integer, etc.
Bring your own ArkType schemas for any field using @prisma-arktype.schema:
`prisma
model User {
id String @id
/// Inline schema for structured JSON
/// @prisma-arktype.schema="{ name: 'string', age: 'number' }"
profile Json
/// External schema from a file (named export)
/// @prisma-arktype.schema="../schemas/address:AddressSchema"
address Json
/// External schema (default export)
/// @prisma-arktype.schema="../schemas/config"
settings Json
}
`
Import Path Rules:
- Paths are relative to the generated validators directory
- Named exports use colon syntax: "path:ExportName""path"
- Default exports omit the colon:
- Works with ANY field type (not just Json)
Priority: schema > typeOverwrite > default type mapping
Example external schema file (schemas/address.ts):`typescript
import { type } from "arktype";
export const AddressSchema = type({
street: "string",
city: "string",
zipCode: "string",
country: "string",
});
`
Prisma types are mapped to ArkType as follows:
| Prisma Type | ArkType Type | Example Output |
|-------------|--------------|----------------|
| String | "string" | "string" |Int
| | "number.integer" | "number.integer" |BigInt
| | "number.integer" | "number.integer" |Float
| | "number" | "number" |Decimal
| | "number" | "number" |Boolean
| | "boolean" | "boolean" |DateTime
| | "Date" | "Date" |Json
| | "unknown" | "unknown" |Bytes
| | "instanceof Buffer" | "instanceof Buffer" |Currency
| Enums | Reference to enum type | (imported from ./Currency) |PostPlain
| Relations | Reference to related Plain type | or PostPlain.array() |
- Optional fields: Use ? on the key name ("name?": "string")| null
- Nullable fields: Add to the type ("string | null").array()
- Arrays: Use syntax for lists (type("string").array() or Currency.array())
- Enums: Generated as separate reusable type definitions and imported where used
- Relations: Reference the Plain type of the related model, imported automatically
While this package is inspired by prismabox, there are some key differences:
1. ArkType vs TypeBox: Uses ArkType's syntax and type system instead of TypeBox
2. Simpler type definitions: ArkType's string-based syntax makes schemas more readable
3. No nullable wrapper: ArkType handles nullable types directly with union syntax
4. Different validation API: Uses ArkType's validation approach
`bashClone the repository
git clone https://github.com/yourusername/prisma-arktype.git
cd prisma-arktype
$3
This library has a completely schema-independent test suite using self-contained test models in
prisma/schema/test-models.prisma.#### Running Tests
`bash
Run all tests
pnpm test:e2eRun tests in watch mode
pnpm test:watch
`#### Test Architecture
The test suite is designed to be 100% independent of production schemas:
- Self-Contained Schema -
prisma/schema/test-models.prisma contains all models needed for testing
- No Production Dependencies - Tests work even if production schemas don't exist
- Comprehensive Coverage - Test models cover all Prisma types, relations, and generator features
- Portable - Can be used across different projects or extracted as a standalone test suite#### Test Model Categories
The test schema includes specialized models for testing:
1. Basic CRUD -
TestUser, TestPost, TestProfile
2. All Prisma Types - TestAllTypes (String, Int, BigInt, Float, Decimal, Boolean, DateTime, Json, Bytes)
3. Relations - One-to-one, one-to-many, many-to-many, composite keys
4. Annotations - @prisma-arktype.hide, @prisma-arktype.input.hide, @prisma-arktype.typeOverwrite
5. Query Operations - Select, Include, OrderBy schemas
6. Enums - TestCurrency, TestStatus#### Adding New Tests
1. Add test models to
prisma/schema/test-models.prisma if needed
2. Update mapping in __tests__/config/model-mapping.ts to reference your models
3. Write tests using helper functions from __tests__/utils/test-helpers.ts
4. Run tests - pnpm testSee existing test files for examples.
#### Why Schema-Independent?
- ✅ Tests never break due to production schema changes
- ✅ Contributors can run tests without setting up production databases
- ✅ Tests can be run in isolation (CI/CD, local development)
- ✅ Clear, documented examples of generator usage
- ✅ Easy to test new features by adding new test models
$3
This project uses Changesets for version management and publishing.
#### Creating a changeset
When you make changes that should be included in the next release:
`bash
pnpm changeset
`This will prompt you to:
1. Select the type of change (major, minor, patch)
2. Provide a description of the changes
Commit the generated changeset file along with your changes.
#### Publishing workflow
1. Create a changeset for your changes
2. Open a PR with your changes and the changeset
3. Merge the PR - The GitHub Action will automatically create a "Version Packages" PR
4. Review and merge the Version Packages PR - This will:
- Update the version in package.json
- Update the CHANGELOG.md
- Publish the package to npm
- Create a GitHub release
#### Manual publishing (maintainers only)
`bash
Build and publish
pnpm release
`Prerequisites:
- Set up
NPM_TOKEN secret in GitHub repository settings
- Ensure you have publish access to the npm packageContributing
Contributions are welcome! Please feel free to submit a Pull Request.
1. Fork the repository
2. Create your feature branch (
git checkout -b feature/amazing-feature)
3. Make your changes
4. Create a changeset (pnpm changeset)
5. Commit your changes following the commit message format (see below)
6. Push to the branch (git push origin feature/amazing-feature)
7. Open a Pull Request$3
This project uses Conventional Commits. Commit messages are automatically linted using commitlint and lefthook.
Format:
Types:
-
feat: New feature
- fix: Bug fix
- docs: Documentation changes
- style: Code style changes (formatting, etc.)
- refactor: Code refactoring
- perf: Performance improvements
- test: Adding or updating tests
- build: Build system changes
- ci: CI/CD changes
- chore: Other changesExamples:
`bash
git commit -m "feat: add support for custom type validators"
git commit -m "fix: resolve issue with nullable DateTime fields"
git commit -m "docs: update installation instructions"
git commit -m "refactor: simplify where clause generation"
`$3
This project uses lefthook to manage git hooks:
- commit-msg: Validates commit message format
- pre-commit: Runs linter and checks for debug statements
- pre-push: Runs tests before pushing
To skip hooks (use sparingly):
`bash
git commit --no-verify -m "your message"
``MIT
This package is heavily based on prismabox by m1212e. Many thanks for the excellent foundation and architecture!
- ArkType - TypeScript's 1:1 validator
- Prisma - Next-generation ORM for Node.js & TypeScript
- prismabox - Generate TypeBox schemas from Prisma (inspiration for this project)