ํ ์ด๋ธ์ ์ ์ฑ ์ ์ถ๊ฐํ๋ฉด RLS๊ฐ ์๋์ผ๋ก ํ์ฑํ๋ฉ๋๋ค. ๋ฐ๋ผ์ ํ ์ด๋ธ์ ์ ์ฑ ์ ์ถ๊ฐํ ๋ ๋ช ์์ ์ผ๋ก RLS๋ฅผ ํ์ฑํํ ํ์๊ฐ ์์ต๋๋ค.
Row-Level Security (RLS)
Drizzle๋ฅผ ์ฌ์ฉํ๋ฉด Postgres ํ ์ด๋ธ์ ๋ํด RLS(ํ ์์ค ๋ณด์)๋ฅผ ํ์ฑํํ๊ณ , ๋ค์ํ ์ต์ ์ผ๋ก ์ ์ฑ ์ ์์ฑํ๋ฉฐ, ํด๋น ์ ์ฑ ์ด ์ ์ฉ๋๋ ์ญํ ์ ์ ์ํ๊ณ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
Drizzle์ ์ํ๋ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๋ Postgres ์ ์ฑ
๋ฐ ์ญํ ์ ์์ ํํ์ ์ง์ํฉ๋๋ค. ์ด๋ Neon ๋ฐ Supabase์ ๊ฐ์ ์ธ๊ธฐ ์๋ Postgres ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ณต์
์ฒด์์ ์๋ํฉ๋๋ค.
Drizzle์๋ ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ณต์ ์ฒด ๋ชจ๋์ ๋ํด ๋ฏธ๋ฆฌ ์ ์๋ RLS ์ญํ ๋ฐ ํจ์๊ฐ ์์ง๋ง, ๊ณ ์ ํ ๋ก์ง์ ์ ์ํ ์๋ ์์ต๋๋ค.
RLS ํ์ฑํ
์ ์ฑ
์ ์ถ๊ฐํ์ง ์๊ณ ํ
์ด๋ธ์์ RLS๋ง ํ์ฑํํ๋ ค๋ฉด .enableRLS()๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
PostgreSQL ๋ฌธ์์ ์ธ๊ธ๋ ๋๋ก:
ํ ์ด๋ธ์ ์ ์ฑ ์ด ์๋ ๊ฒฝ์ฐ ๊ธฐ๋ณธ ๊ฑฐ๋ถ ์ ์ฑ ์ด ์ฌ์ฉ๋๋ฏ๋ก ํ์ด ๋ณด์ด์ง ์๊ฑฐ๋ ์์ ํ ์ ์์ต๋๋ค. TRUNCATE ๋ฐ REFERENCES์ ๊ฐ์ด ์ ์ฒด ํ ์ด๋ธ์ ์ ์ฉ๋๋ ์์ ์ ํ ๋ณด์์ ์ํฅ์ ๋ฐ์ง ์์ต๋๋ค.
import { integer, pgTable } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: integer(),
}).enableRLS();์ญํ
ํ์ฌ Drizzle์ ์๋์ ๊ฐ์ด ๋ช ๊ฐ์ง ๋ค๋ฅธ ์ต์ ์ผ๋ก ์ญํ ์ ์๋ฅผ ์ง์ํฉ๋๋ค. ํฅํ ๋ฆด๋ฆฌ์ค์์ ๋ ๋ง์ ์ต์ ์ ๋ํ ์ง์์ด ์ถ๊ฐ๋ ์์ ์ ๋๋ค.
import { pgRole } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin', { createRole: true, createDb: true, inherit: true });๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ญํ ์ด ์ด๋ฏธ ์กด์ฌํ๊ณ drizzle-kit๊ฐ ์ด๋ฅผ โํ์ธโํ๊ฑฐ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ํฌํจํ์ง ์๋๋ก ํ๋ ค๋ฉด ์ญํ ์ ๊ธฐ์กด ๊ฒ์ผ๋ก ํ์ํ ์ ์์ต๋๋ค.
import { pgRole } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin').existing();์ ์ฑ
RLS๋ฅผ ์์ ํ ํ์ฉํ๋ ค๋ฉด Drizzle ํ ์ด๋ธ ๋ด์์ ์ ์ฑ ์ ์ ์ํ ์ ์์ต๋๋ค.
PostgreSQL์์ ์ ์ฑ
์ ๊ธฐ์กด ํ
์ด๋ธ์ ์ฐ๊ฒฐ๋์ด์ผ ํฉ๋๋ค. ์ ์ฑ
์ ํญ์ ํน์ ํ
์ด๋ธ๊ณผ ์ฐ๊ฒฐ๋๋ฏ๋ก ์ ์ฑ
์ ์๋ pgTable์ ๋งค๊ฐ๋ณ์๋ก ์ ์๋์ด์ผ ํ๋ค๊ณ ๊ฒฐ์ ํ์ต๋๋ค.
์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ ์์ฑ์ด ํฌํจ๋ pgPolicy ์์
import { sql } from 'drizzle-orm';
import { integer, pgPolicy, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
pgPolicy('policy', {
as: 'permissive',
to: admin,
for: 'delete',
using: sql``,
withCheck: sql``,
}),
]);์ ์ฑ ์ต์
as | ๊ฐ๋ฅํ ๊ฐ์ permissive ๋๋ restrictive์
๋๋ค. |
to | ์ ์ฑ
์ด ์ ์ฉ๋๋ ์ญํ ์ ์ง์ ํฉ๋๋ค. ๊ฐ๋ฅํ ๊ฐ์๋ public, current_role, current_user, session_user ๋๋ ๋ฌธ์์ด๋ก ๋ ๋ค๋ฅธ ์ญํ ์ด๋ฆ์ด ํฌํจ๋ฉ๋๋ค. pgRole ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ ์๋ ์์ต๋๋ค. |
for | ์ด ์ ์ฑ
์ด ์ ์ฉ๋ ๋ช
๋ น์ ์ ์ํฉ๋๋ค. ๊ฐ๋ฅํ ๊ฐ์ all, select, insert, update, delete์
๋๋ค. |
using | ์ ์ฑ
์์ฑ ๋ฌธ์ USING ๋ถ๋ถ์ ์ ์ฉ๋ SQL ๋ฌธ์
๋๋ค. |
withCheck | ์ ์ฑ
์์ฑ ๋ฌธ์ WITH CHECK ๋ถ๋ถ์ ์ ์ฉ๋ SQL ๋ฌธ์
๋๋ค. |
๊ธฐ์กด ํ ์ด๋ธ์ ์ ์ฑ ์ฐ๊ฒฐ
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ธฐ์กด ํ
์ด๋ธ์ ์ ์ฑ
์ ์ฐ๊ฒฐํด์ผ ํ๋ ์ํฉ์ด ์์ต๋๋ค.
๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ์ฌ์ฉ ์ฌ๋ก๋ Neon ๋๋ Supabase์ ๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ณต์
์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ก, ๊ธฐ์กด ํ
์ด๋ธ์ ์ ์ฑ
์ ์ถ๊ฐํด์ผ ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ .link() API๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
import { sql } from "drizzle-orm";
import { pgPolicy } from "drizzle-orm/pg-core";
import { authenticatedRole, realtimeMessages } from "drizzle-orm/supabase";
export const policy = pgPolicy("authenticated role insert policy", {
for: "insert",
to: authenticatedRole,
using: sql``,
}).link(realtimeMessages);๋ง์ด๊ทธ๋ ์ด์
drizzle-kit๋ฅผ ์ฌ์ฉํ์ฌ ์คํค๋ง์ ์ญํ ์ ๊ด๋ฆฌํ๋ ๊ฒฝ์ฐ Drizzle ์คํค๋ง์ ์ ์๋์ง ์์ ์ญํ ์ ์ฐธ์กฐํ๋ ค๋ ์ํฉ์ด ์์ ์ ์์ต๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ drizzle ์คํค๋ง์ ๊ฐ ์ญํ ์ ์ ์ํ๊ณ .existing()์ผ๋ก ํ์ํ์ง ์๊ณ ๋ drizzle-kit๊ฐ ์ด๋ฌํ ์ญํ ๊ด๋ฆฌ๋ฅผ ๊ฑด๋๋ฐ๋๋ก ํ ์ ์์ต๋๋ค.
์ด๋ฌํ ๊ฒฝ์ฐ drizzle.config.ts์์ entities.roles๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ ์ฒด ์ฐธ์กฐ๋ drizzle.config.ts ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์.
๊ธฐ๋ณธ์ ์ผ๋ก drizzle-kit๋ ์ญํ ์ ๊ด๋ฆฌํ์ง ์์ผ๋ฏ๋ก drizzle.config.ts์์ ์ด ๊ธฐ๋ฅ์ ํ์ฑํํด์ผ ํฉ๋๋ค.
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'postgresql',
schema: "./drizzle/schema.ts",
dbCredentials: {
url: process.env.DATABASE_URL!
},
verbose: true,
strict: true,
entities: {
roles: true
}
});์ถ๊ฐ ๊ตฌ์ฑ ์ต์ ์ด ํ์ํ ๊ฒฝ์ฐ ๋ช ๊ฐ์ง ์์ ๋ฅผ ๋ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
admin ์ญํ ์ด ์๊ณ ๊ด๋ฆฌ ๊ฐ๋ฅํ ์ญํ ๋ชฉ๋ก์์ ์ ์ธํ๋ ค๋ ๊ฒฝ์ฐ
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
exclude: ['admin']
}
}
});admin ์ญํ ์ด ์๊ณ ๊ด๋ฆฌ ๊ฐ๋ฅํ ์ญํ ๋ชฉ๋ก์ ํฌํจํ๋ ค๋ ๊ฒฝ์ฐ
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
include: ['admin']
}
}
});Neon์ ์ฌ์ฉํ๊ณ Neon์์ ์ ์ํ ์ญํ ์ ์ ์ธํ๋ ค๋ ๊ฒฝ์ฐ provider ์ต์
์ ์ฌ์ฉํ ์ ์์ต๋๋ค
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
provider: 'neon'
}
}
});Supabase๋ฅผ ์ฌ์ฉํ๊ณ Supabase์์ ์ ์ํ ์ญํ ์ ์ ์ธํ๋ ค๋ ๊ฒฝ์ฐ provider ์ต์
์ ์ฌ์ฉํ ์ ์์ต๋๋ค
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
provider: 'supabase'
}
}
});๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ณต์
์ฒด์์ ์ง์ ํ ์ ์ญํ ๊ณผ ๋น๊ตํ์ฌ Drizzle์ด ์ฝ๊ฐ ์ค๋๋ ์ํฉ์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
์ด๋ฌํ ๊ฒฝ์ฐ provider ์ต์
์ ์ฌ์ฉํ๊ณ ์ถ๊ฐ ์ญํ ์ excludeํ ์ ์์ต๋๋ค:
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
provider: 'supabase',
exclude: ['new_supabase_role']
}
}
});๋ทฐ์ RLS
Drizzle๋ฅผ ์ฌ์ฉํ๋ฉด ๋ทฐ์ ๋ํ RLS ์ ์ฑ
๋ ์ง์ ํ ์ ์์ต๋๋ค. ์ด๋ฅผ ์ํด์๋ ๋ทฐ์ WITH ์ต์
์์ security_invoker๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ๋ค์์ ๊ฐ๋จํ ์์ ์
๋๋ค:
...
export const roomsUsersProfiles = pgView("rooms_users_profiles")
.with({
securityInvoker: true,
})
.as((qb) =>
qb
.select({
...getTableColumns(roomsUsers),
email: profiles.email,
})
.from(roomsUsers)
.innerJoin(profiles, eq(roomsUsers.userId, profiles.id))
);Neon๊ณผ ํจ๊ป ์ฌ์ฉํ๊ธฐ
Neon ํ์ ์์ ์ ์ฑ
API ์์ ๋ํผ์ ๋ํ ๋น์ ์ ๊ตฌํํ๋ ๋ฐ ๋์์ ์ฃผ์์ต๋๋ค. ๋ฏธ๋ฆฌ ์ ์๋ ํจ์์ Neon์ ๊ธฐ๋ณธ ์ญํ ์ ํฌํจํ๋ crudPolicy ํจ์๊ฐ ์๋ ํน์ /neon ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ ์ํ์ต๋๋ค.
crudPolicy ํจ์๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
import { crudPolicy } from 'drizzle-orm/neon';
import { integer, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
crudPolicy({ role: admin, read: true, modify: false }),
]);์ด ์ ์ฑ ์ ๋ค์๊ณผ ๋์ผํฉ๋๋ค:
import { sql } from 'drizzle-orm';
import { integer, pgPolicy, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
pgPolicy(`crud-${admin.name}-policy-insert`, {
for: 'insert',
to: admin,
withCheck: sql`false`,
}),
pgPolicy(`crud-${admin.name}-policy-update`, {
for: 'update',
to: admin,
using: sql`false`,
withCheck: sql`false`,
}),
pgPolicy(`crud-${admin.name}-policy-delete`, {
for: 'delete',
to: admin,
using: sql`false`,
}),
pgPolicy(`crud-${admin.name}-policy-select`, {
for: 'select',
to: admin,
using: sql`true`,
}),
]);Neon์ ๋ฏธ๋ฆฌ ์ ์๋ authenticated ๋ฐ anaonymous ์ญํ ๊ณผ ๊ด๋ จ ํจ์๋ฅผ ๋
ธ์ถํฉ๋๋ค. RLS์ Neon์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ๊ธฐ์กด์ผ๋ก ํ์๋ ์ด๋ฌํ ์ญํ ๊ณผ RLS ์ฟผ๋ฆฌ์ ๊ด๋ จ ํจ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
// drizzle-orm/neon
export const authenticatedRole = pgRole('authenticated').existing();
export const anonymousRole = pgRole('anonymous').existing();
export const authUid = (userIdColumn: AnyPgColumn) => sql`(select auth.user_id() = ${userIdColumn})`;
export const neonIdentitySchema = pgSchema('neon_identity');
export const usersSync = neonIdentitySchema.table('users_sync', {
rawJson: jsonb('raw_json').notNull(),
id: text().primaryKey().notNull(),
name: text(),
email: text(),
createdAt: timestamp('created_at', { withTimezone: true, mode: 'string' }),
deletedAt: timestamp('deleted_at', { withTimezone: true, mode: 'string' }),
});์๋ฅผ ๋ค์ด Neon ๋ฏธ๋ฆฌ ์ ์๋ ์ญํ ๋ฐ ํจ์๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ต๋๋ค:
import { sql } from 'drizzle-orm';
import { authenticatedRole } from 'drizzle-orm/neon';
import { integer, pgPolicy, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
pgPolicy(`policy-insert`, {
for: 'insert',
to: authenticatedRole,
withCheck: sql`false`,
}),
]);Supabase์ ํจ๊ป ์ฌ์ฉํ๊ธฐ
๋ํ ์คํค๋ง์์ ์ฌ์ฉํ ์ ์๋ ๊ธฐ์กด์ผ๋ก ํ์๋ ๋ฏธ๋ฆฌ ์ ์๋ ์ญํ ์ธํธ๊ฐ ์๋ /supabase ๊ฐ์ ธ์ค๊ธฐ๋ ์์ต๋๋ค.
์ด ๊ฐ์ ธ์ค๊ธฐ๋ ํฅํ ๋ฆด๋ฆฌ์ค์์ RLS ๋ฐ Supabase ์ฌ์ฉ์ ๋ ๊ฐ๋จํ๊ฒ ๋ง๋๋ ๋ ๋ง์ ํจ์์ ํฌํผ๋ก ํ์ฅ๋ ์์ ์
๋๋ค.
// drizzle-orm/supabase
export const anonRole = pgRole('anon').existing();
export const authenticatedRole = pgRole('authenticated').existing();
export const serviceRole = pgRole('service_role').existing();
export const postgresRole = pgRole('postgres_role').existing();
export const supabaseAuthAdminRole = pgRole('supabase_auth_admin').existing();์๋ฅผ ๋ค์ด Supabase ๋ฏธ๋ฆฌ ์ ์๋ ์ญํ ์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ต๋๋ค:
import { sql } from 'drizzle-orm';
import { serviceRole } from 'drizzle-orm/supabase';
import { integer, pgPolicy, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
pgPolicy(`policy-insert`, {
for: 'insert',
to: serviceRole,
withCheck: sql`false`,
}),
]);/supabase ๊ฐ์ ธ์ค๊ธฐ์๋ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฌ์ฉํ ์ ์๋ ๋ฏธ๋ฆฌ ์ ์๋ ํ
์ด๋ธ ๋ฐ ํจ์๋ ํฌํจ๋ฉ๋๋ค.
// drizzle-orm/supabase
const auth = pgSchema('auth');
export const authUsers = auth.table('users', {
id: uuid().primaryKey().notNull(),
});
const realtime = pgSchema('realtime');
export const realtimeMessages = realtime.table(
'messages',
{
id: bigserial({ mode: 'bigint' }).primaryKey(),
topic: text().notNull(),
extension: text({
enum: ['presence', 'broadcast', 'postgres_changes'],
}).notNull(),
},
);
export const authUid = sql`(select auth.uid())`;
export const realtimeTopic = sql`realtime.topic()`;์ด๋ฅผ ์ฝ๋์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ Drizzle Kit๋ ์ด๋ฅผ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ์ทจ๊ธํ์ฌ ๋ค๋ฅธ ์ํฐํฐ์ ์ฐ๊ฒฐํ๊ธฐ ์ํ ์ ๋ณด๋ก๋ง ์ฌ์ฉํฉ๋๋ค.
import { foreignKey, pgPolicy, pgTable, text, uuid } from "drizzle-orm/pg-core";
import { sql } from "drizzle-orm/sql";
import { authenticatedRole, authUsers } from "drizzle-orm/supabase";
export const profiles = pgTable(
"profiles",
{
id: uuid().primaryKey().notNull(),
email: text().notNull(),
},
(table) => [
foreignKey({
columns: [table.id],
// Supabase์ auth ํ
์ด๋ธ ์ฐธ์กฐ
foreignColumns: [authUsers.id],
name: "profiles_id_fk",
}).onDelete("cascade"),
pgPolicy("authenticated can view all profiles", {
for: "select",
// Supabase์ ๋ฏธ๋ฆฌ ์ ์๋ ์ญํ ์ฌ์ฉ
to: authenticatedRole,
using: sql`true`,
}),
]
);Supabase์ ์กด์ฌํ๋ ํ
์ด๋ธ์ ์ ์ฑ
์ ์ถ๊ฐํ๋ ์์ ๋ฅผ ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
import { sql } from "drizzle-orm";
import { pgPolicy } from "drizzle-orm/pg-core";
import { authenticatedRole, realtimeMessages } from "drizzle-orm/supabase";
export const policy = pgPolicy("authenticated role insert policy", {
for: "insert",
to: authenticatedRole,
using: sql``,
}).link(realtimeMessages);๋ํ Drizzle RLS๋ฅผ Supabase์ ํจ๊ป ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๊ณผ ์ค์ ์ฟผ๋ฆฌ๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ฃผ๋ ํ๋ฅญํ ์์ ๊ฐ ์์ต๋๋ค.
๋ํ Supabase์์ ๋ชจ๋ ํธ๋์ญ์
์์
์ ์ฒ๋ฆฌํ ์ ์๋ ํ๋ฅญํ ๋ํผ์ธ createDrizzle๋ ํฌํจ๋์ด ์์ต๋๋ค.
ํฅํ ๋ฆด๋ฆฌ์ค์์๋ drizzle-orm/supabase๋ก ์ด๋๋์ด ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค.
Drizzle SupaSecureSlack repo๋ฅผ ํ์ธํ์ธ์.
๋ค์์ ์ด ์ ์ฅ์์ ๊ตฌํ ์์ ์ ๋๋ค.
type SupabaseToken = {
iss?: string;
sub?: string;
aud?: string[] | string;
exp?: number;
nbf?: number;
iat?: number;
jti?: string;
role?: string;
};
export function createDrizzle(token: SupabaseToken, { admin, client }: { admin: PgDatabase<any>; client: PgDatabase<any> }) {
return {
admin,
rls: (async (transaction, ...rest) => {
return await client.transaction(async (tx) => {
// Supabase๋ auth.uid() ๋ฐ auth.jwt()๋ฅผ ๋
ธ์ถํฉ๋๋ค
// https://supabase.com/docs/guides/database/postgres/row-level-security#helper-functions
try {
await tx.execute(sql`
-- auth.jwt()
select set_config('request.jwt.claims', '${sql.raw(
JSON.stringify(token)
)}', TRUE);
-- auth.uid()
select set_config('request.jwt.claim.sub', '${sql.raw(
token.sub ?? ""
)}', TRUE);
-- set local role
set local role ${sql.raw(token.role ?? "anon")};
`);
return await transaction(tx);
} finally {
await tx.execute(sql`
-- reset
select set_config('request.jwt.claims', NULL, TRUE);
select set_config('request.jwt.claim.sub', NULL, TRUE);
reset role;
`);
}
}, ...rest);
}) as typeof client.transaction,
};
}๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ต๋๋ค.
// https://github.com/orgs/supabase/discussions/23224
// ์๋ช
๋ ์ก์ธ์ค ํ ํฐ์ ์ฌ์ฉํ๊ณ ์คํ ๋ฆฌ์ง์์ ์ง์ ์ฝ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฏ๋ก ์์ ํด์ผ ํฉ๋๋ค
export async function createDrizzleSupabaseClient() {
const {
data: { session },
} = await createClient().auth.getSession();
return createDrizzle(decode(session?.access_token ?? ""), { admin, client });
}
async function getRooms() {
const db = await createDrizzleSupabaseClient();
return db.rls((tx) => tx.select().from(rooms));
}