Chapterبەش 7 · Lectureوانە 7.2
Storing IoT Readings in the Cloudهەڵگرتنی خوێندنەوەکانی IoT لە هەوردا
A cloud back end for sensor readings with Supabase: a typed Postgres table (time, temperature, cover_open), the REST API that PostgREST makes for it, the 2026 rule that tables need explicit grants, publishable and secret keys, Row Level Security policies, a Nano ESP32 sketch that adds a row every minute and reads the reply code, dashboards with an hourly query and a chart, the phone app, and Firebase Firestore compared.
هەوری بۆ خوێندنەوەی بە Supabase: جۆرداکراوی Postgres (کات، پلەی گەرمی، cover_open)، ئەو APIـیەی PostgREST بۆی دروست دەکات، یاسای 2026 کە مۆڵەتی ئاشکرایان دەوێت، و ، سیاسەتەکانی ، Nano ESP32 کە هەموو خولەکێک ڕیزێک زیاد دەکات و کۆدی وەڵام دەخوێنێتەوە، بە کاتژمێری و ، ئەپی مۆبایل و بەراوردکردن لەگەڵ Firebase Firestore.
What you will learnچی فێر دەبیت
- Design a typed table for sensor readings and create it with SQL.
- Use the auto-generated REST API to insert and read rows.
- Explain publishable and secret keys, grants and Row Level Security, and keep a device safe with them.
- Send a reading from a Wi-Fi board over HTTPS and act on the reply code.
- Show stored readings in a dashboard and an app, and compare Supabase with Firebase.
- جۆرداکراو بۆ خوێندنەوەی دیزاین بکەیت و بە دروستی بکەیت.
- APIـیە خۆکارەکە بۆ زیادکردن و خوێندنەوەی ڕیز بەکار بهێنیت.
- و ، مۆڵەت و ڕوون بکەیتەوە و ئامێرێک پێیان بپارێزیت.
- خوێندنەوەیەک لە بۆردێکی بە بنێریت و بەپێی کۆدی وەڵامەکە کار بکەیت.
- خوێندنەوە هەڵگیراوەکان لە و ئەپێکدا پیشان بدەیت و Supabase لەگەڵ Firebase بەراورد بکەیت.
Where do the readings go?خوێندنەوەکان بۆ کوێ دەچن؟
A sensor that reports every minute makes 1,440 readings a day. To be useful they must be stored, searched and shown: “what was the temperature last night?”, “was the cover closed at sunset?”. That is the job of a back end in the cloud (the cloud model). Instead of building a server yourself, you can rent a Backend-as-a-Service that gives you a database, an API and sign-in ready-made (BaaS).
کە هەموو خولەکێک دەنێرێت ڕۆژانە 1,440 خوێندنەوە دروست دەکات. بۆ ئەوەی بەسوود بن دەبێت هەڵبگیرێن، بگەڕێنرێن و پیشان بدرێن: «دوێنێ شەو پلەی گەرمی چەند بوو؟»، «لە کاتی ئاوابوونی خۆردا بەرگەکە داخرابوو؟». ئەمە ئەرکی لە هەوردا (مۆدێلی هەور). لە جیاتی ئەوەی خۆت دروست بکەیت، دەتوانیت بەکرێ بگریت کە ، API و چوونەژوورەوەت بە ئامادەیی دەداتێ (BaaS).
This lesson uses Supabase, the service named in this course’s bird-cage project: every Supabase project is a full Postgres database with an API made for you. The path is: board → Wi-Fi → HTTPS request → a table → apps and dashboards. Sending over HTTP and JSON was taught in Sending IoT data.
ئەم وانەیە Supabase بەکار دەهێنێت، ئەو خزمەتگوزارییەی لە پرۆژەی قەفەزی باڵندەی ئەم خولەدا ناوی هاتووە: هەموو پرۆژەیەکی Supabase تەواوی Postgresـە لەگەڵ APIـیەک کە بۆت دروست کراوە. ڕێڕەوەکە ئەمەیە: بۆرد ← ← ← ← ئەپ و . ناردن بە و لە ناردنی دراوەی IoT فێر کرا.
Design the readings tableدیزاینی خشتەی خوێندنەوەکان
In a relational database, data lives in tables: each row (record) is one reading and each column (field) has a fixed data type (tables, rows and columns). The project stores two values per reading — the temperature (a decimal number, a double) and whether the cage cover is open (a Boolean) — so the table needs:
لە لە یە: هەر (ڕیز) یەک خوێندنەوەیە و هەر (ستوون) جێگیری هەیە (خشتە، ڕیز و ستوون). پرۆژەکە دوو نرخ بۆ هەر خوێندنەوەیەک هەڵدەگرێت — پلەی گەرمی (، double) و ئایا بەرگی قەفەزەکە کراوەیە () — بۆیە پێویستی بەمانەیە:
id— a number the database gives each row (the primary key).created_at— a timestamp the database fills in itself; a small board has no reliable clock. Postgrestimestamptzstores it in UTC, and the app shows Erbil time (UTC+3).device— which board sent it, so several cages can share one table.temperature—double precision(a double, also calledfloat8).cover_open—boolean: true or false.
id— ژمارەیەک کە دەیداتە هەر ڕیزێک ().created_at— کە خۆی پڕی دەکاتەوە؛ بۆردێکی بچووک کاتژمێرێکی متمانەپێکراوی نییە.timestamptzـی Postgres بە UTC هەڵیدەگرێت و ئەپەکە کاتی هەولێر (UTC+3) پیشان دەدات.device— کام بۆرد ناردوویەتی، بۆ ئەوەی چەند قەفەزێک یەک بەکار بهێنن.temperature—double precision(double، کە پێی دەڵێنfloat8ـیش).cover_open—boolean: true یان false.
Design the readings tableدیزاینی خشتەی خوێندنەوەکان
Creating the table in Supabaseدروستکردنی خشتەکە لە Supabaseـدا
You can click the table together in Supabase’s Table Editor, or paste this SQL into its SQL Editor (CREATE TABLE). It makes the table, switches on Row Level Security, and gives each kind of user only what it needs — the next screens explain the grants, the keys and the policies.
دەتوانیت لە Table Editorـی Supabaseـدا بە کرتە دروست بکەیت، یان ئەم لە SQL Editorـەکەیدا دابنێیت (CREATE TABLE). دروست دەکات، هەڵدەکات و بە هەر جۆرە بەکارهێنەرێک تەنیا ئەوە دەدات کە پێویستیەتی — پەڕەکانی داهاتوو مۆڵەتەکان، و سیاسەتەکان ڕوون دەکەنەوە.
-- 1. The table: one row per reading
create table public.readings (
id bigint generated always as identity primary key,
created_at timestamptz not null default now(),
device text not null,
temperature double precision not null check (temperature between -40 and 80),
cover_open boolean not null
);
-- 2. Switch on Row Level Security
alter table public.readings enable row level security;
-- 3. Boards (publishable key = role anon) may only add rows
grant insert on public.readings to anon;
create policy "boards can add readings"
on public.readings for insert
to anon
with check (device = 'cage-01');
-- 4. Signed-in users of the app may read them
grant select on public.readings to authenticated;
create policy "signed-in users can read readings"
on public.readings for select
to authenticated
using (true);Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 3
generated always as identity: the database numbers the rows itself.generated always as identity: خۆی ژمارە دەداتە ڕیزەکان.- Lineدێڕی 4
default now()fills in the time of arrival.default now()کاتی گەیشتن پڕ دەکاتەوە.- Lineدێڕی 6
A check refuses impossible values, such as 999 °C from a faulty sensor.
check نرخی نەگونجاو ڕەت دەکاتەوە، وەک 999 °C لە تێکچووەوە.
- Lineدێڕی 14-18
The board may only add rows, and only for its own device name.
بۆردەکە تەنیا دەتوانێت ڕیز زیاد بکات، ئەویش تەنیا بۆ ناوی ئامێرەکەی خۆی.
- Lineدێڕی 21-25
Signed-in app users may read; nobody may change or delete rows through the API.
بەکارهێنەرانی چووەژوورەوەی ئەپ دەتوانن بخوێننەوە؛ هیچ کەس ناتوانێت لە ڕێگەی APIـیەوە ڕیز بگۆڕێت یان بسڕێتەوە.
A REST API made for youREST APIـیەک کە بۆت دروست کراوە
Supabase reads your tables and builds a REST API for them automatically, with a program called PostgREST: change the table and the API changes at once. Each table becomes a URL under https://<project-ref>.supabase.co/rest/v1/, and the HTTP method says what to do: POST inserts a row, GET reads rows (with filters such as ?device=eq.cage-01), PATCH updates and DELETE removes (REST). A successful insert answers 201 Created.
Supabase دەخوێنێتەوە و بە شێوەی خۆکار APIـیان بۆ دروست دەکات، بە بەرنامەیەک بە ناوی PostgREST: بگۆڕە و API یەکسەر دەگۆڕێت. هەر دەبێتە لە ژێر https://<project-ref>.supabase.co/rest/v1/ و دەڵێت چی بکرێت: POST ڕیزێک زیاد دەکات، GET ڕیز دەخوێنێتەوە (بە فلتەری وەک ?device=eq.cage-01)، PATCH دەگۆڕێت و DELETE دەسڕێتەوە (REST). زیادکردنێکی سەرکەوتوو بە 201 Created وەڵام دەداتەوە.
POST /rest/v1/readings HTTP/1.1
Host: your-project-ref.supabase.co
apikey: sb_publishable_REPLACE_WITH_YOUR_KEY
Content-Type: application/json
Prefer: return=minimal
{"device":"cage-01","temperature":24.5,"cover_open":true}Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 3
The publishable key goes in the
apikeyheader (new Supabase keys are not sent asAuthorization: Bearer).دەچێتە ناو سەردێڕی
apikey( نوێی Supabase وەکAuthorization: Bearerنانێردرێن).- Lineدێڕی 5
return=minimal: answer 201 with no body, to save the board’s memory and data.return=minimal: بە 201 بێ جەستە وەڵام بدەرەوە، بۆ پاشەکەوتی و بۆردەکە.
A REST API made for youREST APIـیەک کە بۆت دروست کراوە
Since October 2026: grant the table firstلە تشرینی یەکەمی 2026ـەوە: سەرەتا مۆڵەتی خشتەکە بدە
Supabase changed a default in 2026. New projects since 30 May 2026, and all existing projects since 30 October 2026, no longer open new tables to the API automatically. A table is reachable only after an explicit grant — an SQL permission that says which role may use it (GRANT and REVOKE). Without one, the API answers with error 42501, “permission denied for table readings” (HTTP 401 for the publishable key).
Supabase لە 2026ـدا ڕێکخستنێکی بنەڕەتیی گۆڕی. پرۆژە نوێکان لە 30ی ئایاری 2026ـەوە و هەموو پرۆژە کۆنەکان لە 30ی تشرینی یەکەمی 2026ـەوە ئیتر نوێیەکان بە شێوەی خۆکار بۆ API ناکەنەوە. تەنیا دوای مۆڵەتێکی ئاشکرا (grant) دەگاتێ — ڕێگەپێدانێکی کە دەڵێت کام ڕۆڵ دەتوانێت بەکاری بهێنێت (GRANT و REVOKE). بەبێ ئەو، API بە هەڵەی 42501 وەڵام دەداتەوە، «permission denied for table readings» (HTTP 401 بۆ ).
So grants and policies work as a pair: the grant opens the table to a role, and Row Level Security decides which rows that role may touch. Lines 14 and 21 of the SQL above are the grants.
کەواتە مۆڵەت و سیاسەت پێکەوە کار دەکەن: مۆڵەت بۆ ڕۆڵێک دەکاتەوە و دیاری دەکات ئەو ڕۆڵە دەتوانێت دەست لە کام ڕیزانە بدات. دێڕەکانی 14 و 21ی سەرەوە مۆڵەتەکانن.
Two keys: publishable and secretدوو کلیلی API: ئاشکرا و نهێنی
Every request carries an API key that tells Supabase which project and which role is asking. Supabase gives each project two kinds (as of November 2026):
هەموو هەڵدەگرێت کە بە Supabase دەڵێت کام پرۆژە و کام ڕۆڵ داوا دەکات. Supabase دوو جۆر دەداتە هەر پرۆژەیەک (تا تشرینی دووەمی 2026):
- The publishable key (
sb_publishable_…) is made to be public: it may sit in a board’s firmware, a phone app or a web page. It acts as theanonrole (orauthenticatedonce a user signs in), so it can do only what the grants and RLS policies allow. - The secret key (
sb_secret_…) has full power: it acts asservice_role, which bypasses Row Level Security. It belongs only on a server you control — never in firmware, an app, a browser or a public GitHub repository.
- (
sb_publishable_…) بۆ ئەوە دروست کراوە کە ئاشکرا بێت: دەتوانێت لە بۆردێک، ئەپێکی مۆبایل یان بێت. وەک ڕۆڵیanonکار دەکات (یانauthenticatedکاتێک بەکارهێنەرێک دەچێتە ژوورەوە)، بۆیە تەنیا ئەوە دەکات کە مۆڵەت و سیاسەتەکانی ڕێگەی دەدەن. - (
sb_secret_…) دەسەڵاتی تەواوی هەیە: وەکservice_roleکار دەکات، کە تێدەپەڕێنێت. تەنیا لەسەر دەبێت کە خۆت کۆنترۆڵی دەکەیت — هەرگیز لە ، ئەپ، یان کۆگایەکی گشتیی GitHubـدا نا.
Two keys: publishable and secretدوو کلیلی API: ئاشکرا و نهێنی
Old keys and leaked keysکلیلەکانی APIـی کۆن و دزەکردوو
The older keys, anon and service_role (long JWTs starting with eyJ), work the same way but are being retired by the end of 2026; new projects should use the new pair (API keys).
کۆن، anon و service_role (JWTـی درێژ کە بە eyJ دەست پێ دەکەن)، بە هەمان شێوە کار دەکەن، بەڵام تا کۆتایی 2026 لادەبرێن؛ پرۆژە نوێکان دەبێت جووتە نوێیەکە بەکار بهێنن (کلیلەکانی API).
Row Level Security: the gate at every rowئاسایشی ئاستی ڕیز: دەرگا لە هەر ڕیزێکدا
rules that decide, row by row, who may read or change each of a . In Supabase they make it safe to use a in a device.
یاساکانی کە ڕیز بە ڕیز بڕیار دەدەن کێ دەتوانێت هەر بخوێنێتەوە یان بیگۆڕێت. لە Supabaseـدا وا دەکەن بەکارهێنانی لەناو ئامێرێکدا سەلامەت بێت.
With RLS switched on and no policies, the publishable key can reach no data at all. Each policy then opens one narrow door: for insert … with check (…) says which new rows a role may add; for select … using (…) says which rows it may read. In the readings table the board (role anon) may only insert rows for cage-01, and signed-in app users (role authenticated) may only read. Nobody may update or delete through the API. This is the same idea as Firebase Security Rules (security rules).
کاتێک هەڵکراوە و هیچ سیاسەتێک نییە، ناگاتە هیچ . پاشان هەر سیاسەتێک دەرگایەکی تەسک دەکاتەوە: for insert … with check (…) دەڵێت ڕۆڵێک کام ڕیزی نوێ دەتوانێت زیاد بکات؛ for select … using (…) دەڵێت کام ڕیزانە دەتوانێت بخوێنێتەوە. لە خوێندنەوەکاندا بۆردەکە (ڕۆڵی anon) تەنیا دەتوانێت ڕیز بۆ cage-01 زیاد بکات و بەکارهێنەرانی چووەژوورەوەی ئەپ (ڕۆڵی authenticated) تەنیا دەتوانن بخوێننەوە. هیچ کەس لە ڕێگەی APIـیەوە ناتوانێت بیگۆڕێت یان بیسڕێتەوە. ئەمە هەمان بیرۆکەی Firebase Security Rulesـە (یاساکانی ئاسایش).
Sending a reading from the boardناردنی خوێندنەوەیەک لە بۆردەکەوە
The device is a Nano ESP32, a Wi-Fi board (boards for IoT), with a DHT11 on pin 2 and the cover’s limit switch on pin 3 (limit switch). Every minute the sketch reads both, builds one JSON row and POSTs it over HTTPS.
ئامێرەکە Nano ESP32ـە، بۆردێکی (بۆردەکان بۆ IoT)، لەگەڵ DHT11 لەسەر پینی 2 و بەرگەکە لەسەر پینی 3 (سویچی سنوور). هەموو خولەکێک هەردووکیان دەخوێنێتەوە، ڕیزێکی دروست دەکات و بە POSTی دەکات.
// Never share this file or upload it to GitHub.
#define SECRET_SSID "your-wifi-name"
#define SECRET_PASS "your-wifi-password"
#define SUPABASE_URL "https://your-project-ref.supabase.co"
#define SUPABASE_KEY "sb_publishable_REPLACE_WITH_YOUR_KEY" // publishable only, never the secret key
#define SECRET_ROOT_CA "-----BEGIN CERTIFICATE-----\n...the server's root certificate...\n-----END CERTIFICATE-----\n"Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 5
Only the publishable key ever goes here.
تەنیا دەچێتە ئێرە.
// Add one reading to the Supabase table "readings" every minute (Nano ESP32)
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <DHT.h>
#include "arduino_secrets.h" // Wi-Fi, project URL, publishable key, root certificate
DHT dht(2, DHT11); // DHT11 data pin on pin 2
const int closedSwitch = 3; // limit switch: pressed (LOW) = cover closed
WiFiClientSecure tls;
void setup() {
Serial.begin(115200);
dht.begin();
pinMode(closedSwitch, INPUT_PULLUP);
WiFi.begin(SECRET_SSID, SECRET_PASS);
while (WiFi.status() != WL_CONNECTED) delay(250);
Serial.println("Wi-Fi connected");
tls.setCACert(SECRET_ROOT_CA); // HTTPS: check the server's certificate
}
void loop() {
float t = dht.readTemperature();
bool coverOpen = digitalRead(closedSwitch) == HIGH;
if (isnan(t)) {
Serial.println("DHT11 read failed: nothing sent");
} else {
String body = "{\"device\":\"cage-01\",\"temperature\":" + String(t, 1) +
",\"cover_open\":" + (coverOpen ? "true" : "false") + "}";
HTTPClient http;
http.begin(tls, String(SUPABASE_URL) + "/rest/v1/readings");
http.addHeader("apikey", SUPABASE_KEY);
http.addHeader("Content-Type", "application/json");
http.addHeader("Prefer", "return=minimal");
int code = http.POST(body); // 201 = the row was added
http.end();
Serial.println(body + " -> HTTP " + String(code));
}
delay(60000); // the next reading in a minute
}Wi-Fi connected
{"device":"cage-01","temperature":24.5,"cover_open":true} -> HTTP 201
{"device":"cage-01","temperature":24.5,"cover_open":true} -> HTTP 201
{"device":"cage-01","temperature":24.1,"cover_open":false} -> HTTP 201
{"device":"cage-01","temperature":24.1,"cover_open":false} -> HTTP 201CêIT Arduino checker with its Wi-Fi and HTTPClient stand-ins (every POST answers 201, as PostgREST does after an insert); scenario: 24.5 °C with the cover open, then 24.1 °C and the switch pressed from 110 s; 190 s simulated.
پشکنەری Arduinoـی CêIT لەگەڵ جێگرەوەکانی WiFi و HTTPClient (هەموو POSTـێک بە 201 وەڵام دەداتەوە، وەک PostgREST دوای زیادکردن)؛ سیناریۆ: 24.5 °C و بەرگی کراوە، پاشان 24.1 °C و پەستێنراو لە چرکەی 110ـەوە؛ 190 چرکە لاسایی کراوەتەوە.
Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 19
HTTPS: the board checks the server’s certificate before sending anything.
: بۆردەکە پێش ناردنی هەر شتێک دەپشکنێت.
- Lineدێڕی 24
Pressed switch (LOW) = cover closed, so HIGH means not closed.
پەستێنراو (LOW) = بەرگی داخراو، بۆیە HIGH واتە دانەخراو.
- Lineدێڕی 25-26
A failed DHT11 read sends nothing (NaN is not valid JSON).
خوێندنەوەیەکی تێکچووی DHT11 هیچ نانێرێت (NaN دروست نییە).
- Lineدێڕی 28-29
Only the three columns the board knows; id and created_at come from the database.
تەنیا ئەو سێ بۆردەکە دەیانزانێت؛ id و created_at لە دێن.
- Lineدێڕی 32-34
The three headers: the key, the format, and “no body back, please”.
سێ سەردێڕەکە: ، شێوازەکە و «تکایە جەستە مەگەڕێنەرەوە».
- Lineدێڕی 35
POST()returns the HTTP status code, or a negative number if there was no connection.POST()دەگەڕێنێتەوە، یان ژمارەیەکی نەرێنی ئەگەر پەیوەندی نەبوو.
Sending a reading from the boardناردنی خوێندنەوەیەک لە بۆردەکەوە
What the reply tells the boardوەڵامەکە چی بە بۆردەکە دەڵێت
A device far from anyone must decide by itself what to do when sending fails, so the sketch should look at the code that POST() returns. With the publishable key, PostgREST answers 401 both for a wrong key and for a missing grant or a refused policy (the body then contains error 42501); a wrong column or a broken check gives 400. A negative number means the request never reached the server.
ئامێرێک کە لە هەموو کەس دوورە دەبێت خۆی بڕیار بدات چی بکات کاتێک ناردن سەرناکەوێت، بۆیە دەبێت سەیری ئەو کۆدە بکات کە POST() دەیگەڕێنێتەوە. بە ، PostgREST هەم بۆ هەڵە و هەم بۆ نەبوونی مۆڵەت یان ڕەتکردنەوەی سیاسەت 401 دەداتەوە (لەو کاتەدا جەستەکە هەڵەی 42501ـی تێدایە)؛ هەڵە یان checkـێکی شکاو 400 دەدات. ژمارەیەکی نەرێنی واتە هەرگیز نەگەیشتووەتە .
Dashboards: seeing the dataداشبۆردەکان: بینینی دراوە
A screen that shows the most important live of a system at a glance, with numbers, charts and alerts, such as today's temperatures from all sensors.
شاشەیەکە کە گرنگترین ڕاستەوخۆی سیستەمێک بە یەک سەیرکردن پیشان دەدات، بە ژمارە، و ئاگادارکردنەوە، وەک پلەی گەرمیی ئەمڕۆ لە هەموو .
Rows are hard to read; a chart is not. A dashboard for the cage could show the temperature line for the last day against the 20–25 °C target, and when the cover was open. Supabase’s own Table Editor shows the rows, and dashboard tools such as Grafana can connect straight to a Postgres database. Behind each chart is a query; this one, run in the SQL Editor, gives one average per hour:
خوێندنەوەی ڕیزەکان قورسە؛ نا. بۆ قەفەزەکە دەتوانێت هێڵی پلەی گەرمیی دوایین ڕۆژ بەرامبەر ئامانجی 20–25 °C پیشان بدات، لەگەڵ ئەوەی بەرگەکە کەی کراوە بوو. Table Editorـی خودی Supabase ڕیزەکان پیشان دەدات و ئامرازەکانی وەک Grafana دەتوانن ڕاستەوخۆ بە Postgresـەوە ببەسترێن. لە پشت هەر هەیە؛ ئەمەیان، کە لە SQL Editorـدا کار دەکات، بۆ هەر کاتژمێرێک تێکڕایەک دەدات:
select date_trunc('hour', created_at) as hour,
round(avg(temperature)::numeric, 1) as avg_temp
from public.readings
where device = 'cage-01'
and created_at > now() - interval '1 day'
group by hour
order by hour;Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 1
date_trunccuts each time down to its hour, so readings fall into hourly groups.date_truncهەر کاتێک دەبڕێتەوە بۆ کاتژمێرەکەی، بۆیە خوێندنەوەکان دەکەونە گرووپی کاتژمێری.- Lineدێڕی 2
::numericis needed because Postgres rounds to one decimal only on numeric values.::numericپێویستە چونکە Postgres تەنیا لەسەر نرخی numeric بۆ یەک ژمارەی دوای فاریزە دەیخڕێنێت.
Dashboards: seeing the dataداشبۆردەکان: بینینی دراوە
A new row every minuteهەر خولەکێک ڕیزێکی نوێ
Watch the table and the chart grow. Each minute the board POSTs, the API answers 201 and one more row appears. With Supabase Realtime, a dashboard or app can subscribe to the table and receive each new row as soon as it is inserted, instead of asking again and again — the same push idea as MQTT (publish and subscribe).
سەیری گەشەکردنی و بکە. هەموو خولەکێک بۆردەکە POST دەکات، API بە 201 وەڵام دەداتەوە و ڕیزێکی تر دەردەکەوێت. بە Realtimeـی Supabase، یان ئەپێک دەتوانێت بەشداری بێت و هەر ڕیزێکی نوێ هەر کە زیاد کرا وەربگرێت، لە جیاتی ئەوەی هەر بپرسێت — هەمان بیرۆکەی ناردنی ڕاستەوخۆی (بڵاوکردنەوە و بەشداربوون).


The phone appئەپی مۆبایل
The owner wants to see the cage from anywhere. The phone app holds only the publishable key, lets the user sign in (role authenticated), and then reads the newest rows with a GET request — exactly the HTTP and JSON skills of the Flutter track (HTTP and JSON in Flutter) — or receives new rows through Realtime. Supabase also has an official Flutter package that wraps these calls. The app, like the board, never holds the secret key.
خاوەنەکە دەیەوێت لە هەر شوێنێکەوە قەفەزەکە ببینێت. ئەپی مۆبایل تەنیا هەڵدەگرێت، ڕێگە بە بەکارهێنەر دەدات بچێتە ژوورەوە (ڕۆڵی authenticated) و پاشان نوێترین ڕیزەکان بە GET دەخوێنێتەوە — ڕێک تواناکانی و بەشی Flutter (HTTP و JSON لە Flutterـدا) — یان ڕیزە نوێیەکان لە ڕێگەی Realtimeـەوە وەردەگرێت. Supabase هەروەها پاکێجێکی فەرمیی Flutterـی هەیە کە ئەم بانگکردنانە ئاسان دەکات. ئەپەکە، وەک بۆردەکە، هەرگیز هەڵناگرێت.
Firebase comparedبەراوردکردن لەگەڵ Firebase
Firebase, Google’s BaaS, can store the same readings in Cloud Firestore, a NoSQL database: instead of rows in a table, each reading is a document of fields in a collection called readings, and documents may have different fields (Firestore’s data model). Firebase Security Rules play the part of RLS. Supabase was described in this course’s project as “similar to Google Firebase”; the real difference is the database underneath.
Firebase، Google، دەتوانێت هەمان خوێندنەوەکان لە Cloud Firestoreـدا هەڵبگرێت، کە : لە جیاتی ڕیز لە ، هەر خوێندنەوەیەک بەڵگەنامەیەکە لە لە ناو کۆمەڵەیەکدا بە ناوی readings و بەڵگەنامەکان دەتوانن جیاوازیان هەبێت (مۆدێلی دراوەی Firestore). Firebase Security Rules ڕۆڵی دەگێڕن. لە پرۆژەی ئەم خولەدا Supabase وەک «هاوشێوەی Firebaseـی Google» باس کراوە؛ جیاوازیی ڕاستەقینە ئەو لە ژێرەوەیە.
| Featureتایبەتمەندی | Supabase | Firebase (Firestore) |
|---|---|---|
| Database | Postgres: relational tablesPostgres: پەیوەندیدار | Firestore: NoSQL documentsFirestore: بەڵگەنامەی |
| Shape of dataشێوەی | fixed, typed columns جێگیر و جۆرداکراو | flexible fields per document نەرم بۆ هەر بەڵگەنامەیەک |
| Queries | full SQL: filters, joins, averages تەواو: فلتەر، بەستنەوە، تێکڕا | simpler queries: count, sum and average, but no GROUP BY or joins سادەتر: ژماردن، کۆ و تێکڕا، بەڵام بێ GROUP BY و بەستنەوە |
| Protectionپاراستن | grants + RLS policiesمۆڵەت + سیاسەتی | Firebase Security Rules |
| Reach from a boardگەیشتن لە بۆردەوە | REST API over HTTPS API بە | REST API or an SDK API یان |
Common mistakesهەڵە باوەکان
Exercisesڕاهێنانەکان
Write the SQL for a table greenhouse that stores, per reading, the device name, the air temperature (decimal), the soil moisture in % (a whole number from 0 to 100) and whether the pump is on. Let the database number the rows and add the time.
بە ناوی greenhouse بنووسە کە بۆ هەر خوێندنەوەیەک ناوی ئامێرەکە، پلەی گەرمیی هەوا (کەرتی)، شێی خاک بە % ( لە 0 تا 100) و ئایا پەمپەکە هەڵکراوە هەڵدەگرێت. با ژمارە بداتە ڕیزەکان و کاتەکە زیاد بکات.
Show answerوەڵامەکە پیشان بدە
create table public.greenhouse (
id bigint generated always as identity primary key,
created_at timestamptz not null default now(),
device text not null,
temperature double precision not null,
soil_moisture integer not null check (soil_moisture between 0 and 100),
pump_on boolean not null
);
alter table public.greenhouse enable row level security;For that table, write the JSON body a board sends for: device gh-01, 29.4 °C, soil 37 %, pump off. Which headers does the request need?
بۆ ئەو ، ئەو جەستە بنووسە کە بۆردێک دەینێرێت بۆ: ئامێری gh-01، 29.4 °C، خاک 37 %، پەمپ کوژاوە. کام سەردێڕانەی پێویستە؟
Show answerوەڵامەکە پیشان بدە
{"device": "gh-01", "temperature": 29.4, "soil_moisture": 37, "pump_on": false} — POST to /rest/v1/greenhouse with apikey: <publishable key>, Content-Type: application/json and, optionally, Prefer: return=minimal. No id and no created_at: the database adds them.
{"device": "gh-01", "temperature": 29.4, "soil_moisture": 37, "pump_on": false} — POST بۆ /rest/v1/greenhouse بە apikey: <publishable key>، Content-Type: application/json و بە ئارەزووی خۆت Prefer: return=minimal. نە id و نە created_at: زیادیان دەکات.
For greenhouse: boards using the publishable key may only insert rows whose device starts with gh-; signed-in users may read all rows. Write the grants and the policies.
بۆ greenhouse: ئەو بۆردانەی بەکار دەهێنن تەنیا دەتوانن ئەو ڕیزانە زیاد بکەن کە ناوی ئامێرەکەیان بە gh- دەست پێ دەکات؛ بەکارهێنەرانی چووەژوورەوە دەتوانن هەموو ڕیزەکان بخوێننەوە. مۆڵەت و سیاسەتەکان بنووسە.
Show answerوەڵامەکە پیشان بدە
grant insert on public.greenhouse to anon;
create policy "boards add greenhouse rows"
on public.greenhouse for insert
to anon
with check (device like 'gh-%');
grant select on public.greenhouse to authenticated;
create policy "users read greenhouse rows"
on public.greenhouse for select
to authenticated
using (true);Say which key (publishable, secret or none) belongs in each place: (a) the bird-cage sketch; (b) the Flutter app; (c) a nightly server script that deletes readings older than a year; (d) a public GitHub repository of the project.
بڵێ کام (ئاشکرا، نهێنی یان هیچ) لە هەر شوێنێکدا دەبێت: (a) قەفەزی باڵندە؛ (b) ئەپی Flutter؛ (c) بەرنامەیەکی شەوانەی کە خوێندنەوەی کۆنتر لە ساڵێک دەسڕێتەوە؛ (d) کۆگایەکی گشتیی GitHubـی پرۆژەکە.
Show answerوەڵامەکە پیشان بدە
(a) publishable; (b) publishable, with the user signing in; (c) secret, because it runs on your own server and must delete past RLS; (d) none — only placeholders, with real keys kept in an ignored file such as arduino_secrets.h.
(a) ئاشکرا؛ (b) ئاشکرا، لەگەڵ چوونەژوورەوەی بەکارهێنەر؛ (c) نهێنی، چونکە لەسەر خۆت کار دەکات و دەبێت بۆ سڕینەوە تێبپەڕێنێت؛ (d) هیچ — تەنیا جێگرەوە؛ ڕاستەقینە لە فایلێکی پشتگوێخراودا دەمێننەوە، وەک arduino_secrets.h.
What should the board do after each reply? (a) 201; (b) 401 with 42501 in the body; (c) −1; (d) 400 after a firmware update.
بۆردەکە دوای هەر وەڵامێک چی بکات؟ (a) 201؛ (b) 401 لەگەڵ 42501 لە جەستەکەدا؛ (c) −1؛ (d) 400 دوای نوێکردنەوەی .
Show answerوەڵامەکە پیشان بدە
(a) Nothing special: the row is stored. (b) Stop retrying and report it: a grant or a policy is missing, and resending will not help. (c) Keep the reading and retry later: the network or the server could not be reached. (d) The new firmware sends JSON that does not match the table: fix the sketch or the columns.
(a) هیچی تایبەت: ڕیزەکە هەڵگیرا. (b) دووپاتە هەوڵدان ڕابگرە و ڕاپۆرتی بکە: مۆڵەتێک یان سیاسەتێک کەمە و دووپاتە ناردن سوودی نییە. (c) خوێندنەوەکە ڕابگرە و دواتر هەوڵ بدەرەوە: نەگەیشتە یان . (d) نوێیەکە دەنێرێت کە لەگەڵ یەک ناگرێتەوە: یان چاک بکە.
Summaryپوختە
- A cloud back end stores, searches and shows readings; a BaaS such as Supabase gives a Postgres database, an API and sign-in ready-made.
- One reading = one row of a typed table: id, created_at (set by the database), device, temperature (double), cover_open (Boolean).
- PostgREST makes
/rest/v1/<table>: POST inserts (201), GET reads with filters, PATCH updates, DELETE removes. - Since October 2026 a table needs explicit grants; RLS policies then decide row by row.
- The publishable key may live in boards and apps; the secret key bypasses RLS and stays on a server. The old anon/service_role keys are retired by the end of 2026.
- Dashboards and the phone app read the table (GET or Realtime); Firebase Firestore is the NoSQL alternative with Security Rules.
- هەوری خوێندنەوەکان هەڵدەگرێت، دەگەڕێت و پیشانیان دەدات؛ وەک Supabase Postgres، API و چوونەژوورەوە بە ئامادەیی دەدات.
- یەک خوێندنەوە = یەک ڕیز لە جۆرداکراو: id، created_at ( دایدەنێت)، device، temperature (double)، cover_open ().
- PostgREST
/rest/v1/<table>دروست دەکات: POST زیاد دەکات (201)، GET بە فلتەر دەخوێنێتەوە، PATCH دەگۆڕێت، DELETE دەسڕێتەوە. - لە تشرینی یەکەمی 2026ـەوە مۆڵەتی ئاشکرای دەوێت؛ پاشان سیاسەتەکانی ڕیز بە ڕیز بڕیار دەدەن.
- دەتوانێت لە بۆرد و ئەپدا بێت؛ تێدەپەڕێنێت و لەسەر دەمێنێتەوە. کۆنی anon/service_role تا کۆتایی 2026 لادەبرێن.
- و ئەپی مۆبایل دەخوێننەوە (GET یان Realtime)؛ Firebase Firestore جێگرەوەی بە Security Rules.
Next: Keeping IoT devices safe and private, then the course project, a smart bird-cage cover, which puts the sensors, the motor and this cloud table together.
داهاتوو: پاراستنی ئامێرەکانی IoT و تایبەتمەندییان، پاشان پرۆژەی خولەکە، بەرگی هۆشمەندی قەفەزی باڵندە، کە ، بزوێنەرەکە و ئەم هەوریە پێکەوە دەبەستێتەوە.
Lesson complete!وانەکە تەواو بوو!
Well done. Review the key terms, then go on to the next lesson.دەستخۆش. سەیرێکی زاراوە سەرەکییەکان بکەرەوە، پاشان بڕۆ بۆ وانەی داهاتوو.
Key termsزاراوە سەرەکییەکان
Keeping IoT Devices Safe and Privateئامێرەکانی IoT: ئاسایش و تایبەتێتی
Related lecturesوانە پەیوەندیدارەکان
- Mobile Cloud and Backend-as-a-Serviceهەورەژمێرکاریی مۆبایل و BaaS
- Firebase and Cloud FirestoreFirebase و Cloud Firestore
- Getting Data from the Internet: HTTP and JSONوەرگرتنی دراوە لە ئینتەرنێت: HTTP و JSON
- Web APIs and JSONAPIـی وێب و JSON
- Database Security: Threats and Countermeasuresئاسایشی بنکەدراوە: هەڕەشەکان و ڕێوشوێنەکانی بەرگری
- Cloud, Fog and Edge Computingهەورەژمێرکاری، ژمێرکاریی تەم و ژمێرکاریی لێوار
- Keeping IoT Devices Safe and Privateئامێرەکانی IoT: ئاسایش و تایبەتێتی
- Project: a Smart Bird-Cage Coverپرۆژە: بەرگی هۆشمەندی قەفەزی باڵندە
- Actuators: DC Motors, Encoders and Servosجووڵێنەرەکان: ماتۆڕەکانی DC، کۆدکەرەکانی هەنگاوبەهەنگاو و ماتۆڕەکانی سێرڤۆ
References and further readingسەرچاوەکان و خوێندنەوەی زیاتر
- Supabase docs: Database overview (every project is a full Postgres database)
- Supabase docs: REST API (PostgREST, /rest/v1/, Row Level Security)
- Supabase docs: API keys (publishable and secret keys, legacy anon/service_role deprecated by the end of 2026, apikey header)
- Supabase docs: Data API quickstart (grants, RLS, policies)
- Supabase docs: Row Level Security
- Supabase changelog: tables not exposed to the Data API automatically (30 May 2026 new projects, 30 October 2026 existing projects)
- Supabase pricing (Free plan limits, November 2026)
- PostgREST: Tables and views (insert returns 201 Created, filters, order)
- PostgREST: Errors (42501: 401 anonymous, 403 authenticated; other errors 400)
- Firebase docs: Cloud Firestore data model
- Firebase docs: Summarize data with aggregation queries (count, sum, average)
This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.