By Gabriel L. Manor
Gabriel L. Manor
Supabase waxaa loo isticmaali karaa in la isticmaali karaa adeegyada app-ka iyo in la isticmaali karaa email, OAuth, iyo link magic.
Supabase waxaa loo isticmaali kartaa backend-ka ugu fiican oo la isticmaali karaa Auth iyo Row Level Security (RLS), xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka xanuunka.
wax yar oo dhanxisaabinta kala duwan ee user iyo data
Saacadda aad u baahan tahay si ay u qiyaastii wax soo saarka ama si ay u qiyaastii wax soo saarka, si ay u qiyaastii wax soo saarka, ama si ay u qiyaastii wax soo saarka.
Dhulka this waxaa laga yaabaa sida loo helo Supabase authentication iyo authorization ee Next.js app.
Supabase authentication iyo kharashkaDiiwaanka.jsWe'll start with Supabase Auth for login and session management, then add authorization rules using Relationship-Based Access Control (ReBAC), enforced through Supabase Edge Functions and a local Policy Decision Point (PDP).
Supabase Qalabkawax yar oo dhanRelationship-Based Access Control (ReBAC)Supabase Edge Functions Local Policy Decision Point (PDP)
Waqtiga, sidoo kale waxaa laga yaabaa app-ka ah oo loo isticmaali karaa in la isticmaali karaa oo la isticmaali karaa oo la isticmaali karaa oo la isticmaali karaa oo la isticmaali karaa oo la isticmaali karaa.
What We’re Building
In dhismaha this, waxaan soo saarka app qiyaasta real-time oo loo isticmaalo Supabase iyo Next.js oo ku soo bandhigay sida xisaabinta iyo xisaabinta in xisaabinta.
Luuqadda Diiwaanka.js
The app wuxuu ku yaalaa in ay isticmaali karaa si ay u loo soo saarka macluumaadka, si ay u heli karaa dadka kale, iyo si ay u isticmaali karaa content ka badan. It demonstrates how to implement Supabase Auth for login/signup and how to enforce authorization policies that control who can vote, edit, or delete.
Supabase Qalabkawax yar oo dhan
We will use Supabase's core features—Auth, Postgres, RLS, Realtime, iyo Edge Functions—combinada iyo model Relationship-Based Access Control (ReBAC)
to enforce per-user and per-resource access rules.
Sidaalka Tech
- Supabase – Backend-as-a-service for database, authentication, realtime, and edge functions
- Next.js – Frontend framework for building the app UI and API routes
- Permit.io – (for ReBAC) to define and evaluate authorization logic via PDP
- Supabase CLI – To manage and deploy Edge Functions locally and in production
Qalabka ugu horeysay
- Node.js installed
- Supabase account
- Permit.io account
- Familiarity with React/Next.js
- Starter project repo
Waa'iid ka mid ah loo isticmaali karaa?
Demo app waa mid ka mid ah mid ah mid ka mid ah Next.js iyo Supabase, oo aad isticmaali kartaa mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah.
- Dhammaan user (waxariisada ama ma ahaato) waa in ay u aragto list of public polls
- Only authenticated users can create polls and vote
- A user can not vote on a poll they created
- Only the creator of a poll can edit or delete it
Tutorial Ujeedada
Waa soo xiriir oo dhan:
- Ganacsiga Supabase project, schema, auth, and RLS
- Build core app features sida loo soo saarka iyo dhismaha
- Rules model authorization define roles and rules in Permit.io
- Create Supabase Edge Functions for syncing users, allocating roles, and checking permissions
- Enforce policies in frontend app using those edge functions
Let’s get started -
Waqtiisa waxaa loo yaqaan 'Supabase in the Project'
Waqtiisa waxaa loo yaqaan "Supabase" ee ProjectQalabka: Clone template Starter
I've already created a starter template on GitHub with all the code you need to start so we can focus on implementing Supabase and Permit.io.
starter templateGitHub waxaa laga yaabaa
Saacadda waxaa loo isticmaali karaa in ay ku xiran tahay:
git clone <https://github.com/permitio/supabase-fine-grained-authorization>
Saacad ka hortiisa Project, navigate to the project directory and install the dependencies:
cd realtime-polling-app-nextjs-supabase-permitio npm install
cd realtime-polling-app-nextjs-supabase-permitio
npm install
Waaq u shaqeeyaan Project in Supabase
Waqtiga ugu horeysay:
-
Go to https://supabase.com and sign in or create an account.
-
Click "New Project" and fill in your project name, password, and region.
-
Once created, go to Project Settings → API and note your Project URL and Anon Key — you’ll need them later.
Go to https://supabase.com and sign in or create an account.
Go to https://supabase.com and sign in or create an account.
https://supabase.comClick "Project New" iyo ku xiran tahay xisaabinta, password, iyo region.
Click "Project New" iyo ku yaalid ku saabsan name of your project, password, and region.
"Nooqada cusub"Waaq ka mid ah loo isticmaalo, ka mid ah Project Settings → API oo ka mid ah Project URL iyo Anon Key - aad u baahan yahay ka hor.
Koo loo soo saarka, ka mid ah Project Settings → API oo ka mid ah Project URL iyo Anon Key - aad u baahan yahay ka hor.
Project Settings → APIWaraaqaha URLAnon Key
Coolka dhismaha iyo Database in Supabase
We will use Supabase’s built-in email/password auth:
Waayo, waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ka mid ah.Waaq ka mid ah Authentication → Providers
Waaq ka mid ah Authentication → Providers
Diiwaanka → WaqtigaWaax yar Email oo ka soo xiriir
Waax yar Email oo ka soo saarka
Tusaale : Tusaale : Tusaale : Tusaale : Tusaale : Tusaale : Tusaale(Optional) Waayo, waxaa loo isticmaali karaa in la isticmaali karaa in la isticmaali karaa in la isticmaali karaa.
(Optional) Waayo, waxaa loo isticmaali karaa in aad u isticmaali karaa in aad u isticmaali karaa in aad isticmaali karaa in aad isticmaali karaa
Shiqaalka dhismaha database
Araacada waxaa loo isticmaali karaa 3 tababarka ugu weyn: polls
, options
, iyo votes
.polls
options
votes
Sql Editor waxaa laga yaabaa
-- Create a polls table
CREATE TABLE polls (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
question TEXT NOT NULL,
created_by UUID REFERENCES auth.users(id) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()),
creator_name TEXT NOT NULL,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
);
-- Create an options table
CREATE TABLE options (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
poll_id UUID REFERENCES polls(id) ON DELETE CASCADE,
text TEXT NOT NULL,
);
-- Create a votes table
CREATE TABLE votes (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
poll_id UUID REFERENCES polls(id) ON DELETE CASCADE,
option_id UUID REFERENCES options(id) ON DELETE CASCADE,
user_id UUID REFERENCES auth.users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()),
UNIQUE(poll_id, user_id)
);
Row Level Security (RLS)
Enable RLS for each table and define policies:
RLSMarka aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u-- Polls policies
ALTER TABLE polls ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Anyone can view polls" ON polls
FOR SELECT USING (true);
CREATE POLICY "Authenticated users can create polls" ON polls
FOR INSERT TO authenticated
WITH CHECK (auth.uid() = created_by);
-- Options policies
ALTER TABLE options ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Anyone can view options" ON options
FOR SELECT USING (true);
CREATE POLICY "Poll creators can add options" ON options
FOR INSERT TO authenticated
WITH CHECK (
EXISTS (
SELECT 1 FROM polls
WHERE id = options.poll_id
AND created_by = auth.uid()
)
);
-- Votes policies
ALTER TABLE votes ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Anyone can view votes" ON votes
FOR SELECT USING (true);
CREATE POLICY "Authenticated users can vote once" ON votes
FOR INSERT TO authenticated
WITH CHECK (
auth.uid() = user_id AND
NOT EXISTS (
SELECT 1 FROM polls
WHERE id = votes.poll_id
AND created_by = auth.uid()
)
);
Saacadda loo isticmaali karaa macluumaadka real-time ee Supabase:
-
In the sidebar, go to Table Editor
-
For each of the three tables (
polls
,options
,votes
):-
Click the three dots → Edit Table
-
Toggle "Enable Realtime"
-
Save changes
-
In the sidebar, go to Table Editor
Waqtiisa waxaa ka mid ah Table Editor
Table Editor waxaa laga yaabaa
For each of the three tables (polls
, options
, votes
):
-
Click the three dots → Edit Table
-
Toggle "Enable Realtime"
-
Save changes
Waaq ka mid ah 3 tababarka (polls
, options
, votes
):
polls
options
votes
-
Click the three dots → Edit Table
-
Toggle "Enable Realtime"
-
Save changes
Click the three dots → Edit Table
Click the three dots → Edit Table
Waaqada TababarkaToggle "Waax yar"
Toggle "Waqtiga ah"
"Diiwaanka Realtime"Save changes
Waqtiisa waxaa laga yaabaa
Diiwaanka Supabase Email Authentication ee App
In this demo app, dadka waa in aad u aragto list of polls available on the app, both active and expired. To view the details of a poll, manage, or vote on any poll, user must be logged in. We will be using email and password as means of authentication for this project. In your Next.js project, guuley your Supabase credentials in .env.local
:
.env.local
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
Waaq u soo xiriir in aad login iyo login via email / password:
import { useState } from "react";
import { createClient } from "@/utils/supabase/component";
const LogInButton = () => {
const supabase = createClient();
async function logIn() {
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
setError(error.message);
} else {
setShowModal(false);
}
}
async function signUp() {
const { error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
user_name: userName,
},
},
});
if (error) {
setError(error.message);
} else {
setShowModal(false);
}
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
if (isLogin) {
await logIn();
} else {
await signUp();
}
};
return (
<>
<button
onClick={() => setShowModal(true)}
className="flex items-center gap-2 p-2 bg-gray-800 text-white rounded-md">
Log In
</button>
...
</>
);
};
export default LogInButton;
In this, waxaan u isticmaali signInWithPassword
ka mid ah Supabase si loo isticmaali karaa user iyo signUp
ka mid ah in la isticmaali karaa user cusub ee email iyo password. We are also storing the user's name in the user_name
field in user's metadata.
signInWithPassword
signUp
user_name
Anda sidoo kale loo isticmaali supabase.auth.signOut()
si loo isticmaali karaa users out iyo si ay u isticmaali karaa:
supabase.auth.signOut()
Import { createClient } from "@/utils/supabase/component"; import { useRouter } from "next/router"; const LogOutButton = ({ closeDropdown }: { closeDropdown: () => void }) => { const router = useRouter(); const supabase = createClient(); const handleLogOut = async () => { await supabase.auth.signOut(); closeDropdown(); router.push("/"}); return ( ... ); } export default LogOutButton;
import { createClient } from "@/utils/supabase/component";
import { useRouter } from "next/router";
const LogOutButton = ({ closeDropdown }: { closeDropdown: () => void }) => {
const router = useRouter();
const supabase = createClient();
const handleLogOut = async () => {
await supabase.auth.signOut();
closeDropdown();
router.push("/");
};
return (
...
);
};
export default LogOutButton;
Waa soo isticmaali signOut
ka mid ah Supabase si ay u hesho user iyo si ay u soo xiriir home page.
signOut
Listening for Changes in User's Authentication State
Listening for changes in the user's authentication state waxay ka heli kartaa UI ku saabsan status authentication user.
- Show/hide UI elements such as login/logout buttons
- Conditionally restrict access to protected pages (like voting or managing polls)
- Sure only authenticated users can perform restricted actions
We will use supabase.auth.onAuthStateChange()
to listen to these events and update the app accordingly.
supabase.auth.onAuthStateChange()
In the Layout.tsx
file: Dhammaan Dhammaan Dhammaan
Layout.tsx
file: Track Auth State oo dhan
import React, { useEffect, useState } from "react";
import { createClient } from "@/utils/supabase/component";
import { User } from "@supabase/supabase-js";
const Layout = ({ children }: { children: React.ReactNode }) => {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
const fetchUser = async () => {
const supabase = createClient();
const { data } = supabase.auth.onAuthStateChange((event, session) => {
setUser(session?.user || null);
});
return () => {
data.subscription.unsubscribe();
};
};
fetchUser();
}, []);
return (
...
);
};
export default Layout;
Restrict Access on Protected Pages
Waqtiga sida Details Poll ama Poll management, waxaad sidoo kale ku qiyaasta in ay ku habboonayaa wax soo saarka ah in ay ku habboonayaa isticmaalka la isticmaalaa.
Dhammaan oo dhanGanacsiga ugu fiican
Haddii waa mid ka mid ah pages/polls/[id].tsx
:
pages/polls/[id].tsx
Waayo, sidoo kale waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah midimport { createClient } from "@/utils/supabase/component";
import { User } from "@supabase/supabase-js";
const Page = () => {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
const fetchUser = async () => {
const supabase = createClient();
const { data } = supabase.auth.onAuthStateChange((event, session) => {
setUser(session?.user || null);
setLoading(false);
});
return () => {
data.subscription.unsubscribe();
};
};
fetchUser();
}, []);
return (
...
);
export default Page;
And a similar pattern applies in pages/polls/manage.tsx
, where users should only see their own polls if logged in:
pages/polls/manage.tsx
import { createClient } from "@/utils/supabase/component";
import { User } from "@supabase/supabase-js";
const Page = () => {
const [user, setUser] = useState<User | null>(null);
const supabase = createClient();
useEffect(() => {
const fetchUser = async () => {
const { data } = supabase.auth.onAuthStateChange((event, session) => {
setUser(session?.user || null);
if (!session?.user) {
setLoading(false);
}
});
return () => {
data.subscription.unsubscribe();
};
};
fetchUser();
}, []);
return (
...
);
};
export default Page;
Diiwaanka ugu badan waxaa laga yaabaa in aad UI oo ku yaalaa xawaaraha adeegga ah ee isticmaala iyo loo yaabaa xawaaraha adeegga ah oo ay ku yaalaa in ay ka mid ah ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka miduser
checkPermission
Waaqaynta shaqada App Polling
Waa soo xiriir Supabase iyo wax soo saarka authentication, sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale:
- Waaqadaha cusub
- Waaqadaha real-time
- Waaqadaha cusub
Diiwaanka waxaa loo yaqaan 'app' ah oo loo yaqaan 'app' ah oo loo yaqaan 'app' ah oo loo yaqaan 'app' ah.
Waaq u soo xiriir
Wala soo xiriir in la soo xiriir in la soo xiriir in la soo xiriir in la soo xiriir in la soo xiriir in la soo xiriir in la soo xiriir in la soo xiriir in la soo xiriir in la soo xiriir
Wala soo xiriir in la xiriir in la xiriir in la xiriir in la xiriir
Dhabka NewPoll.tsx
, ka mid ah loo isticmaali karaa, oo loo isticmaali karaa Supabase si ay u isticmaali karaa jilicsan iyo qiyaasadda:
NewPoll.tsx
import React, { useEffect, useState } from "react";
import { createClient } from "@/utils/supabase/component";
import { User } from "@supabase/supabase-js";
const NewPoll = () => {
const [user, setUser] = useState<User | null>(null);
const supabase = createClient();
useEffect(() => {
const fetchUser = async () => {
const supabase = createClient();
const { data } = supabase.auth.onAuthStateChange((event, session) => {
setUser(session?.user || null);
});
return () => {
data.subscription.unsubscribe();
};
};
fetchUser();
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (question.trim() && options.filter(opt => opt.trim()).length < 2) {
setErrorMessage("Please provide a question and at least two options.");
return;
}
// Create the poll
const { data: poll, error: pollError } = await supabase
.from("polls")
.insert({
question,
expires_at: new Date(expiryDate).toISOString(),
created_by: user?.id,
creator_name: user?.user_metadata?.user_name,
})
.select()
.single();
if (pollError) {
console.error("Error creating poll:", pollError);
setErrorMessage(pollError.message);
return;
}
// Create the options
const { error: optionsError } = await supabase.from("options").insert(
options
.filter(opt => opt.trim())
.map(text => ({
poll_id: poll.id,
text,
}))
);
if (!optionsError) {
setSuccessMessage("Poll created successfully!");
handleCancel();
} else {
console.error("Error creating options:", optionsError);
setErrorMessage(optionsError.message);
}
};
return (
...
);
};
export default NewPoll;
We will later call a Edge Function here to assign the “creator” role in Permit.io.
Diiwaanka iyo Diiwaanka Polls
Polls waxaa laga yaabaa in active (wax yar) iyo past (wax yar). Waxaad ka heli karaa in la isticmaali karaa qiyaasadda Supabase oo ku yaalaa timestamp ah, oo ay ku yaalaa adeegga real-time in ay ku yaalaa qiyaasadda.
Waayo, waxaa laga yaabaa in aad u baahan yahay in aad u baahan yahay.Xaabinta
Diiwaanka of pages/index.tsx
:
pages/index.tsx
import { PollProps } from "@/helpers";
import { createClient } from "@/utils/supabase/component";
export default function Home() {
const supabase = createClient();
useEffect(() => {
const fetchPolls = async () => {
setLoading(true);
const now = new Date().toISOString();
try {
// Fetch active polls
const { data: activePolls, error: activeError } = await supabase
.from("polls")
.select(
`
id,
question,
expires_at,
creator_name,
created_by,
votes (count)
`
)
.gte("expires_at", now)
.order("created_at", { ascending: false });
if (activeError) {
console.error("Error fetching active polls:", activeError);
return;
}
// Fetch past polls
const { data: expiredPolls, error: pastError } = await supabase
.from("polls")
.select(
`
id,
question,
expires_at,
creator_name,
created_by,
votes (count)
`
)
.lt("expires_at", now)
.order("created_at", { ascending: false });
if (pastError) {
console.error("Error fetching past polls:", pastError);
return;
}
setCurrentPolls(activePolls);
setPastPolls(expiredPolls);
} catch (error) {
console.error("Unexpected error fetching polls:", error);
} finally {
setLoading(false);
}
};
fetchPolls();
// Set up real-time subscription on the polls table:
const channel = supabase
.channel("polls")
.on(
"postgres_changes",
{
event: "*",
schema: "public",
table: "polls",
},
fetchPolls
)
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, []);
return (
...
);
}
Waqtiisa iyo Shuruudaha Shuruudaha user
Waaq, waxaan ka mid ah ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ka mid ka mid ah.
polls
polls
Sidaa pages/manage.tsx
page si ay u qaadi karaa iyo u aragtiyo mid ka mid ah wax soo saarka isticmaalka:
pages/manage.tsx
import { PollProps } from "@/helpers";
const Page = () => {
useEffect(() => {
if (!user?.id) return;
const fetchPolls = async () => {
try {
const { data, error } = await supabase
.from("polls")
.select(
`
id,
question,
expires_at,
creator_name,
created_by,
votes (count)
`
)
.eq("created_by", user.id)
.order("created_at", { ascending: false });
if (error) {
console.error("Error fetching polls:", error);
return;
}
setPolls(data || []);
} catch (error) {
console.error("Unexpected error fetching polls:", error);
} finally {
setLoading(false);
}
};
fetchPolls();
// Set up real-time subscription
const channel = supabase
.channel(`polls_${user.id}`)
.on(
"postgres_changes",
{
event: "*",
schema: "public",
table: "polls",
filter: `created_by=eq.${user.id}`,
},
fetchPolls
)
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, [user]);
return (
...
);
};
export default Page;
Waaq, waxaan ka mid ah ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ka mid ah.
polls
Waa'iid ka mid ah PollCard
component in ay ka mid ka mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah in la mid ah la mid ah in la mid ah in la mid ah in la mid ah in la mid ah.
PollCard
import { createClient } from "@/utils/supabase/component";
import { User } from "@supabase/supabase-js";
const PollCard = ({ poll }: { poll: PollProps }) => {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
const supabase = createClient();
const fetchUser = async () => {
const { data } = supabase.auth.onAuthStateChange((event, session) => {
setUser(session?.user || null);
setLoading(false);
});
return () => {
data.subscription.unsubscribe();
};
};
fetchUser();
}, []);
return (
...
)}
</Link>
);
};
export default PollCard;
Waa'iid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ka mid ah.
Waqtiisa nidaamka Poll Vote
Logic waxaa loo isticmaali karaa:
- Only one vote per user per poll
- Creators can't vote on their own polls
- Votes waxaa ku salaysan
votes
table - Results waxaa loo soo saarka iyo si ay u adeegsan in real-time
votes
tablevotes
Let's break down how this works in the ViewPoll.tsx
component:
ViewPoll.tsx
Fetch the Logged-In UserWe need the current user's ID to determine voting eligibility and record their vote.
Fetch user-ka ah oo loo yaabaa
import { createClient } from "@/utils/supabase/component";
import { User } from "@supabase/supabase-js";
const ViewPoll = () => {
const [user, setUser] = useState<User | null>(null);
const supabase = createClient();
useEffect(()
const fetchUser = async () => {
const {
data: { user },
} = await supabase.auth.getUser();
setUser(user);
};
fetchUser();
}, []);
Load Poll Details and Check Voting StatusWala soo bandhigay user, waxaan ka heli:
Load Poll Details iyo Check Voting Status- Qalabka ugu horeysay ee loo yaqaan 'Polling' (wax yar oo ku yaalaa options and votes counts)
- Waax yar oo ku yaalaa
Waa soo xiriir oo dhan oo ka mid ah wax soo saarka real-time.
useEffect(() => {
if (!user) {
return;
}
const checkUserVote = async () => {
const { data: votes } = await supabase
.from("votes")
.select("id")
.eq("poll_id", query.id)
.eq("user_id", user.id)
.single();
setHasVoted(!!votes);
setVoteLoading(false);
};
const fetchPoll = async () => {
const { data } = await supabase
.from("polls")
.select(
`
*,
options (
id,
text,
votes (count)
)
`
)
.eq("id", query.id)
.single();
setPoll(data);
setPollLoading(false);
checkUserVote();
};
fetchPoll();
Listen for Real-time Updates
Listen for Real-time UpdatesWaa soo xiriir oo ka mid ah macluumaadka ee tababarka votes
, si ay u hesho this poll.votes
const channel = supabase .channel(`poll-${query.id}`) .on( "postgres_changes", { event: "*", schema: "public", table: "votes", filter: `poll_id=eq.${query.id}`, }, () => { fetchPoll(); checkUserVote(); } ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, [query.id, user]);
const channel = supabase
.channel(`poll-${query.id}`)
.on(
"postgres_changes",
{
event: "*",
schema: "public",
table: "votes",
filter: `poll_id=eq.${query.id}`,
},
() => {
fetchPoll();
checkUserVote();
}
)
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, [query.id, user]);
Waqtiisa waxaa laga yaabaa in la soo xiriir
Waqtiisa waxaa laga yaabaa in la soo xiriirIf user has not voted and is allowed to vote (we will add a permit check later), we insert their vote.
const handleVote = async (optionId: string) => { if (!user) return; try { const { error } = wait supabase.from("votes").insert({ poll_id: query.id, option_id: optionId, user_id: user.id, }); if (!error) { setHasVoted(true); } } catch (error) { console.error("Error voting:", error); };
const handleVote = async (optionId: string) => {
if (!user) return;
try {
const { error } = await supabase.from("votes").insert({
poll_id: query.id,
option_id: optionId,
user_id: user.id,
});
if (!error) {
setHasVoted(true);
}
} catch (error) {
console.error("Error voting:", error);
}
};
Show Results of PollsWaa soo qiyaastii qiyaastii dhismaha iyo qiyaastii ka mid ah waqti dhismaha.
Show the Poll Results
if (!pollannoo-pollLoadingannoo-pollo voteLoading) return <div>Loading...</div> // 6. xisaabinta xisaabinta totalVotes = xisaabinta TotalVotes(poll.options); xisaabinta countdown = getCountdown(poll.expires_at); xisaabinta ( ... ); }; export default ViewPoll;
if (!poll || pollLoading || voteLoading) return <div>Loading...</div>;
// 6. calculate total votes
const totalVotes = calculateTotalVotes(poll.options);
const countdown = getCountdown(poll.expires_at);
return (
...
);
};
export default ViewPoll;
Wala soo saarka ugu horeysay ee loo yaabaa, nidaamka lagu soo saarka waxaa laga yaqaan 'Prompt.io' iyo Supabase Edge Functions waxaa loo isticmaali karaa.
Wala soo saarka waxaa laga yaqaan 'Prompt.io' iyo Supabase Edge Functions waxaa loo isticmaali karaa.
Wala soo bandhigiisa, ka dibna soo baxsan karaan layer qiyaastii ah oo aan la isticmaali karaa.
Diiwaanka ReBAC (Relationship-Based Access Control)
Supabase waxay ku dhigi karaa xisaabinta iyo xisaabinta sare leh, laakiin waxay ku dhigi karaa xisaabinta sare ah sida:
- Preventing users from voting on their own polls
- Assigning per-resource roles (like “creator” for a specific poll)
- Managing access via external policies
Waqtiisa in la soo saarka loo isticmaali karaa, waxaan u isticmaali karaa ReBAC iyo Permit.io.
Relationship-Based Access Control (ReBAC) is a model for managing permissions based on the relationships between users and resources. Instead of relying solely on roles or attributes (as in RBAC or ABAC), ReBAC determines access by evaluating how a user is connected to the resource they’re trying to access.
Relationship-Based Access Control (ReBAC)Relationship-Based Access Control (ReBAC)
In this tutorial, waxaan loo isticmaali karaa ReBAC in la isticmaali karo app:
- A user who created a poll is the only one who can manage (edit/delete) it
- A user cannot vote on their own poll
- Other authenticated users can vote once per poll
By modeling this relationships in Permit.io, waxaan sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale.
For more on ReBAC, check out Permit.io’s ReBAC docs.
Permit.io’s ReBAC docsDajinta Access Control
Waqtiisa waxaa loo isticmaali karaa:
Waayo, waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah midWaayo, waxaa laga yaabaa in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay.- polls:
create
,read
,delete
,update
.
create
read
delete
update
Waayo, waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka midWaayo, waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka midcreate
iyo read
ka mid ah macluumaadka.Waayo, waxaa laga yaabaa in ay ku saabsan:create
read
delete
update
Waayo, sidoo kale waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka midWaqtiga: create
read
delete
update
read
create
create
Diiwaanka Permit.io
Waqtiisa waa in la soo xiriir in la soo xiriir.
- Create a new project in Permit.io
- Name it something like
supabase-polling
- Name it something like
- Define the
polls
resource- Go to the Policy → Resources tab
- Click “Create Resource”
- Name it
polls
, and add the actions:read
,create
,update
,delete
- Enable ReBAC for the resource
-
Under “ReBAC Options,” define the following roles:
authenticated
creator
-
Click Save
-
- Name it something like
supabase-polling
- Name this something like
supabase-polling
supabase-polling
supabase-polling
Waayo, sidoo kale waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka midDefine iyopolls
Waayo, waxaa laga yaabaa in aad u baahan tahay in ay u baahan yahay in ay u baahan yahay in aad u baahan yahay.Waayo, waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka midpolls
, oo ka mid ah wax soo saarka: read
, create
, update
, delete
polls
read
create
update
delete
Waayo, sidoo kale waxaa laga yaabaa in aad Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada Cudarada CWaqtiga ReBAC for resourceWaayo, sidoo kale waxaa laga yaabaa in ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ka mid ah.Waayo, sidoo kale waxaa laga yaabaa in ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah.Waqtiga "ReBAC Options" waxaa loo yaqaan 'Rolls:
wax yar
authenticated
gaarka
creator
Waayo, waxaa laga yaabaa in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay in aad u baahan yahay.Waayo, waxaa laga yaabaa in aad u baahan tahay in aad Cudarada Cudarada Cudarada.Diiwaanka
Navigate in "Roles" tab si aad u aragto roles ka mid ah mid ah midabka waxaan ka mid ah loo aasaasay. Haddii aad u aragto in la aasaasay roles default (admin
, editor
, user
) oo aad u baahan yahay in this tutorial.
admin
editor
user
-
Define access policies
- Go to the Policy → Policies tab
- Use the visual matrix to define:
-
authenticated
canread
andcreate
polls -
creator
canread
,update
, anddelete
polls -
(Optional) You can configure vote permissions as part of this or via a second resource if you model votes separately
-
-
Add resource instances
-
Go to Directory → Instances
-
Add individual poll IDs as resource instances (you’ll automate this later when new polls are created)
-
Assign roles to users per poll (e.g.
user123
iscreator
ofpoll456
)
-
Define access policies
- Go to the Policy → Policies tab
- Use the visual matrix to define:
-
authenticated
canread
andcreate
polls -
creator
canread
,update
, anddelete
polls -
(Optional) You can configure vote permissions as part of this or via a second resource if you model votes separately
-
Diiwaanka ugu fiican ee macluumaadkaDiiwaanka warshadaha accessWaayo, sidoo kale waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid
-
authenticated
canread
andcreate
polls -
creator
canread
,update
, anddelete
polls -
(Optional) Waxaad ka heli karaa xisaabinta xisaabinta ka mid ah mid ka mid ah this ama ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah
-
authenticated
canread
andcreate
polls -
creator
canread
,update
, anddelete
polls -
authenticated
waxaa loo isticmaali karaaread
iyocreate
polls -
creator
waxaa laga yaabaaread
,update
, iyowax yar
polls -
(Optional) Waxaad ka heli karaa xisaabtood ka mid ah this ama ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah
(Optional) Waxaad ka heli karaa xisaabtood ka mid ah this ama ka mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah.
Diiwaanka ugu horeysay ee ku saabsan
Waqtiisa ah oo ku yaala-
Go to Directory → Instances
-
Add individual poll IDs as resource instances (you will automate this later when new polls are created)
-
Assign roles to users per poll (e.g.
user123
iscreator
ofpoll456
)
-
-
Go to Directory → Instances
-
Waax ka mid ah ID-ga ah oo ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah.
-
Waax yar oo ka mid ah macluumaad ka mid ah macluumaadka (e.g.
user123
waacreator
ofpoll456
)
- Sync isticmaalka cusub
- Role creator Assign
- Check access on demand
- Sync isticmaalka cusub
- Waax yar oo dhan
- Check access on demand
-
Sync si aad u baahan tahay in ay ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah.
Sync ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ahHaku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku:
Waaxii isticmaalka
creator
rol oo ka mid ah in la helocreator
import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { Permit } from "npm:permitio"; const corsHeaders = { 'Access-Control-Allow-Origin': "*", 'Access-Control-Allow-Headers': 'Authorization, x-client-info, apikey, Content-Type', 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE', } Deno.serve(async (req) => { const permit = new Permit({ token: Deno.env.get("PERMIT_API_KEY"), pdp: "<https://real-time-polling-app-production.up.railway.app>", }); try { const { userId, pollId } = await req.json(); // Validate input parameters if (!userId || !pollId) { return new Response( JSON.stringify({ error: "Missing required parameters." }), { status: 400, headers: { "Content-Type": "application/json" } }, ); } // Sync the resource (poll) to Permit.io await permit.api.syncResource({ type: "polls", key: pollId, tenant: "default", attributes: { createdBy: userId } }); // Assign the creator role to the user for this specific poll await permit.api.assignRole({ role: "creator", tenant: "default", user: userId, resource: { type: "polls", key: pollId, } }); return new Response( JSON.stringify({ message: "Creator role assigned successfully", success: true }), { status: 200, headers: corsHeaders }, ); } catch (error) { console.error("Error assigning creator role: ", error); return new Response( JSON.stringify({ message: "Error occurred while assigning creator role.", error: error }), { status: 500, headers: { "Content-Type": "application/json" } }, ); } });
Qalabka dhismaha (
checkPermission.ts
)checkPermission.ts
Function this function acts as the gatekeeper—it checks whether a user is allowed to perform a given action (
create
,read
,update
,delete
) on a particular poll.create
read
update
delete
import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { Permit } from "npm:permitio"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "Authorization, x-client-info, apikey, Content-Type", "Access-Control-Allow-Methods": "POST, GET, OPTIONS, PUT, DELETE", }; Deno.serve(async req => { const permit = new Permit({ token: Deno.env.get("PERMIT_API_KEY"), pdp: "<https://real-time-polling-app-production.up.railway.app>", }); try { const { userId, operation, key } = await req.json(); // Validate input parameters if (!userId || !operation || !key) { return new Response( JSON.stringify({ error: "Missing required parameters." }), { status: 400, headers: { "Content-Type": "application/json" } } ); } // Check permissions using Permit's ReBAC const permitted = await permit.check(userId, operation, { type: "polls", key, tenant: "default", // Include any additional attributes that Permit needs for relationship checking attributes: { createdBy: userId, // This will be used in Permit's policy rules }, }); return new Response(JSON.stringify({ permitted }), { status: 200, headers: corsHeaders, }); } catch (error) { console.error("Error checking user permission: ", error); return new Response( JSON.stringify({ message: "Error occurred while checking user permission.", error: error, }), { status: 500, headers: { "Content-Type": "application/json" } } ); } });
Local Testing
Diiwaanka adeegga Supabase ee loo yaqaan 'Development Server' ee loo yaqaan 'Development Server':
nppx supabase start nppx supabase functions serve
npx supabase start npx supabase functions serve
Saacadda waxaad ka heli karaa:
<http://localhost:54321/functions/v1/><function-name>
<http://localhost:54321/functions/v1/><function-name>
Tusaale: Tusaale: Tusaale: Tusaale: Tusaale<http://localhost:54321/functions/v1/checkPermission>
<http://localhost:54321/functions/v1/checkPermission>
Integrating Authorization Checks in UI
Wala soo saarka loo soo saarka logic autorization la Permit.io iyo loo soo saarka via Supabase Edge Functions, waxaa laga yaabaa in la qiyaasta in ay ka mid ah qiyaasta ah ee app.
In this section, waxaan soo dejiso components UI key si ay u soo xiriir macluumaadka iyo si ay u qiyaastii karaa ama si ay u qiyaastii wax soo saarka macaamiisha sida si ay u dhiso ama si ay u qiyaastii si ay u qiyaastii qiyaastii.
NewPoll.tsx
: Dhammaan Dhammaan Dhammaan Dhammaan DhammaanNewPoll.tsx
Saacad ka dib markii loo soo saarka oo ku salaysan in Supabase, waxaan ka heli
updateCreatorRole
function in:updateCreatorRole
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ -
Sync si loo isticmaali karaa mid ka mid ah 'Permit.io
-
Wala soo xiriir waxaa laga yaabaa in la soo xiriir oo la xiriir
-
Wala soo xiriir oo ka mid ah wax soo saarka oo ka mid ah wax soo saarka oo ka mid ah wax soo saarka oo ka mid ah wax soo saarka.
Waxaysiinta waxaa laga yaabaa in la heli karo in ay didn't create
Didn't waxaa laga yaabaaCreators cannot vote on their own polls
Creators cannot vote on their own polls
gaaxiib ah oo ku saabsanOnly creators see edit/delete options on their polls
Only creators see edit/delete options on their polls
xirfado/xirfado
Saacadda waxaa laga yaabaa in aad u aragto wax soo saarka ah ee app si aad u aragto browser. On screen home, users can view the list of active and past polls, whether they are logged in or not. However, when they click on a poll, they will not be able to view the poll details or vote on it. Instead, they will be prompted to log in.
Saacad ka hor si ay u soo xiriir, isticmaalka waa in ay u aragti si ay u aragti si ay u aragti si ay u aragti si ay u aragti si ay u aragti si ay u aragti si ay u aragti si ay u aragti.
Diiwaanka
In this tutorials, waxaan ku yaalaa sida loo isticmaalaa Supabase authentication iyo authorization in a real-world Next.js application.
Supabase authentication iyo kharashkaDiiwaanka.jsWala soo bandhigay Supabase Auth for login and signup, soo bandhigay dhismaha relational oo loo isticmaalo Row Level Security, iyo soo bandhigay dhismaha dhismaha dhismaha dhismaha dhismaha oo isticmaalo ReBAC.Wala soo bandhigay Supabase Edge Functions iyo a Policy Decision Point (PDP), sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale sidoo kale.
Supabase QalabkaDiiwaankaSupabase Edge Functions Policy Decision Point (PDP)
Dhammaan ka mid ah Supabase Auth iyo qiyaasta Access flexible, waxaan noqon doonaa:
Supabase Qalabka- Dhammaan users via email and password
- Restrict voting and poll management to authorized users
- Dhammaan creators from voting on their own polls
- Dhammaan iyo si ay u hesho role users based on relationships to data
- Coolka isticmaalka email iyo password
- Dhammaan oo ka mid ah shuruudaha iyo shuruudaha waxaa laga yaqaan 'Limit voting and poll management to authorized users'.
- Diiwaanka loo isticmaali karaa in la heli karaa in la heli karo oo ka mid ah wax soo saarka.
- Dhammaan iyo si ay u hesho user roles ku salaysan relationships to data
Dhammaan waxaa loo isticmaali karaa in ay u isticmaali karaa macluumaadka macluumaadka iyo macluumaadka.
Laha soo xiriir
Permit + Authentication Providers
Permit + Authentication ProvidersPermit Elements: Embedded UI for Role Management
Permit Elements: Embedded UI for Role Management
Got questions? Join our Slack community, where hundreds of developers are building and discussing authorization.
Slack communitySlack community
(Optional) Waxaad ka heli karaa xisaabinta xisaabinta ka mid ah this ama ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ah mid
authenticated
waxaa loo isticmaali karaaread
iyocreate
pollsauthenticated
read
create
creator
waxaa laga yaqaanread
,update
, iyodelete
pollscreator
read
update
delete
Go to Directory → Instances
Dhammaanada → DhammaanadaWaqtiyo ID-ga ah oo ku yaalaa mid ka mid ah macluumaadka ah (wax yar u isticmaalaa in ay ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah mid ah mid ka mid ah.
Waax yar oo ka mid ah macluumaad ka mid ah macluumaadka (e.g.
user123
waacreator
ofpoll456
)user123
creator
poll456
Waaqada this waxay ka dhigi karaa in ay ku xiran karaa qiyaasadaha qiyaasadaha iyo si ay u isticmaalo, per poll.
Now that we have completed the initial setup on the Permit dashboard, let's use it in our application. Next, we’ll connect Permit.io to our Supabase project via Edge Functions that:
Permit.ioWaqtiisa waxaa loo isticmaali karaa in la isticmaali karaa
Waqtiisa loo yaqaan 'Polling Application'Permit offers multiple ways to integrate with your application, but we'll use the Container PDP for this tutorial. You have to host the container online to access it in Supabase Edge functions. You can use services like railway.com. Once you have hosted it, save the url for your container.
railway.com
Dhammaan API-ka Dhammaan ka mid ah "Projects" ee tababarka tababarka Dhammaan, si ay u shaqeeyaan in ay u shaqeeyaan, si ay u mid ka mid ah three dots, oo ka mid ah "Copy API Key".
Creeing Supabase Edge Function API for Authorization
Supabase Edge Functions waa mid ka mid ah si ay u isticmaali karaa adeegga kale sida Permit.io. Waxaan loo isticmaali karaa si ay u isticmaali karaa qiyaasta ReBAC ee la xiran tahay in ay isticmaali karaa si ay u isticmaali karaa macluumaadka gaarka ah.
Supabase Edge FunctionsWaaq u soo xiriir oo loo isticmaali karaa
Waaq u soo xiriir in SupabaseDiiwaanka Supabase ee shuruudahaaga iyo loo helo 3 kala duwan oo loo isticmaalo
Supabase functions new
command.supabase functions new
npx supabase init npx supabase function new syncUser npx supabase function new updateCreatorRole npx supabase function new checkPermission
npx supabase init npx supabase functions new syncUser npx supabase functions new updateCreatorRole npx supabase functions new checkPermission
Diiwaanka waxaa loo isticmaali karaa foldera
functions
ee folderasupabase
sida loo isticmaali karaa endpoints.functions
supabase
Syncing Users to Permit.io on Signup (
syncUser.ts
)syncUser.ts
Function this listens for Supabase's
SIGNED_UP
auth event. Markaad ka mid ah isticmaalka cusub waa in la soo xiriir, waxaan soo xiriir si loo isticmaalaa Permit.io iyo si ay u soo xiriirauthenticated
rol.SIGNED_UP
authenticated
import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { Permit } from "npm:permitio"; const corsHeaders = { 'Access-Control-Allow-Origin': "*", 'Access-Control-Allow-Headers': 'Authorization, x-client-info, apikey, Content-Type', 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE', } // Supabase Edge Function to sync new users with Permit.io Deno.serve(async (req) => { const permit = new Permit({ token: Deno.env.get("PERMIT_API_KEY"), pdp: "<https://real-time-polling-app-production.up.railway.app>", }); try { const { event, user } = await req.json(); // Only proceed if the event type is "SIGNED_UP" if (event === "SIGNED_UP" && user) { const newUser = { key: user.id, email: user.email, name: user.user_metadata?.name || "Someone", }; // Sync the user to Permit.io await permit.api.createUser(newUser); await permit.api.assignRole({ role: "authenticated", tenant: "default", user: user.id, }); console.log(`User ${user.email} synced to Permit.io successfully.`); } // Return success response return new Response( JSON.stringify({ message: "User synced successfully!" }), { status: 200, headers: corsHeaders }, ); } catch (error) { console.error("Error syncing user to Permit: ", error); return new Response( JSON.stringify({ message: "Error syncing user to Permit.", "error": error }), { status: 500, headers: { "Content-Type": "application/json" } }, ); } });
Diiwaanka Role Creator (
updateCreatorRole.ts
)updateCreatorRole.ts
Wala soo saarka isticmaalka loo soo saarka, function this is called to:
Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku: Haku:Sync si aad u soo xiriir sida resource in Permit.io
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{Waaxii isticmaalka ugu horeysay ee
creator
rol for that specific pollcreator
const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (question.trim() && options.filter(opt => opt.trim()).length < 2) { setErrorMessage("Please provide a question and at least two options."); return; } try { // Create the poll const { data: poll, error: pollError } = await supabase .from("polls") .insert({ question, expires_at: new Date(expiryDate).toISOString(), created_by: user?.id, creator_name: user?.user_metadata?.user_name, }) .select() .single(); if (pollError) { console.error("Error creating poll:", pollError); setErrorMessage(pollError.message); return; } // Create the options const { error: optionsError } = await supabase.from("options").insert( options .filter(opt => opt.trim()) .map(text => ({ poll_id: poll.id, text, })) ); if (optionsError) { console.error("Error creating options:", optionsError); setErrorMessage(optionsError.message); return; } // Update the creator role in Permit.io const response = await fetch( "<http://127.0.0.1:54321/functions/v1//updateCreatorRole>", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user?.id, pollId: poll.id, }), } ); const { success, error } = await response.json(); if (!success) { console.error("Error updating creator role:", error); // Note: We don't set an error message here as the poll was still created successfully } setSuccessMessage("Poll created successfully!"); handleCancel(); } catch (error) { console.error("Error in poll creation process:", error); setErrorMessage("An unexpected error occurred while creating the poll."); } };
ViewPoll.tsx
: Qalabka ugu soo saarkaViewPoll.tsx
Wala soo bandhigay user ka soo bandhigay on a poll, waxaan soo bandhigay
checkPermission
function si ay u soo bandhigaycreate
permit on thevotes
resource. This is how we enforce the rule: “A creator cannot vote on their own poll.”checkPermission
create
votes
“A creator cannot vote on his own poll.”
Diiwaanka xisaabinta:
Waqtiisa loo yaabaa in la soo xiriir:
const [canVote, setCanVote] = useState(false); useEffect(() => { const checkPermission = async () => { if (!user || !query.id) return; try { const response = await fetch("<http://127.0.0.1:54321/functions/v1/checkPermission>", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user.id, operation: "create", key: query.id, }), }); const { permitted } = await response.json(); setCanVote(permitted); } catch (error) { console.error("Error checking permission:", error); setCanVote(false); } }; checkPermission(); }, [user, query.id]);
Diiwaanka si aad u isticmaali karaa:
Waax yar oo ka mid ah wax soo saarka:
<button onClick={() => handleVote(option.id)} disabled={!user Mediateca !canVote}} className="w-full text-left p-4 rounded-md hover:bg-slate-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"> {option.text} </button>
<button onClick={() => handleVote(option.id)} disabled={!user || !canVote}} className="w-full text-left p-4 rounded-md hover:bg-slate-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"> {option.text} </button>
Show a message if the user is not allowed to vote:
Show a message if user is not allowed to vote:
{user && !canVote && ( <p className="mt-4 text-gray-600">Diiwaanka waxaa laga yaabaa in aad u hesho</p> )}
{user && !canVote && ( <p className="mt-4 text-gray-600">You cannot vote on your own poll</p> )}
PollCard.tsx
: Qalabka ugu soo saarkaPollCard.tsx
Waa soo bandhigay ka mid ah macluumaadka loo isticmaali karaa (xirfadaha iyo xiran) by checking if the user has the
update
ordelete
permission on that poll.update
delete
Diiwaanka xisaabinta:
Diiwaanka xanuunada xanuunada:
const [canManagePoll, setCanManagePoll] = useState(false); useEffect(() => { const checkPollPermissions = async () => { if (!user || !poll.id) return; try { // Check for both edit and delete permissions const [editResponse, deleteResponse] = await Promise.all([ fetch("<http://127.0.0.1:54321/functions/v1/checkPermission>", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user.id, operation: "update", key: poll.id, }), }), fetch("/api/checkPermission", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user.id, operation: "delete", key: poll.id, }), }), ]); const [{ permitted: canEdit }, { permitted: canDelete }] = await Promise.all([editResponse.json(), deleteResponse.json()]); // User can manage poll if they have either edit or delete permission setCanManagePoll(canEdit || canDelete); } catch (error) { console.error("Error checking permissions:", error); setCanManagePoll(false); } }; checkPollPermissions(); }, [user, poll.id]);
Diiwaanka dhismaha dhismaha dhismaha:
Ganacsiga dhismaha dhismaha dhismaha:
Tusaale:
{user?.id === poll?.created_by && (
{user?.id === poll?.created_by && (
With:
{canManagePoll && ( <div className="flex justify-start gap-4 mt-4"> <button type="button" onClick={handleEdit}> </button> <button type="button" onClick={handleDelete}> </button> </div> )}
{canManagePoll && ( <div className="flex justify-start gap-4 mt-4"> <button type="button" onClick={handleEdit}> </button> <button type="button" onClick={handleDelete}> </button> </div> )}
Testing Integraasi
Saacad ka mid ah, sidoo kale aad u soo baxsan kartaa dhismaha ugu horeysay ee app:
Waayo, sidoo kale waxaa laga yaabaa in ka mid ah mid ka mid ah mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka mid ah mid ka midWala soo xiriir waxaa laga yaabaa in la soo xiriir oo la soo xiriir
-