Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/supabase

src/update-rows.ts

115 lines · 3.2 KB · TypeScript
import { projectAuth } from './setup.ts'
import {
	assertFilteredWrite,
	assertMutationAllowed,
	readMutationFlags,
} from './safety.ts'
import { asLive, projectRequest } from './client.ts'
import {
	filterQuery,
	optionalSchema,
	profileHeaders,
	readFilters,
	readPatch,
	requireTable,
	restPath,
} from './table.ts'
import {
	optionalBoolean,
	optionalString,
	type DryRunResult,
	type JsonRecord,
	type SupabaseAuthInput,
} from './types.ts'

/**
 * Update rows in a PostgREST table. Requires `confirm: true`, or use `dryRun: true`.
 * Unfiltered updates also need `allowUnfiltered: true`.
 * @example
 * import updateRows from 'kody:@kody/supabase/update-rows'
 * const preview = await updateRows({
 *   projectRef: 'abcdefghijklmnop',
 *   table: 'items',
 *   filters: { id: 'eq.1' },
 *   values: { name: 'Renamed' },
 *   dryRun: true,
 * })
 */
export async function updateRows(
	input: SupabaseAuthInput & {
		table: string
		schema?: string
		filters?: Record<string, string>
		values: JsonRecord
		allowUnfiltered?: boolean
		dryRun?: boolean
		confirm?: boolean
	},
) {
	const table = requireTable(input.table)
	const flags = readMutationFlags(input)
	assertFilteredWrite(
		{
			hasFilters: Boolean(input.filters && Object.keys(input.filters).length > 0),
			allowUnfiltered: input.allowUnfiltered,
			dryRun: flags.dryRun,
			confirm: flags.confirm,
		},
		'update-rows',
	)
	const allowed = assertMutationAllowed(flags, 'update-rows')
	const path = restPath(table)
	if (allowed.dryRun) {
		const preview: DryRunResult = {
			dryRun: true,
			method: 'PATCH',
			path,
			query: input.filters,
			body: input.values,
		}
		return preview
	}
	const live = asLive<{ data: unknown }>(
		await projectRequest({
			auth: projectAuth(input),
			method: 'PATCH',
			path,
			headers: {
				...profileHeaders(optionalSchema(input.schema)),
				Prefer: 'return=representation',
			},
			query: filterQuery(input.filters),
			body: input.values,
		}),
	)
	const items = Array.isArray(live.data) ? (live.data as Array<JsonRecord>) : []
	return { items, count: items.length }
}

/**
 * Update rows in a PostgREST table. Requires `confirm: true`, or use `dryRun: true`.
 * @example
 * import updateRows from 'kody:@kody/supabase/update-rows'
 * const preview = await updateRows({ projectRef: 'abcdefghijklmnop', table: 'items', filters: { id: 'eq.1' }, values: { name: 'Renamed' }, dryRun: true })
 */
export default async function updateRowsEntrypoint(
	params: Partial<SupabaseAuthInput> & Record<string, unknown> = {},
) {
	return await updateRows({
		account: optionalString(params.account, 'account'),
		secretName: optionalString(params.secretName, 'secretName'),
		serviceRoleSecretName: optionalString(
			params.serviceRoleSecretName,
			'serviceRoleSecretName',
		),
		projectRef: optionalString(params.projectRef, 'projectRef'),
		projectUrl: optionalString(params.projectUrl, 'projectUrl'),
		table: requireTable(params.table),
		schema: optionalSchema(params.schema),
		filters: readFilters(params.filters),
		values: readPatch(params.values ?? params.patch),
		allowUnfiltered: optionalBoolean(params.allowUnfiltered, 'allowUnfiltered'),
		dryRun: optionalBoolean(params.dryRun, 'dryRun'),
		confirm: optionalBoolean(params.confirm, 'confirm'),
	})
}