npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

supabase-drizzle

v1.1.3

Published

Manage Postgres RLS policies with Drizzle-like syntax, just as you manage your Postgres schema

Downloads

70

Readme

supabase-drizzle 🚀

npm version License CI - Test and Build Release to NPM


⚰️ Deprecation Notice ⚰️

This package was created to plug a gap when Drizzle ORM did not support RLS. Now that it does, there is no reason to use or maintain this package. Please follow the Drizzle ORM RLS with Supabase docs here instead.


Motivation 🔥

Drizzle ORM makes managing your database schema in TypeScript and handling migrations simple. However, managing RLS policies still requires manual SQL or using the Supabase dashboard, which can slow down your workflow.

supabase-drizzle aims to streamline this process, making RLS policy management as easy and intuitive as managing your schema with Drizzle, so you can move faster.


Installation 📦

To install supabase-drizzle, simply run:

yarn add supabase-drizzle
# or
npm install supabase-drizzle

Peer Dependencies 🤜🤛

Make sure you have the following peer dependencies installed in your project:

yarn add drizzle-kit drizzle-orm
# or
npm install drizzle-kit drizzle-orm

Usage ⚡️

Step 1

Follow the Drizzle setup guide for Supabase, then come back here and carry on to step 2.

Step 2

Add a file policies.config.ts in the same directory as your drizzle.config.ts file.

Step 3

In drizzle.config.ts, import defineConfig from supabase-drizzle instead of drizzle-kit:

- import { defineConfig } from 'drizzle-kit';
+ import { defineConfig } from 'supabase-drizzle';

Your config file will now support a policies option. This should point to the policies file policies.config.ts you made in step 2:

// drizzle.config.ts

import { defineConfig } from 'supabase-drizzle';
import { config } from 'dotenv';

config({ path: '.env' });

export default defineConfig({
  schema: 'drizzle/schema.ts',
  out: 'drizzle/migrations',
  dialect: 'postgresql',
  dbCredentials: {
    url: process.env.DATABASE_URL,
  },
  schemaFilter: ['public'],
  policies: 'drizzle/policies.ts'
});

Step 4

Define RLS policies for your tables in policies.config.ts. This is a basic example, see examples below for user role and multi-tenant configurations:

// policies.config.ts

import { rlsPolicyBuilder } from 'supabase-drizzle';
import * as schema from './schema';

const { tables, own, rls, authenticated, everyone } = rlsPolicyBuilder(schema, {});

const profiles = rls(tables.profiles, {
  insert: authenticated(), // User can create their profile row if authenticated
  update: own(), // User can update their own profile row
  select: everyone(), // Everyone can select all profile rows
});

const tenantUserRoles = rls(tables.todos, {
  insert: authenticated(), // User can create a todo row if authenticated
  update: own(), // User can update their own todo row
  select: own(), // User can select their own todo row
  delete: own(), // User can delete their own todo row
});

// Make sure your policies are exported from the file!
export { profiles, tenantUserRoles };

Step 5

Generate your RLS policy migration:

# Make sure to point to the correct config file location for your project

npx supabase-drizzle generate --config drizzle/drizzle.config.ts

supabase-drizzle generate outputs the location of the generated SQL migration file for your RLS policies. I suggest you manually check this for errors.

Step 6

Run the migration using drizzle:

# Make sure to point to the correct config file location for your project

npx drizzle-kit migrate --config drizzle/drizzle.config.ts

Step 7

For ease of use, consider adding these scripts to your package.json (Make sure to point to the correct config file location for your project):

{

  "scripts": {
    "drizzle-generate-schema": "npx drizzle-kit generate --config drizzle/drizzle.config.ts",
    "drizzle-generate-rls": "npx supabase-drizzle generate --config drizzle/drizzle.config.ts",
    "drizzle-migrate": "npx drizzle-kit migrate --config drizzle/drizzle.config.ts",
  }
}

Then you can sync your local schema and RLS policies to Supabase in one go using:

yarn drizzle-generate-schema && drizzle-generate-rls && yarn drizzle-migrate
#or
npm run drizzle-generate-schema && npm run drizzle-generate-rls && npm run yarn drizzle-migrate

Usage 📚

rlsPolicyBuilder(schema, rlsConfig)

Returns functions that allow you to define RLS policies for your tables.

// policies.config.ts

import { rlsPolicyBuilder } from 'supabase-drizzle';
import * as schema from './schema';

const {
  tables, // Strongly typed table names object based on your schema
  roles, // Strong typed user roles object based on your RLS config
  rls, // Function to define RLS policies for a table
  own, // RLS policy to allow access where the row's user_id matches the user's uid
  authenticated // RLS policy to allow access to all authenticated users
  hasRole, // RLS policy to allow access if a user has a role
  belongsTenant, // RLS policy that allows access to all users belonging to a tenant (used only in multi-tenant configs)
  everyone // RLS policy that allows access to all users
} = rlsPolicyBuilder(
  schema, // Your drizzle schema (only single file schema supported)
  {
    tenantsTable: schema.tenants, // Optional - used for multi-tenant setups
    userRolesTable: schema.userRoles, // Optional - used for RLS policies with user roles
    userRoles: { // Optional - used for RLS policies with user roles
      owner: {},
      admin: {},
      member: {}
    }
  }
);

rls()

Function to define RLS policies for a table.

// policies.config.ts

const profiles = rls(tables.profiles, {
  insert: authenticated(),
  update: own(),
  select: everyone(),
});

export { profiles }

own()

RLS policy to allow access where the row's user_id matches the user's uid. Tables must have a user_id column to use this policy.

// policies.config.ts

const todos = rls(tables.todos, {
  all: own() // User has access to all methods where their uid matches the row's user_id
});

export { profiles }

authenticated()

RLS policy to allow access to authenticated users.

// policies.config.ts

const profiles = rls(tables.profiles, {
  insert: authenticated(),
});

export { profiles }

hasRole(userRole: string | string[])

RLS policy to allow access if a user has a role. User roles are defined in your rlsConfig passed to rlsPolicyBuilder.

// policies.config.ts

const profiles = rls(tables.profiles, {
  insert: authenticated(),
  select: everyone(),
  update: own(),
  delete: hasRole(['admin', 'owner']) // Or simply `hasRole('admin')`
});

export { profiles }

belongsTenant()

RLS policy to allow access if a user belongs to a tenant. Only available when tenantTable is provided to rlsConfig, signifying a multi-tenant configuration.

// policies.config.ts

const profiles = rls(tables.profiles, {
  insert: authenticated(),
  select: belongsTenant(), // All users belonging to the same tenant can select
  update: own(),
});

export { profiles }

everyone()

RLS policy to allow access to all users. Proceed with caution.

// policies.config.ts

const profiles = rls(tables.profiles, {
  select: everyone(),
});

export { profiles }

Examples 📝


Limitations 🚫

  • Currently only a single file Drizzle schema is supported.
  • I have only tested this lib on MacOS. I expect it will work on *nix systems but not Windows. If there is enough interest I might look into it.
  • Only Typescript Drizzle Kit config files are supported. Plain JavaScript will not work. Again, if there's enough interest I might look into it.

Contributing 🤝

Contributions are welcome. I am a single developer who built this to solve a problem I had, so if there's a feature you specifically want feel free to open a pull request.


Disclaimers❗

  • supabase-drizzle is not an official library affiliated with the Supabase or Drizzle teams.
  • I make no guarantees that this lib is bug free.
  • I strongly suggest that you manually review generated SQL for RLS migrations before running them.
  • Don't use this lib anywhere near production databases until it's more stable and battle tested.

License 📝

This project is licensed under the MIT License.


Authors