Reputation: 2715
I am trying to figure out how to query supabase. I have an events table with a host_id attribute. it is a foreign key to the user.id in the users table.
I am trying (and failing miserably) at figuring out how to follow the instructions in [this documentation][1] to get to a point where I can query the first_name and last_name on the user table and display that on my events page.
My events.ts has:
import { createSupabaseServerClient } from '../supabaseServerClient';
import { Database } from '../types/supabase';
export type Event = Database['public']['Tables']['events']['Row'];
export type Status = { label: string };
type HostInfo = {
first_name: string;
last_name: string;
}
export type EventWithStatus = Omit<Event, 'status'> & {
status: Status | null;
status_id: string | null;
created_at: string ;
host?: HostInfo;
host_name?: string;
users?: { first_name: string; last_name: string } | null;
};
export default async function getEvents(): Promise<EventWithStatus[]> {
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase
.from('events')
.select(`
id,
title,
comment,
created_at,
creator_id,
host_id,
date,
linkToVirtual,
location,
maxGroupSize,
time,
virtual,
status_id,
status:status_id(label),
users:host_id(first_name, last_name)
`);
//
if (error) {
console.error('Error fetching events:', error.message);
throw error;
}
return (data || []).map((event) => ({
id: event.id,
title: event.title,
comment: event.comment,
created_at: event.created_at,
creator_id: event.creator_id,
host_id: event.host_id,
date: event.date,
linkToVirtual: event.linkToVirtual,
location: event.location,
maxGroupSize: event.maxGroupSize,
time: event.time,
virtual: event.virtual,
status_id: event.status_id,
status: event.status ? { label: event.status.label } : null,
host_name: event.users
? `${event.users.first_name} ${event.users.last_name}`
: 'Unknown Host',
})) as EventWithStatus[];
}
// Client-side hooks for interacting with events
export function useEvents() {
// Fetch all events with statuses
const getEvents = async (): Promise<EventWithStatus[]> => {
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase
.from('events')
.select(`
id,
title,
comment,
created_at,
creator_id,
host_id,
date,
linkToVirtual,
location,
maxGroupSize,
time,
virtual,
status_id,
status:status_id(label),
users:host_id(first_name, last_name)
`);
if (error) {
console.error('Error fetching events:', error.message);
throw error;
}
return (data || []).map(event => ({
id: event.id,
title: event.title,
comment: event.comment,
created_at: event.created_at,
creator_id: event.creator_id,
host_id: event.host_id,
date: event.date,
linkToVirtual: event.linkToVirtual,
location: event.location,
maxGroupSize: event.maxGroupSize,
time: event.time,
virtual: event.virtual,
status_id: event.status_id,
status: event.status ? { label: event.status.label } : null,
host_name: event.users
? `${event.users.first_name} ${event.users.last_name}`
: 'Unknown Host',
})) as EventWithStatus[];
};
const getRecentEvents = async (): Promise<EventWithStatus[]> => {
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase
.from('events')
.select(`
id,
title,
comment,
created_at,
creator_id,
host_id,
date,
linkToVirtual,
location,
maxGroupSize,
time,
virtual,
status_id,
status:status_id(label)
`)
.order('created_at', { ascending: false })
.limit(5);
if (error) {
console.error('Error fetching events:', error.message);
throw error;
}
// users(first_name, last_name)
return (data || []).map(event => ({
id: event.id,
title: event.title,
comment: event.comment,
created_at: event.created_at,
creator_id: event.creator_id,
host_id: event.host_id,
date: event.date,
linkToVirtual: event.linkToVirtual,
location: event.location,
maxGroupSize: event.maxGroupSize,
time: event.time,
virtual: event.virtual,
status_id: event.status_id,
status: event.status ? { label: event.status.label } : null,
})) as EventWithStatus[];
};
// Fetch a single event by ID
const getEventById = async (id: string): Promise<EventWithStatus | null> => {
console.log('Fetching event with ID console log:', id); // Log the incoming id
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase
.from('events')
.select(`
id,
title,
comment,
created_at,
creator_id,
host_id,
date,
linkToVirtual,
location,
maxGroupSize,
time,
virtual,
status_id,
status:status_id(label),
users(first_name, last_name)
`)
.eq('id', id)
.single();
if (error) {
console.error('Error fetching event:', error.message);
return null;
}
console.log('Fetched eventid data:', data);
return data ? {
id: data.id,
title: data.title,
comment: data.comment,
created_at: data.created_at,
creator_id: data.creator_id,
host_id: data.host_id,
date: data.date,
linkToVirtual: data.linkToVirtual,
location: data.location,
maxGroupSize: data.maxGroupSize,
time: data.time,
virtual: data.virtual,
status_id: data.status_id,
status: data.status ? { label: data.status.label } : null,
host_name: data.users
? `${data.users.first_name} ${data.users.last_name}`
: 'Unknown Host'
} : null;
};
// Create a new event
const createEvent = async (eventData: Omit<Event, 'id'>): Promise<Event> => {
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase
.from('events')
.insert(eventData)
.select()
.single();
if (error) {
console.error('Error creating event:', error.message);
throw error;
}
return data as Event;
};
// Update an existing event
const updateEvent = async (
eventId: string,
eventData: Partial<Event>
): Promise<Event[]> => {
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase.from('events').update(eventData).eq('id', eventId);
if (error) {
console.error('Error updating event:', error.message);
throw error;
}
return data ? (data as Event[]) : [];
};
// Delete an event by ID
const deleteEvent = async (eventId: string): Promise<Event[]> => {
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase.from('events').delete().eq('id', eventId);
if (error) {
console.error('Error deleting event:', error.message);
throw error;
}
return data ? (data as Event[]) : [];
};
return { getEvents, getEventById, getRecentEvents, createEvent, updateEvent, deleteEvent };
}
My serverEvents.ts has
import { createSupabaseServerClient } from '../supabaseServerClient';
import { EventWithStatus } from './events';
export async function getServerSideEvents(): Promise<EventWithStatus[]> {
try {
console.log('๐ Attempting to fetch events in serverEvents');
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase
.from('events')
.select(`
id,
title,
comment,
created_at,
creator_id,
host_id,
date,
linkToVirtual,
location,
maxGroupSize,
time,
virtual,
status_id,
status:status_id(label),
users:host_id(first_name, last_name)
`);
if (error) {
console.error('โ Error fetching events:', error.message);
return [];
}
if (!data || data.length === 0) {
console.warn('โ ๏ธ No events found');
return [];
}
const processedEvents = data.map((event) => ({
id: event.id ?? '',
title: event.title ?? 'Untitled Event',
comment: event.comment ?? null,
created_at: event.created_at ?? new Date().toISOString(),
creator_id: event.creator_id ?? null,
host_id: event.host_id,
date: event.date ?? null,
linkToVirtual: event.linkToVirtual ?? null,
location: event.location ?? null,
maxGroupSize: event.maxGroupSize ?? null,
time: event.time ?? null,
virtual: event.virtual ?? null,
status_id: event.status_id ?? null,
status: event.status
? { label: event.status.label ?? 'Unknown Status' }
: null,
host_name: event.users
? `${event.users.first_name} ${event.users.last_name}`
: 'Unknown Host'
}));
return processedEvents;
} catch (error) {
console.error('โ Detailed error in getServerSideEvents:', error);
return [];
}
}
export async function getServerSideEventById(id: string): Promise<EventWithStatus | null> {
try {
console.log('๐ Attempting to fetch event by ID in serverEvents:', id);
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase
.from('events')
.select(`
id,
title,
comment,
created_at,
creator_id,
host_id,
date,
linkToVirtual,
location,
maxGroupSize,
time,
virtual,
status_id,
status:status_id(label),
users(first_name, last_name)
`)
.eq('id', id)
.single();
if (error) {
console.error('โ Error fetching event by ID:', error.message);
return null;
}
if (!data) {
console.warn('โ ๏ธ No event found with ID:', id);
return null;
}
const processedEvent: EventWithStatus = {
id: data.id ?? '',
title: data.title ?? 'Untitled Event',
comment: data.comment ?? null,
created_at: data.created_at ?? new Date().toISOString(),
creator_id: data.creator_id ?? null,
host_id: data.host_id,
date: data.date ?? null,
linkToVirtual: data.linkToVirtual ?? null,
location: data.location ?? null,
maxGroupSize: data.maxGroupSize ?? null,
time: data.time ?? null,
virtual: data.virtual ?? null,
status_id: data.status_id ?? null,
status: data.status
? { label: data.status.label ?? 'Unknown Status' }
: null,
host_name: data.users
? `${data.users.first_name} ${data.users.last_name}`
: 'Unknown Host'
};
return processedEvent;
} catch (error) {
console.error('โ Detailed error in getServerSideEventById:', error);
return null;
}
}
I then try to use it on this page:
// events/[id]/page.tsx
import { DateTime } from 'luxon';
import { Badge } from '@/components/badge';
import { Heading, Subheading } from '@/components/heading';
import { Text, TextLink } from '@/components/text';
import { Link } from '@/components/link';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/table';
import { getServerSideEventById } from '@/lib/supabase/events/serverEvents';
import { EventWithStatus } from '@/lib/supabase/events/events';
import { notFound } from 'next/navigation';
import UserFullName from '@/components/user/fullName'; // Import UserFullName component
interface PageProps {
params: Promise<{
id: string;
}>;
}
export default async function EventDetailPage({ params }: PageProps): Promise<JSX.Element> {
// Await the params before using them
const { id } = await params;
const event: EventWithStatus | null = await getServerSideEventById(id);
if (!event) {
notFound();
}
console.log(event.comment)
const formatDate = (date: string | null) => {
if (!date) return 'Invalid Date'; // Return a fallback if the date is null or undefined
const options: Intl.DateTimeFormatOptions = { day: '2-digit', month: 'short', year: 'numeric' };
return new Date(date).toLocaleDateString('en-GB', options); // en-GB for dd MMM yyyy format
};
const formatTimeWithTimezone = (time: string | null) => {
if (!time) return 'Invalid Time'; // Return a fallback if the time is null or undefined
// Parse the time using Luxon, handling the timezone
const dateObj = DateTime.fromISO(time); // Parse the time string directly (including the timezone)
if (!dateObj.isValid) return 'Invalid Time'; // Handle invalid date
// Use TIME_WITH_SHORT_OFFSET to include the time, abbreviated timezone, and the offset
return dateObj.toLocaleString(DateTime.TIME_WITH_SHORT_OFFSET);
};
return (
<>
<div className="max-lg:hidden">
<Link
href="/events"
className="inline-flex items-center gap-2 text-sm/6 text-zinc-500 dark:text-zinc-400"
>
Back to Events
</Link>
</div>
<div className="mt-4 flex flex-wrap items-end justify-between gap-4">
<div className="flex flex-wrap items-center gap-6">
{/* <div className="w-32 shrink-0"></div> */}
<div>
<div className="flex flex-wrap items-center gap-x-4 gap-y-2">
<Heading><span className="capitalize">{event.title}</span></Heading>
<Text>hosted by: {event.host_id ? <UserFullName userId={event.host_id} /> : 'Unknown'}</Text>
<Badge
color={event.status?.label === 'Published' ? 'teal' : 'zinc'}
>
{event.status?.label}
</Badge>
</div>
<div className="mt-2 text-sm/6 text-zinc-500">
{formatDate(event.date)} at {formatTimeWithTimezone(event.time)}
<span aria-hidden="true" className="pl-2">ยท</span>
<span className="capitalize pl-2">{event.location}</span>
</div>
<div>
<Text className=" pt-4">{event.comment} </Text>
</div>
</div>
</div>
</div>
{/* Reference Materials */}
<Subheading className="mt-12">Interesting Materials</Subheading>
<Table className="mt-4 [--gutter:theme(spacing.6)] lg:[--gutter:theme(spacing.10)]">
<TableHead>
<TableRow>
<TableHeader>Title</TableHeader>
<TableHeader>Heads up</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{/* Reference Material 1
{event.referenceMaterials1 && (
<TableRow key={`${event.id}-ref1`}>
<TableCell>
<Link href={event.referenceMaterials1.source} className="flex">
<span>{event.referenceMaterials1.title}</span>
</Link>
</TableCell>
<TableCell className="whitespace-normal max-w-3/4">
{event.referenceMaterials1.headsUp}
</TableCell>
</TableRow>
)}
Reference Material 2
{event.referenceMaterials2 && (
<TableRow key={`${event.id}-ref2`}>
<TableCell>
<Link href={event.referenceMaterials2.source} className="flex">
<span>{event.referenceMaterials2.title}</span>
</Link>
</TableCell>
<TableCell className="whitespace-normal max-w-3/4">
{event.referenceMaterials2.headsUp}
</TableCell>
</TableRow>
)}
*/}
</TableBody>
</Table>
{/* Suggest Materials Link */}
<Link href="/" className="my-2 text-stone-400 hover:text-stone-300 float-right">
Suggest materials
</Link>
</>
);
}
The error message says:
Attempting to fetch events in serverEvents ๐ Attempting to fetch event by ID in serverEvents: 368dce9f-0fd9-463a-a8db-9fb77378d5e8 โ Error fetching event by ID: unrecognized configuration parameter "request.select.columns" โ Error fetching events: unrecognized configuration parameter "request.select.columns"
None of claude, chatgpt or perplexity are useful in solving this problem. Replit has refunded my subscription becuase it can't address the problem. I'm stuck for where to go for help with this.
Can anyone suggest my next step for solving this error? I dont even understand what it means and i cant get any of the LLMs to explain what im supposed to be looking for as an error.
Gemini suggests that supabase select queries can't be made on foreign key relationships (I cant find anything to support this assertion in teh supabase docs). It suggests:
his approach ensures compatibility and provides more control over the query: TypeScript
const { data: events, error } = await supabase
.from('events')
.select(`
id,
title,
comment,
created_at,
creator_id,
host_id,
date,
linkToVirtual,
location,
maxGroupSize,
time,
virtual,
status_id,
status:status_id(label)
`);
// ... (error handling and data processing)
const eventsWithHost = await Promise.all(
events.map(async (event) => {
const { data: user, error } = await supabase
.from('users')
.select('first_name, last_name')
.eq('id', event.host_id)
.single();
return {
...event,
host_name: user ? `${user.first_name} ${user.last_name}` : 'Unknown Host',
};
})
);
Does anyone have any clues for where i might find information supporting this proposed approach? I need more help that this snippet to try it and I dont understand it. [1]: https://supabase.com/docs/guides/database/joins-and-nesting
Upvotes: 0
Views: 25