gukbi

supabase

RLS policy

ChatGPT Explain

humans

-- RLS를 활성화해야 합니다.
ALTER TABLE public.humans ENABLE ROW LEVEL SECURITY;

select

CREATE POLICY policy_humans_select
ON public.humans
FOR SELECT
TO authenticated
USING (true); -- 조건 없이 로그인된 사용자 모두 조회 

insert

CREATE POLICY policy_humans_insert
ON public.humans
FOR INSERT
TO authenticated
WITH CHECK (true); -- 조건 없이 로그인된 사용자 모두 삽입 가능

update

CREATE POLICY policy_humans_update
ON public.humans
FOR UPDATE
TO authenticated
USING (true)
WITH CHECK (true);

delete

CREATE POLICY policy_humans_delete
ON public.humans
FOR DELETE
TO authenticated
USING (true);

users

CREATE POLICY "users_policy_delete"
ON public.users
FOR DELETE
TO authenticated
USING (
EXISTS (
SELECT 1 FROM users WHERE id = auth.uid() AND role = 'supaadmin'
)
);
CREATE POLICY "users_policy_select"
ON public.users
FOR SELECT
TO authenticated
USING (
    id = auth.uid()
);
CREATE POLICY "users_policy_insert"
ON public.users
FOR INSERT
TO authenticated
USING (
EXISTS (
SELECT 1 FROM users WHERE id = auth.uid() AND role = 'supaadmin'
)
);
CREATE POLICY "users_policy_update"
ON public.users
FOR UPDATE
TO authenticated
USING (
EXISTS (
SELECT 1 FROM users WHERE id = auth.uid() AND role = 'supaadmin'
)
);

Storage

const { data:{session} } = supabase.auth.getSession();
const currentUser = session?.user;
if(!currentUser) return;
const { data, error } = await supabase.storage
    .from('fileupload')
    .list(supabase.auth.getSession().data.session?.user?.id ?? '', {
        limit: 100,
        sortBy: { column: 'name', order: 'asc' }
    });

for (const file of data) {
    const publicUrl = `${supabaseUrl}/storage/v1/object/public/fileupload/${currentUser.id}/${file.name}`;
    console.log(publicUrl);
}

Examples

Supabase

Example 1 (CRUD)

Example 2 (GitHub Login, Email Login)

Example 3 (Email Login)

Example 4 (REST API)

Example 5 (Realtime)

Storage

Example 6 (Storage Upload)

Example 7 (Storage Upload, Delete)

Example 8 (Storage Upload with extension, Delete)

Example 9 (Storage Upload with extension, Delete, Download)

Edge Functions

Example 10 (Edge Functions)

// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
console.info('server started');
Deno.serve(async (req)=>{
  // CORS preflight 요청 처리
  if (req.method === 'OPTIONS') {
    return new Response(null, {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'POST, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, Authorization',
        'Access-Control-Max-Age': '86400'
      }
    });
  }
  // 실제 POST 요청 처리
  const { name } = await req.json();
  const data = {
    message: `Hello ${name}!`
  };
  return new Response(JSON.stringify(data), {
    headers: {
      'Content-Type': 'application/json',
      'Connection': 'keep-alive',
      'Access-Control-Allow-Origin': '*'
    }
  });
});

Example 11 (Edge Functions, CRUD)

import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from 'jsr:@supabase/supabase-js@2';
// ✅ 환경 변수에서 Supabase 설정 가져오기
const supabaseUrl = Deno.env.get('SUPABASE_URL') ?? '';
const supabaseAnonKey = Deno.env.get('SUPABASE_ANON_KEY') ?? '';
const TABLE_NAME = 'humans';
// 공통 CORS 헤더
const corsHeaders = {
  'Content-Type': 'application/json',
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization'
};
Deno.serve(async (req)=>{
  try {
    if (req.method === 'OPTIONS') {
      return new Response(null, {
        headers: corsHeaders,
        status: 204
      });
    }
    // ✅ 클라이언트에서 보낸 Authorization 헤더 받기
    const authHeader = req.headers.get('Authorization');
    // ✅ Supabase 클라이언트 생성 시 Authorization 헤더 설정
    const supabase = createClient(supabaseUrl, supabaseAnonKey, {
      global: {
        headers: {
          Authorization: authHeader
        }
      }
    });
    const { method } = req;
    if (method === 'GET') {
      const { data, error } = await supabase.from(TABLE_NAME).select('*');
      if (error) throw error;
      return new Response(JSON.stringify({
        data
      }), {
        headers: corsHeaders,
        status: 200
      });
    }
    if (method === 'POST') {
      const { name, age } = await req.json();
      const { data, error } = await supabase.from(TABLE_NAME).insert([
        {
          name,
          age
        }
      ]);
      if (error) throw error;
      return new Response(JSON.stringify({
        message: '데이터가 추가되었습니다.',
        data
      }), {
        headers: corsHeaders,
        status: 201
      });
    }
    if (method === 'PUT') {
      const { id, name, age } = await req.json();
      const { data, error } = await supabase.from(TABLE_NAME).update({
        name,
        age
      }).eq('id', id);
      if (error) throw error;
      return new Response(JSON.stringify({
        message: '데이터가 업데이트되었습니다.',
        data
      }), {
        headers: corsHeaders,
        status: 200
      });
    }
    if (method === 'DELETE') {
      const { id } = await req.json();
      const { data, error } = await supabase.from(TABLE_NAME).delete().eq('id', id);
      if (error) throw error;
      return new Response(JSON.stringify({
        message: '데이터가 삭제되었습니다.',
        data
      }), {
        headers: corsHeaders,
        status: 200
      });
    }
    return new Response(JSON.stringify({
      message: '지원하지 않는 메소드입니다.'
    }), {
      headers: corsHeaders,
      status: 405
    });
  } catch (err) {
    return new Response(JSON.stringify({
      message: err?.message ?? String(err)
    }), {
      headers: corsHeaders,
      status: 500
    });
  }
});

Example 12 (Edge Functions, Resend(Email))

Free Korean Domain(내도메인.한국)
// File: send-email.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
const RESEND_API_KEY = Deno.env.get('RESEND_API_KEY') || 'RESEND_API_KEY'; // 환경변수에서 API 키 가져오기
serve(async (req)=>{
  // CORS 설정
  const corsHeaders = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'POST, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization'
  };
  // Preflight 요청 처리
  if (req.method === 'OPTIONS') {
    return new Response(null, {
      headers: corsHeaders,
      status: 204
    });
  }
  // 인증 헤더 확인
  const authHeader = req.headers.get('Authorization');
  if (!authHeader) {
    return new Response(JSON.stringify({
      code: 401,
      message: 'Missing authorization header'
    }), {
      status: 401,
      headers: {
        ...corsHeaders,
        'Content-Type': 'application/json'
      }
    });
  }
  // 요청 처리
  try {
    const { to, subject, html } = await req.json();
    // 입력 검증
    if (!to || !subject || !html) {
      return new Response(JSON.stringify({
        code: 400,
        message: 'All fields are required'
      }), {
        status: 400,
        headers: {
          ...corsHeaders,
          'Content-Type': 'application/json'
        }
      });
    }
    // Resend API로 이메일 전송
    const resendResponse = await fetch('https://api.resend.com/emails', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${RESEND_API_KEY}`
      },
      body: JSON.stringify({
        from: 'onboarding@resend.dev',
        to,
        subject,
        html
      })
    });
    const data = await resendResponse.json();
    if (!resendResponse.ok) {
      throw new Error(data.message || 'Failed to send email');
    }
    return new Response(JSON.stringify(data), {
      status: 200,
      headers: {
        ...corsHeaders,
        'Content-Type': 'application/json'
      }
    });
  } catch (error) {
    return new Response(JSON.stringify({
      code: 500,
      message: error.message || 'Internal server error'
    }), {
      status: 500,
      headers: {
        ...corsHeaders,
        'Content-Type': 'application/json'
      }
    });
  }
});

Example 13 (Edge Functions, Gmail (SMTP))

import { SMTPClient } from “https://deno.land/x/denomailer/mod.ts”;

// Setup SMTP client with Gmail configuration const client = new SMTPClient({ connection: { hostname: “smtp.gmail.com”, port: 465, tls: true, auth: { username: Deno.env.get(“SMTP_USERNAME”)!, password: Deno.env.get(“SMTP_PASSWORD”)!, }, }, });

const corsHeaders = { “Access-Control-Allow-Origin”: “*”, “Access-Control-Allow-Methods”: “POST, OPTIONS”, “Access-Control-Allow-Headers”: “Content-Type, Authorization”, };

// Start the HTTP server Deno.serve(async (req) => { if (req.method === “OPTIONS”) { return new Response(null, { status: 204, headers: corsHeaders, }); }

try { const { to, subject, html } = await req.json();

if (!to || !subject || !html) {
  return new Response(
    JSON.stringify({ message: "Missing to, subject, or html content." }),
    {
      status: 400,
      headers: { ...corsHeaders, "Content-Type": "application/json" },
    },
  );
}

await client.send({
  from: Deno.env.get("SMTP_USERNAME")!,
  to: Array.isArray(to) ? to : [to],
  subject,
  content: html,
  html, // optional: if you want HTML format
});

return new Response(JSON.stringify({ message: "Email sent" }), {
  status: 200,
  headers: { ...corsHeaders, "Content-Type": "application/json" },
});   } catch (error) {
console.error("Email send error:", error);
return new Response(
  JSON.stringify({ message: error.message || "Internal error" }),
  {
    status: 500,
    headers: { ...corsHeaders, "Content-Type": "application/json" },
  },
);   } }); ```