With @supabase/supabase-js@2.75.1 and a CLI version > 2.53.1, the type-generation and runtime support for embedded functions / computed relationships has been improved. The introspection now includes SetofOptions metadata via the supabase/postgres-meta#971, and the client library types in supabase-js now provide stronger type inference and compile-time errors for invalid function usage.
π Whatβs new
-
Functions returning
SETOFor table types are now correctly inferred as embedded relationships (arrays) when used in.select()queries (computed relationships). Supported viaSetofOptionsfrom introspection. note: If you define a function in Postgres withRETURNS SETOF β¦ ROWS 1, the tool-chain (introspection + type generation) will infer this as a single object instead of an array. -
TypeScript errors at call-site when you attempt to call a function with wrong or conflicting parameters. For example:
_10const res = await supabase.rpc('postgrest_unresolvable_function', { a: 'test' });_10// Error: Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => integer), public.postgrest_unresolvable_function(a => text).This kind of error helps you catch ambiguous overloads that the introspection cannot decide between.
-
Additional error cases like:
_10const res = await supabase.rpc('blurb_message', {});_10// Error: Searched for the function public.blurb_message with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache.This corresponds to underlying PostgREST error codes
PGRST202/PGRST203.
Important caveats:
Nullable embed relationships by default: When you embed a function (computed relationship) in a .select() query, the generated type will treat the function result as possibly null, since it may not return any rows.
You can override the nullability globally using a DeepMerge override when generating types using the CLI. Example:
_17import { MergeDeep } from 'type-fest'_17import { Database as DatabaseGenerated } from './database-generated.types'_17_17export type Database = MergeDeep<_17 DatabaseGenerated,_17 {_17 public: {_17 Functions: {_17 my_function: {_17 SetofOptions: {_17 isNotNullable: true_17 }_17 }_17 }_17 }_17 }_17>
This approach mimics defining custom JSON types via MergeDeep.
However, when a function has multiple overloads or multiple SetofOptions variants (for example one overload returns from someTable β someOtherTable, another returns someTable2 β someOtherTable), a global override may become ambiguous:
_10function_name:_10 | { ..., SetofOptions: { from: 'someTable', to: 'someOtherTable' } }_10 | { ..., SetofOptions: { from: 'someTable2', to: 'someOtherTable' } }
In such cases, the recommendation is to use the !inner hint in the query itself so the inference knows the result wonβt be null:
_10client_10 .from('table')_10 .select('field1, computed_relationship!inner(id)')_10// Inferred type:_10// { field1: string, computed_relationship: { id: string } }_10// rather than default:_10// { field1: string, computed_relationship: { id: string } | null }
β What you should do
- Upgrade to
@supabase/supabase-js@2.75.1(or higher) - Ensure you are using the CLI at version > 2.53.1 so that
SetofOptionsmetadata is correctly included in the generated types. - Regenerate your types (e.g.,
npx supabase gen types typescript β¦) after introspection so that functions withSETOFreturn types are captured with the proper metadata.