-
-
Notifications
You must be signed in to change notification settings - Fork 158
Database typings are not correct on table join for entity to one relationship #408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
👍 And also looking for an elegant way to deal with all the |
Hey @selbyk, I managed to fix it for now by chaining with the type Tables = IDatabase['public']['Tables'];
type TeamsTable = Tables['figma_teams']['Row'];
type FigmaUsersTable = Tables['figma_users']['Row'];
export type Team = {
team: {
id: TeamsTable['id'];
name: TeamsTable['name'];
projects: [
{
files: [
{
comments: [
{
member: { img_url: FigmaUsersTable['img_url'] };
},
];
},
];
},
];
};
};
const { data, error } = await (client ?? supabase)
.from('user_has_figma_team')
.select(
`team:figma_teams(
id,
name,
projects:figma_projects(
files:figma_files(
comments:figma_comments(
member:figma_users(img_url)
)
)
)
)`,
)
.eq('user_id', user?.id)
.returns<Team[]>()
.order('created_at', { foreignTable: 'figma_teams', ascending: false }); |
I am seeing the return types be correct as the #223 states, but the typescript types are still totally ambiguous between array or single response which makes the return types somehow even less useful? Is there a way to get the types to match the return type? |
Ah I see #303 is really where the discussion on this is happening. |
That issue was recently closed in favor of this one.
Any updates on this? Apart from the original problem described in this issue, selecting from a foreign table returns the type Consider the scenario where we have two tables called const { data } = await supabase.from("students").select("*, books (*)");
Can I safely assume that these points hold true? If so, I will use a workaround and override these types, until a fix for this problem is implemented. |
Any updated with this issue? |
I found temporary fix in this discussion comment: https://github.com/orgs/supabase/discussions/7610#discussioncomment-7517390 |
This workaround doesnt change anything for me. Weird. Still a super annoying bug which makes using types with supabase nearly impossible. This bug needs more love from the supabase team. because you know.... 1:1 relations are not that uncommon to say the least. In case the supabase team is looking into this, i ve mentioned the exact same problem in Discord. I replay here: somehow the supabase types generator, generates 1:n types where my DB design has a 1:1 mapping. Following table structure
This is basically the example as shown in the supabase docs.
Now the weird part. The type PersonWithAddress looks like this:
Look at the array just behind the "Address" type (not the outer array, which is the result set array). Address cant be an array because its a 1:1 relation. This means, when mapping through the array like this:
is wrong according to typescript linter because he expects: person.Address[0].id. During runtime of course, i can only do person.Address.id to get data. When looking at my generated typescript file:
Why is "OneToOne" false ?? The address_id (FK field) is a number column. This definitely should be true right? Even if i set it to true (manually, which is.a non starter anyhow), it doesnt change the outcome of the linter. |
What version of supabase-js are you using? There was a bug previously, but it was fixed in postgrest-js v1.9.0. Then, postgrest-js v1.9.0 was incorporated into the release of supabase-js v2.39.2. https://github.com/supabase/postgrest-js/releases/tag/v1.9.0 And, how are you generating the schema through supabase cli? There was such an exchange in this issue. I'm doing fine with the following cli command: plz also check supabse cli version. |
oh you are right! I was on SDK 2.39.0 ... so slightly before the fix. Damn ;-) I should have upgraded before asking. Thanks for pointing out. I tested my code again and now all works as expected. |
I haven't checked what it's like now, but to correctly generate types, please try using the --local option. |
Same here, type error when two level of joins! |
Having the same issue here. |
I don’t know why people are not talking about this. This was single handedly the reason why using Supabase was not feasible for our team.. |
What query and data model are you using? Did you check out my post #536 ? J |
@jdgamble555 I checked that issue and tried the recommended method with no luck. Using
I have relation like this in generated schema.ts, for account table.
EDIT: Might be an issue with my ide or compiler, i ll start from scratch and try again first. |
@ardabeyazoglu - Did you try? city:cities!inner(name) J |
@jdgamble555 finally it worked with using !inner, after regenerating types and creating client with createClient. I think the issue can be closed. |
I tried everything in this thread, as well as in other threads, but I still have the issue. Essentially, for 1-to-1 relationships, the typing expects a T[] for the nested data, while the data effectively returned is a single T. I updated both the CLI and supabase-js to latest versions, re-generated the types, but no luck. I must admit this is quite annoying, and i's not clear to me if the issue is fixed, or if there is workaround, and in which case what the workaround is ... |
@fvermaut - What is your data model and query? |
Well essentially it happens between those 2 tables:
Note that the "id" of the second table is both a primary key, and a foreign key to the first one:
My debug query is this one:
What is the problem: the "name" field is appearing red in VScode, even if it is running fine - because typing expects an array there. The only way to remove the typescript error is ito replace it with 'projects[0].project[0].name', which though doesn't work at runtime. On a side note, the logic behind my model (but should not be relevant to current issue): I want to give WRITE access to authenticated users( through RLS) only to the "users_projects" table - and I have a custom backend process using the service key, that manages the update of the "projects" table based on the requested status ("req_status") set on the "user_projects" row. |
@fvermaut - I'm not having that problem. I simplified your schema to: CREATE TABLE IF NOT EXISTS "public"."user_projects"(
"id" "uuid" PRIMARY KEY DEFAULT "gen_random_uuid"() NOT NULL,
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
"updated_at" timestamp with time zone DEFAULT "now"(),
"name" "text",
"req_hyperparams_map" "json",
"req_start_date" "date",
"req_settings" "json",
"req_model_id" "uuid",
"user_id" "uuid" DEFAULT "auth"."uid"(),
"req_source_id" "uuid"
);
CREATE TABLE IF NOT EXISTS "public"."projects"(
"id" "uuid" NOT NULL PRIMARY KEY REFERENCES public.user_projects(id),
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
"updated_at" timestamp with time zone DEFAULT "now"(),
"name" "text",
"hyperparams_map" "json",
"evaluation_criterias" "json",
"start_date" "date",
"last_executed_at" timestamp with time zone,
"error_message" "text",
"settings" "json",
"source_id" "uuid"
); Here is the types you get for: await supabase.from('user_projects').select("id, project:projects!inner(*)") PostgrestSingleResponse<{
id: string;
project: {
created_at: string;
error_message: string | null;
evaluation_criterias: Json;
hyperparams_map: Json;
id: string;
last_executed_at: string | null;
... 4 more ...;
updated_at: string | null;
} | null;
}[]> The reason it is possibly https://github.com/jdgamble555/supabase-types/blob/master/src/lib/index.ts J |
To me, this is definitely a workaround. There's no reason why you'd have to change your query to get correct types. |
@bnjmnt4n - Technically, the only types that do not work correctly are:
from #536. Using inner for that use case is definitely a work-around. However, @fvermaut is not getting the work-around to work. My point is that we need to stick to the issue at hand as these posts get overwhelmed with 50 different issues and comments, which sometimes are inundated with incorrect information. If we don't stick to the exact problem, it will never get fixed. @fvermaut's code is all over the place, for example, so I suspect something else is going on with his code. If there is a bug with the work-around, we need to know. J |
Sorry, again as I said I won't spend more time to debug this issue, as unfortunately I simply don't have time for this. If the only solution is a workaround, then I found a simpler workaround by just skipping the join. |
@fvermaut - Just trying to solve the problem. 😄 You pasted the generated code from postgres instead of your schema directly, you left out some things, and I still don't know the PK situation. More than one thing is missing and not clear. FWI, this is the advantage of developing locally first. The reason you want to debug this is so you (and the benefit of those on this post) can have a better understanding of how to do it correctly. Maybe this time you can avoid the join, but you inevitably will have to do an inner join like that in the future, so you should understand how to do it (even with the work-around) to appease the Type system. 100% we need the Types to work when querying a FK directly, but that is a very specific issue. It does not mean the whole system is broken. People will find this post (and others) and assume that is the case, and nothing gets solved. If you're not sticking with Supabase, the time involved would not be worth it. If you are, I think we can debug it. J |
I agree that to get down to the issue you have to investigate every cases, I'm just saying that I don't have time for this, especially if the point is to validate a workaround. I use (and pay for) Supabase as a BaaS, on the premise that you can "build in a weekend" (I know it's a marketing stunt, but still the idea is there). So by principle I don't want to spend weekends debugging things like this. I'll keep watching the thread if something clicks at some point. Thanks again for your help. |
I came to this thread after googling my supabase error.
E.g. my Is there a way to further disambiguate the inner join? I would expect either |
Thought so, but shouldn't this work (correctly) then?
|
Yes, it should, but Supabase currently doesn't support typing from a foreign key field. I guess they figured there is a work around so they haven't prioritized it, but now we know one at least one situation without a work-around. J |
Hey everyone ! We've reworked the result inference in To test it out, update your npm install @supabase/supabase-js@2.46.0-rc.1 Please let us know if you encounter any issues, especially with retro-compatibility, so we can finalize it for the next release. |
This seems to fix my test data! However, I did not test any regular features for breaking changes. For anyone testing this:
Make sure to add Thanks for working on this! J |
That’s great to hear, thanks for testing! |
This one didn't work for me. I'm now getting the type: I'm not sure if there is an additional option I'm supposed to add or something. Update: I am also seeing this on rc.1 |
@B-E-A-F does it help if you regenerate your types? |
It doesn't seem to fix itself after restarting my typescript server. Is that what you mean by regenerating my types? |
@B-E-A-F not exactly - if you’re using the CLI you need to re-run this command. If you’re developing locally, it would help to know your CLI version:
|
Does generating against a local Supabase instance same as against cloud instance? I haven't seen any change in the generated schema recently and my Supabase CLI is up to date |
Sorry to be difficult. I don't really want to do this. My type inference "works" until I update to the release candidate though. If I had to run the supabase CLI to regenerate the types wouldn't that be a breaking change? |
They may not always be the same - for local instances the type generator’s version is tied to the CLI version, so it can get out of date if you’re using a very old CLI version.
Not at all! I think you’re right here - based on the error message I think I know where the breaking change comes from. We’ll release a new RC for you to try out. |
@B-E-A-F just to check, do tables in your generated types have a export type Database = {
public: {
Tables: {
mytable: {
Row: { /* ... */ }
Insert: { /* ... */ }
Update: { /* ... */ }
Relationships: [ /* ... */ ]
}
}
}
} |
There is a Relationships field. I just generated and looked at the types from the dashboard. |
Must be something else then. Can I trouble you to file a support ticket (ticking “Allow support access”)? It’d be difficult to troubleshoot the issue otherwise. |
The fix has been released in @B-E-A-F without feedbacks from your side, we couldn't reproduce your issue. Meanwhile we passed a fix for |
I'm currently collecting more information but v2.46.0 seems to have obliterated my previously-working project. I use a lot of views and it previously worked by simply inferring the columns defined in the select as type |
I'm really sorry for the inconvenience. To help us pinpoint what's going on, could you share your database types and any example queries to views that were working in the previous version? If possible, submitting this via a Supabase support ticket (making sure to check “Allow support access”) would be ideal, as it helps us troubleshoot directly. We’ll prioritize getting this fixed ASAP. Thanks! |
How can we insure isOneToOne gets set to true? And what exactly does isOneToOne mean? Does that mean it is a unique key? Like user_id can only exist once in articles if its isOneToOne? { articles: {
Row: {
content: Json
created_at: string | null
id: string
locale: string
published: boolean | null
published_at: string | null
slug: Json
summary: Json | null
tags: string[] | null
thumbnail_url: string | null
title: Json
updated_at: string | null
user_id: string | null
}
Insert: {
content: Json
created_at?: string | null
id?: string
locale?: string
published?: boolean | null
published_at?: string | null
slug: Json
summary?: Json | null
tags?: string[] | null
thumbnail_url?: string | null
title: Json
updated_at?: string | null
user_id?: string | null
}
Update: {
content?: Json
created_at?: string | null
id?: string
locale?: string
published?: boolean | null
published_at?: string | null
slug?: Json
summary?: Json | null
tags?: string[] | null
thumbnail_url?: string | null
title?: Json
updated_at?: string | null
user_id?: string | null
}
Relationships: [
{
foreignKeyName: "articles_user_id_fkey"
columns: ["user_id"]
isOneToOne: false
referencedRelation: "profiles"
referencedColumns: ["id"]
},
]
}
} create table
public.articles (
id uuid not null default gen_random_uuid (),
user_id uuid null,
title jsonb not null,
slug jsonb not null,
content jsonb not null,
locale text not null default 'en'::text,
created_at timestamp with time zone null default now(),
updated_at timestamp with time zone null default now(),
summary jsonb null,
thumbnail_url text null,
published_at timestamp with time zone null,
published boolean null default false,
tags text[] null default '{}'::text[],
constraint articles_pkey primary key (id),
constraint articles_slug_key unique (slug),
constraint articles_user_id_fkey foreign key (user_id) references profiles (id) on delete set null
) tablespace pg_default;
create trigger handle_updated_at before
update on articles for each row
execute function extensions.moddatetime ('updated_at'); |
Also I have a Whispers table that has user_id AND recipient_id I need to get profiles!user_id() and profiles!recipient_id() but they are both returning arrays or {}[] I tried profiles!inner() for other tables and that fixed the array issue. But I cannot do profiles!recipient_id() or profiles!recipient_id!inner(*) :/ EDIT - Noticed it works if I use the foreign key names like this:
Is it not supposed to work with column names? Will it ever? Also it returns as potentially null even though the column is not nullable? create table
public.whispers (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
recipient_id uuid not null,
content jsonb not null default '{}'::jsonb,
locale text not null default 'en'::text,
constraint whispers_pkey primary key (id),
constraint whispers_recipient_id_fkey foreign key (recipient_id) references profiles (id) on delete cascade,
constraint whispers_user_id_fkey foreign key (user_id) references profiles (id) on delete cascade
) EDIT AGAIN - Had to add !inner to make it not nullable. I guess that makes sense? "user:profiles!whispers_user_id_fkey!inner()", phew |
Bug report
Describe the bug
Typings are not correct when joining tables that are always NOT an array. For example, in my application we use the following query to fetch data about
figma_comments
and link tofigma_users
andfigma_files
:From this query, I would expect the built in typings to have both
user
andfile
to be an entity of a singular user and a singular file. Since the constraints I set up in Postgres won’t allow it to be multiple entries because the foreign key is unique on the joinable tables.Here is an overview of the types being generated by supabase CLI:
Expected behavior
I would expect the built in typings to know that user and file in the case of my query is an object if the entity, not an array of objects. This is because
figma_files.file_key
andfigma_users.figma_id
are set to UNIQUE.I understand that making this work might be extremely difficult, but does anyone have any suggestions on how to make the typings work for me so I do not have to cast these types?
System information
The text was updated successfully, but these errors were encountered: