Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add drizzle-effect package - an integration with @effect/schema #2665

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions drizzle-effect/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<div align='center'>
<h1>drizzle-effect <a href=''><img alt='npm' src='https://img.shields.io/npm/v/drizzle-effect?label='></a></h1>
<img alt='npm' src='https://img.shields.io/npm/dm/drizzle-effect'>
<img alt='npm bundle size' src='https://img.shields.io/bundlephobia/min/drizzle-effect'>
<a href='https://discord.gg/yfjTbVXMW4'><img alt='Discord' src='https://img.shields.io/discord/1043890932593987624'></a>
<img alt='License' src='https://img.shields.io/npm/l/drizzle-effect'>
<h6><i>If you know SQL, you know Drizzle ORM</i></h6>
<hr />
</div>

`drizzle-effect` is a plugin for [Drizzle ORM](https://github.com/drizzle-team/drizzle-orm) that allows you to generate [Effect](https://effect.website/) schemas from Drizzle ORM schemas.

| Database | Insert schema | Select schema |
| :--------- | :-----------: | :-----------: |
| PostgreSQL | ✅ | ✅ |
| MySQL | ✅ | ✅ |
| SQLite | ✅ | ✅ |

# Usage

```ts
import { pgEnum, pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
import { createInsertSchema, createSelectSchema } from 'drizzle-effect';
import { Schema } from '@effect/schema';

const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
role: text('role', { enum: ['admin', 'user'] }).notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});

// Schema for inserting a user - can be used to validate API requests
const insertUserSchema = createInsertSchema(users);

// Schema for selecting a user - can be used to validate API responses
const selectUserSchema = createSelectSchema(users);

// Overriding the fields
const insertUserSchema = createInsertSchema(users, {
role: Schema.String,
});

const emailPattern = /[^ .@]+@[^ .@]+\.[^ .@]/;

// Refining the fields - useful if you want to change the fields before they become nullable/optional in the final schema
const insertUserSchema = createInsertSchema(users, {
id: (schema) => schema.id.pipe(Schema.positive()),
email: (schema) => schema.email.pipe(Schema.pattern(emailPattern)),
role: Schema.String,
});

// Usage

const user = Schema.decodeSync(insertUserSchema)({
name: 'John Doe',
email: 'johndoe@test.com',
role: 'admin',
});

// Effect schema type is also inferred from the table schema, so you have full type safety.
// Additionally it is created using `Schema.Struct`, so you can do:
const requestSchema = insertUserSchema.pick('name', 'email');
```
88 changes: 88 additions & 0 deletions drizzle-effect/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"name": "drizzle-effect",
"version": "0.0.1",
"description": "Generate `effect` schemas from Drizzle ORM schemas",
"type": "module",
"scripts": {
"build": "tsx scripts/build.ts",
"b": "pnpm build",
"test:types": "cd tests && tsc",
"pack": "(cd dist && npm pack --pack-destination ..) && rm -f package.tgz && mv *.tgz package.tgz",
"publish": "npm publish package.tgz",
"test": "vitest run"
},
"exports": {
".": {
"import": {
"types": "./index.d.mts",
"default": "./index.mjs"
},
"require": {
"types": "./index.d.cjs",
"default": "./index.cjs"
},
"types": "./index.d.ts",
"default": "./index.mjs"
}
},
"main": "./index.cjs",
"module": "./index.mjs",
"types": "./index.d.ts",
"publishConfig": {
"provenance": true
},
"repository": {
"type": "git",
"url": "git+https://github.com/drizzle-team/drizzle-orm.git"
},
"ava": {
"files": [
"tests/**/*.test.ts",
"!tests/bun/**/*"
],
"extensions": {
"ts": "module"
}
},
"keywords": [
"effect",
"effect-ts",
"@effect/schema",
"validate",
"validation",
"schema",
"drizzle",
"orm",
"pg",
"mysql",
"postgresql",
"postgres",
"sqlite",
"database",
"sql",
"typescript",
"ts"
],
"author": "Drizzle Team",
"license": "MIT",
"peerDependencies": {
"drizzle-orm": ">=0.23.13",
"@effect/schema": ">=0.69.1",
"effect": ">=3.5.6"
},
"devDependencies": {
"@rollup/plugin-terser": "^0.4.1",
"@rollup/plugin-typescript": "^11.1.0",
"@types/node": "^18.15.10",
"cpy": "^10.1.0",
"drizzle-orm": "link:../drizzle-orm/dist",
"rimraf": "^5.0.0",
"rollup": "^3.20.7",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0",
"@effect/schema": "^0.69.1",
"effect": "^3.5.6",
"zx": "^7.2.2"
},
"dependencies": {}
}
32 changes: 32 additions & 0 deletions drizzle-effect/rollup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import terser from '@rollup/plugin-terser';
import typescript from '@rollup/plugin-typescript';
import { defineConfig } from 'rollup';

export default defineConfig([
{
input: 'src/index.ts',
output: [
{
format: 'esm',
dir: 'dist',
entryFileNames: '[name].mjs',
chunkFileNames: '[name]-[hash].mjs',
sourcemap: true,
},
{
format: 'cjs',
dir: 'dist',
entryFileNames: '[name].cjs',
chunkFileNames: '[name]-[hash].cjs',
sourcemap: true,
},
],
external: [/^drizzle-orm\/?/, '@effect/schema', 'effect'],
plugins: [
typescript({
tsconfig: 'tsconfig.build.json',
}),
terser(),
],
},
]);
15 changes: 15 additions & 0 deletions drizzle-effect/scripts/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env -S pnpm tsx
import 'zx/globals';
import cpy from 'cpy';

await fs.remove('dist');
await $`rollup --config rollup.config.ts --configPlugin typescript`;
await $`resolve-tspaths`;
await fs.copy('README.md', 'dist/README.md');
await cpy('dist/**/*.d.ts', 'dist', {
rename: (basename) => basename.replace(/\.d\.ts$/, '.d.mts'),
});
await cpy('dist/**/*.d.ts', 'dist', {
rename: (basename) => basename.replace(/\.d\.ts$/, '.d.cts'),
});
await fs.copy('package.json', 'dist/package.json');
Loading