Skip to content
This repository has been archived by the owner on Dec 12, 2023. It is now read-only.

fix(deps): update dependency drizzle-orm to ^0.29.0 #91

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

renovate[bot]
Copy link

@renovate renovate bot commented Nov 10, 2023

Mend Renovate

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
drizzle-orm ^0.28.5 -> ^0.29.0 age adoption passing confidence

Release Notes

drizzle-team/drizzle-orm (drizzle-orm)

v0.29.1

Compare Source

Fixes
New Features/Helpers
🎉 Detailed JSDoc for all query builders in all dialects - thanks @​realmikesolo

You can now access more information, hints, documentation links, etc. while developing and using JSDoc right in your IDE. Previously, we had them only for filter expressions, but now you can see them for all parts of the Drizzle query builder

🎉 New helpers for aggregate functions in SQL - thanks @​L-Mario564

Remember, aggregation functions are often used with the GROUP BY clause of the SELECT statement. So if you are selecting using aggregating functions and other columns in one query,
be sure to use the .groupBy clause

Here is a list of functions and equivalent using sql template

count

await db.select({ value: count() }).from(users);
await db.select({ value: count(users.id) }).from(users);

// It's equivalent to writing
await db.select({ 
  value: sql`count('*'))`.mapWith(Number) 
}).from(users);
await db.select({ 
  value: sql`count(${users.id})`.mapWith(Number) 
}).from(users);

countDistinct

await db.select({ value: countDistinct(users.id) }).from(users);

// It's equivalent to writing
await db.select({ 
  value: sql`count(${users.id})`.mapWith(Number) 
}).from(users);

avg

await db.select({ value: avg(users.id) }).from(users);

// It's equivalent to writing
await db.select({ 
  value: sql`avg(${users.id})`.mapWith(String) 
}).from(users);

avgDistinct

await db.select({ value: avgDistinct(users.id) }).from(users);

// It's equivalent to writing
await db.select({ 
  value: sql`avg(distinct ${users.id})`.mapWith(String) 
}).from(users);

sum

await db.select({ value: sum(users.id) }).from(users);

// It's equivalent to writing
await db.select({ 
  value: sql`sum(${users.id})`.mapWith(String) 
}).from(users);

sumDistinct

await db.select({ value: sumDistinct(users.id) }).from(users);

// It's equivalent to writing
await db.select({ 
  value: sql`sum(distinct ${users.id})`.mapWith(String) 
}).from(users);

max

await db.select({ value: max(users.id) }).from(users);

// It's equivalent to writing
await db.select({ 
  value: sql`max(${expression})`.mapWith(users.id) 
}).from(users);

min

await db.select({ value: min(users.id) }).from(users);

// It's equivalent to writing
await db.select({ 
  value: sql`min(${users.id})`.mapWith(users.id) 
}).from(users);
New Packages
🎉 ESLint Drizzle Plugin

For cases where it's impossible to perform type checks for specific scenarios, or where it's possible but error messages would be challenging to understand, we've decided to create an ESLint package with recommended rules. This package aims to assist developers in handling crucial scenarios during development

Big thanks to @​Angelelz for initiating the development of this package and transferring it to the Drizzle Team's npm

Install
[ npm | yarn | pnpm | bun ] install eslint eslint-plugin-drizzle

You can install those packages for typescript support in your IDE

[ npm | yarn | pnpm | bun ] install @​typescript-eslint/eslint-plugin @​typescript-eslint/parser
Usage

Create a .eslintrc.yml file, add drizzle to the plugins, and specify the rules you want to use. You can find a list of all existing rules below

root: true
parser: '@​typescript-eslint/parser'
parserOptions:
  project: './tsconfig.json'
plugins:
  - drizzle
rules:
  'drizzle/enforce-delete-with-where': "error"
  'drizzle/enforce-update-with-where': "error"
All config

This plugin exports an all config that makes use of all rules (except for deprecated ones).

root: true
extends:
  - "plugin:drizzle/all"
parser: '@​typescript-eslint/parser'
parserOptions:
  project: './tsconfig.json'
plugins:
  - drizzle

At the moment, all is equivalent to recommended

root: true
extends:
  - "plugin:drizzle/recommended"
parser: '@​typescript-eslint/parser'
parserOptions:
  project: './tsconfig.json'
plugins:
  - drizzle
Rules

enforce-delete-with-where: Enforce using delete with the.where() clause in the .delete() statement. Most of the time, you don't need to delete all rows in the table and require some kind of WHERE statements.

Error Message:

Without `.where(...)` you will delete all the rows in a table. If you didn't want to do it, please use `db.delete(...).where(...)` instead. Otherwise you can ignore this rule here

Optionally, you can define a drizzleObjectName in the plugin options that accept a string or string[]. This is useful when you have objects or classes with a delete method that's not from Drizzle. Such a delete method will trigger the ESLint rule. To avoid that, you can define the name of the Drizzle object that you use in your codebase (like db) so that the rule would only trigger if the delete method comes from this object:

Example, config 1:

"rules": {
  "drizzle/enforce-delete-with-where": ["error"]
}
class MyClass {
  public delete() {
    return {}
  }
}

const myClassObj = new MyClass();

// ---> Will be triggered by ESLint Rule
myClassObj.delete()

const db = drizzle(...)
// ---> Will be triggered by ESLint Rule
db.delete()

Example, config 2:

"rules": {
  "drizzle/enforce-delete-with-where": ["error", { "drizzleObjectName": ["db"] }],
}
class MyClass {
  public delete() {
    return {}
  }
}

const myClassObj = new MyClass();

// ---> Will NOT be triggered by ESLint Rule
myClassObj.delete()

const db = drizzle(...)
// ---> Will be triggered by ESLint Rule
db.delete()

enforce-update-with-where: Enforce using update with the.where() clause in the .update() statement. Most of the time, you don't need to update all rows in the table and require some kind of WHERE statements.

Error Message:

Without `.where(...)` you will update all the rows in a table. If you didn't want to do it, please use `db.update(...).set(...).where(...)` instead. Otherwise you can ignore this rule here

Optionally, you can define a drizzleObjectName in the plugin options that accept a string or string[]. This is useful when you have objects or classes with a delete method that's not from Drizzle. Such as update method will trigger the ESLint rule. To avoid that, you can define the name of the Drizzle object that you use in your codebase (like db) so that the rule would only trigger if the delete method comes from this object:

Example, config 1:

"rules": {
  "drizzle/enforce-update-with-where": ["error"]
}
class MyClass {
  public update() {
    return {}
  }
}

const myClassObj = new MyClass();

// ---> Will be triggered by ESLint Rule
myClassObj.update()

const db = drizzle(...)
// ---> Will be triggered by ESLint Rule
db.update()

Example, config 2:

"rules": {
  "drizzle/enforce-update-with-where": ["error", { "drizzleObjectName": ["db"] }],
}
class MyClass {
  public update() {
    return {}
  }
}

const myClassObj = new MyClass();

// ---> Will NOT be triggered by ESLint Rule
myClassObj.update()

const db = drizzle(...)
// ---> Will be triggered by ESLint Rule
db.update()

v0.29.0

Compare Source

Drizzle ORM version 0.29.0 will require a minimum Drizzle Kit version of 0.20.0, and vice versa. Therefore, when upgrading to a newer version of Drizzle ORM, you will also need to upgrade Drizzle Kit. This may result in some breaking changes throughout the versions, especially if you need to upgrade Drizzle Kit and your Drizzle ORM version is older than <0.28.0

New Features
🎉 MySQL unsigned option for bigint

You can now specify bigint unsigned type

const table = mysqlTable('table', {
  id: bigint('id', { mode: 'number', unsigned: true }),
});

Read more in docs

🎉 Improved query builder types

Starting from 0.29.0 by default, as all the query builders in Drizzle try to conform to SQL as much as possible, you can only invoke most of the methods once. For example, in a SELECT statement there might only be one WHERE clause, so you can only invoke .where() once:

const query = db
  .select()
  .from(users)
  .where(eq(users.id, 1))
  .where(eq(users.name, 'John')); // ❌ Type error - where() can only be invoked once

This behavior is useful for conventional query building, i.e. when you create the whole query at once. However, it becomes a problem when you want to build a query dynamically, i.e. if you have a shared function that takes a query builder and enhances it. To solve this problem, Drizzle provides a special 'dynamic' mode for query builders, which removes the restriction of invoking methods only once. To enable it, you need to call .$dynamic() on a query builder.

Let's see how it works by implementing a simple withPagination function that adds LIMIT and OFFSET clauses to a query based on the provided page number and an optional page size:

function withPagination<T extends PgSelect>(
  qb: T,
  page: number,
  pageSize: number = 10,
) {
  return qb.limit(pageSize).offset(page * pageSize);
}

const query = db.select().from(users).where(eq(users.id, 1));
withPagination(query, 1); // ❌ Type error - the query builder is not in dynamic mode

const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // ✅ OK

Note that the withPagination function is generic, which allows you to modify the result type of the query builder inside it, for example by adding a join:

function withFriends<T extends PgSelect>(qb: T) {
  return qb.leftJoin(friends, eq(friends.userId, users.id));
}

let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);

Read more in docs

🎉 Possibility to specify name for primary keys and foreign keys

There is an issue when constraint names exceed the 64-character limit of the database. This causes the database engine to truncate the name, potentially leading to issues. Starting from 0.29.0, you have the option to specify custom names for both primaryKey() and foreignKey(). We have also deprecated the old primaryKey() syntax, which can still be used but will be removed in future releases

const table = pgTable('table', {
  id: integer('id'),
  name: text('name'),
}, (table) => ({
  cpk: primaryKey({ name: 'composite_key', columns: [table.id, table.name] }),
  cfk: foreignKey({
    name: 'fkName',
    columns: [table.id],
    foreignColumns: [table.name],
  }),
}));

Read more in docs

🎉 Read Replicas Support

You can now use the Drizzle withReplica function to specify different database connections for read replicas and the main instance for write operations. By default, withReplicas will use a random read replica for read operations and the main instance for all other data modification operations. You can also specify custom logic for choosing which read replica connection to use. You have the freedom to make any weighted, custom decision for that. Here are some usage examples:

const primaryDb = drizzle(client);
const read1 = drizzle(client);
const read2 = drizzle(client);

const db = withReplicas(primaryDb, [read1, read2]);

// read from primary
db.$primary.select().from(usersTable);

// read from either read1 connection or read2 connection
db.select().from(usersTable)

// use primary database for delete operation
db.delete(usersTable).where(eq(usersTable.id, 1))

Implementation example of custom logic for selecting read replicas, where the first replica has a 70% chance of being chosen, and the second replica has a 30% chance of being chosen. Note that you can implement any type of random selection for read replicas

const db = withReplicas(primaryDb, [read1, read2], (replicas) => {
    const weight = [0.7, 0.3];
    let cumulativeProbability = 0;
    const rand = Math.random();

    for (const [i, replica] of replicas.entries()) {
      cumulativeProbability += weight[i]!;
      if (rand < cumulativeProbability) return replica;
    }
    return replicas[0]!
});

withReplicas function is available for all dialects in Drizzle ORM

Read more in docs

🎉 Set operators support (UNION, UNION ALL, INTERSECT, INTERSECT ALL, EXCEPT, EXCEPT ALL)

Huge thanks to @​Angelelz for the significant contribution he made, from API discussions to proper type checks and runtime logic, along with an extensive set of tests. This greatly assisted us in delivering this feature in this release

Usage examples:
All set operators can be used in a two ways: import approach or builder approach

Import approach
import { union } from 'drizzle-orm/pg-core'

const allUsersQuery = db.select().from(users);
const allCustomersQuery = db.select().from(customers);

const result = await union(allUsersQuery, allCustomersQuery)
Builder approach
const result = await db.select().from(users).union(db.select().from(customers));

Read more in docs

🎉 New MySQL Proxy Driver

A new driver has been released, allowing you to create your own implementation for an HTTP driver using a MySQL database. You can find usage examples in the ./examples/mysql-proxy folder

You need to implement two endpoints on your server that will be used for queries and migrations(Migrate endpoint is optional and only if you want to use drizzle migrations). Both the server and driver implementation are up to you, so you are not restricted in any way. You can add custom mappings, logging, and much more

You can find both server and driver implementation examples in the ./examples/mysql-proxy folder

// Driver
import axios from 'axios';
import { eq } from 'drizzle-orm/expressions';
import { drizzle } from 'drizzle-orm/mysql-proxy';
import { migrate } from 'drizzle-orm/mysql-proxy/migrator';
import { cities, users } from './schema';

async function main() {
  const db = drizzle(async (sql, params, method) => {
    try {
      const rows = await axios.post(`${process.env.REMOTE_DRIVER}/query`, {
        sql,
        params,
        method,
      });

      return { rows: rows.data };
    } catch (e: any) {
      console.error('Error from pg proxy server:', e.response.data);
      return { rows: [] };
    }
  });

  await migrate(db, async (queries) => {
    try {
      await axios.post(`${process.env.REMOTE_DRIVER}/migrate`, { queries });
    } catch (e) {
      console.log(e);
      throw new Error('Proxy server cannot run migrations');
    }
  }, { migrationsFolder: 'drizzle' });

  await db.insert(cities).values({ id: 1, name: 'name' });

  await db.insert(users).values({
    id: 1,
    name: 'name',
    email: 'email',
    cityId: 1,
  });

  const usersToCityResponse = await db.select().from(users).leftJoin(
    cities,
    eq(users.cityId, cities.id),
  );
}
🎉 New PostgreSQL Proxy Driver

Same as MySQL you can now implement your own http driver for PostgreSQL database. You can find usage examples in the ./examples/pg-proxy folder

You need to implement two endpoints on your server that will be used for queries and migrations (Migrate endpoint is optional and only if you want to use drizzle migrations). Both the server and driver implementation are up to you, so you are not restricted in any way. You can add custom mappings, logging, and much more

You can find both server and driver implementation examples in the ./examples/pg-proxy folder

import axios from 'axios';
import { eq } from 'drizzle-orm/expressions';
import { drizzle } from 'drizzle-orm/pg-proxy';
import { migrate } from 'drizzle-orm/pg-proxy/migrator';
import { cities, users } from './schema';

async function main() {
  const db = drizzle(async (sql, params, method) => {
    try {
      const rows = await axios.post(`${process.env.REMOTE_DRIVER}/query`, { sql, params, method });

      return { rows: rows.data };
    } catch (e: any) {
      console.error('Error from pg proxy server:', e.response.data);
      return { rows: [] };
    }
  });

  await migrate(db, async (queries) => {
    try {
      await axios.post(`${process.env.REMOTE_DRIVER}/query`, { queries });
    } catch (e) {
      console.log(e);
      throw new Error('Proxy server cannot run migrations');
    }
  }, { migrationsFolder: 'drizzle' });

  const insertedCity = await db.insert(cities).values({ id: 1, name: 'name' }).returning();
  const insertedUser = await db.insert(users).values({ id: 1, name: 'name', email: 'email', cityId: 1 });
  const usersToCityResponse = await db.select().from(users).leftJoin(cities, eq(users.cityId, cities.id));
}
🎉 D1 Batch API support

Reference: https://developers.cloudflare.com/d1/platform/client-api/#dbbatch

Batch API usage example:

const batchResponse = await db.batch([
  db.insert(usersTable).values({ id: 1, name: 'John' }).returning({
    id: usersTable.id,
  }),
  db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)),
  db.query.usersTable.findMany({}),
  db.select().from(usersTable).where(eq(usersTable.id, 1)),
  db.select({ id: usersTable.id, invitedBy: usersTable.invitedBy }).from(
    usersTable,
  ),
]);

Type for batchResponse in this example would be:

type BatchResponse = [
  {
    id: number;
  }[],
  D1Result,
  {
    id: number;
    name: string;
    verified: number;
    invitedBy: number | null;
  }[],
  {
    id: number;
    name: string;
    verified: number;
    invitedBy: number | null;
  }[],
  {
    id: number;
    invitedBy: number | null;
  }[],
];

All possible builders that can be used inside db.batch:

`db.all()`,
`db.get()`,
`db.values()`,
`db.run()`,
`db.query.<table>.findMany()`,
`db.query.<table>.findFirst()`,
`db.select()...`,
`db.update()...`,
`db.delete()...`,
`db.insert()...`,

More usage examples here: integration-tests/tests/d1-batch.test.ts and in docs


Drizzle Kit 0.20.0
  1. New way to define drizzle.config using defineConfig function
  2. Possibility to access Cloudflare D1 with Drizzle Studio using wrangler.toml file
  3. Drizzle Studio is migrating to https://local.drizzle.studio/
  4. bigint unsigned support
  5. primaryKeys and foreignKeys now can have custom names
  6. Environment variables are now automatically fetched
  7. Some bug fixes and improvements

You can read more about drizzle-kit updates here


Configuration

📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate. View repository job log here.

Copy link
Author

renovate bot commented Nov 10, 2023

⚠ Artifact update problem

Renovate failed to update an artifact related to this branch. You probably do not want to merge this PR as-is.

♻ Renovate will retry this branch, including artifacts, only when one of the following happens:

  • any of the package files in this branch needs updating, or
  • the branch becomes conflicted, or
  • you click the rebase/retry checkbox if found above, or
  • you rename this PR's title to start with "rebase!" to trigger it manually

The artifact failure details are included below:

File name: package-lock.json
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: undefined@undefined
npm ERR! Found: @cloudflare/workers-types@3.19.0
npm ERR! node_modules/@cloudflare/workers-types
npm ERR!   dev @cloudflare/workers-types@"^3.19.0" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer @cloudflare/workers-types@"^4.0.0" from @remix-run/cloudflare@2.2.0
npm ERR! node_modules/@remix-run/cloudflare
npm ERR!   @remix-run/cloudflare@"^2.0.0" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! 
npm ERR! For a full report see:
npm ERR! /tmp/worker/400b8d/f3207a/cache/others/npm/_logs/2023-11-10T08_38_11_577Z-eresolve-report.txt

npm ERR! A complete log of this run can be found in: /tmp/worker/400b8d/f3207a/cache/others/npm/_logs/2023-11-10T08_38_11_577Z-debug-0.log

Copy link

cloudflare-workers-and-pages bot commented Nov 10, 2023

Deploying with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8bd4319
Status: ✅  Deploy successful!
Preview URL: https://57782037.washboard.pages.dev
Branch Preview URL: https://renovate-drizzle-orm-0-x.washboard.pages.dev

View logs

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants