React Dev

React Dev

1670403203

Build an Image Gallery with Supabase Storage and React

In this video we show how to create a React application with Supabase that allows users to store images in their own image galleries. Users will log in with Supabase Magic Link authorization, and then we handle image upload with PostgreSQL and Supabase Storage. This project is great for any React developers interested in large file storage, and creating a system like this video is a great step forward when creating apps that can scale with massive traffic. You can apply the concepts in this video to creating social media feeds, creating sharable MP3s, sharing 3D objects across users, and many other use cases involving larger files! End of story: Supabase bucket storage is epic.

0:00 Intro
1:40 Initialize Supabase app
2:38 Setup React app + Supabase initialization 
4:50 Creating Magic Link login page
11:57 Creating Image Gallery page
13:45 Setting up Supabase Storage
15:59 Image upload to gallery
21:15 Get images from Supabase Storage
24:30 Explaining CDN links
27:15 Showing images to user
30:37 Delete user image
32:49 Functionality test, thanks for watching!

Full Code: https://github.com/coopercodes/SupabaseImageGallery 

Subscribe: https://www.youtube.com/@CooperCodes/featured 

#react #supabase 

Build an Image Gallery with Supabase Storage and React

The Vue 3 Bootcamp - The Complete Developer Guide

Learn to built frontend Vue 3 applications using Pinia, TypeScript, Supabase and the Composition API 

Udemy Course (with discount): https://bit.ly/3DxBO1S 

Timeline:
0:00 - Project 1
1:38:58 - Project 2
2:48:51 - Project 3

In this course we will take you from a Vue 3 novice to a job ready engineer. This course is loaded with practical projects and examples so that you can truly understand and utilize Vue 3 and the composition API in great depth.

We will be building five projects, each one getting more and more complex. We will end this course by building an Instagram clone with features like file upload and user authentication. By the end of this course, you should have multiple practical example to show off your knowledge!

 

Here are a list of thing you will learn in this course:

  • The difference between pure Vanilla JS and Vue3
  • How to utilize the composition API - this course is 100% composition API, no options API
  • Utilizing all the important Vue directives for things like conditional rendering or rendering of a list
  • Fetching data from an external API and handling the success, loading and error states
  • Handling user authentication
  • Building a Postgres database to handle complex relations
  • Utilizing TypeScript for bug free code
  • All the important ways to handle state (pinia, composables, inject/eject, ref, reactive)
  • Animating and transitioning a UI application
  • Storing and retrieving images from a bucket
  • Scroll based pagination with the Observer Intersection API

I really hope you enjoy this course and learn a ton from it!

Who this course is for:

  • Beginner Web Developers that want to dive in to Vue3

#vue #vuejs #pinia #typescript #supabase

The Vue 3 Bootcamp - The Complete Developer Guide
Hoang  Ha

Hoang Ha

1660881900

Xây Dựng ứng Dụng Ngăn Xếp Đầy Đủ Với Next.js Và Supabase

Khi nói đến việc xây dựng và lựa chọn các khuôn khổ cho ứng dụng full-stack tiếp theo của bạn, kết hợp Next.js với Supabase là một trong những lựa chọn tốt nhất để làm việc theo ý kiến ​​của tôi.

Supabase là một giải pháp thay thế Firebase mã nguồn mở với rất nhiều công cụ mạnh mẽ, bao gồm xác thực liền mạch . Là một nhà phát triển, đây là chìa khóa để xây dựng một ứng dụng full-stack thành công.

Cùng với xác thực, Supabase đi kèm với các tính năng khác, chẳng hạn như cơ sở dữ liệu Postgres, đăng ký thời gian thực và lưu trữ đối tượng. Tôi tin rằng Supabase là một trong những dịch vụ phụ trợ dễ dàng nhất để bắt đầu hoặc tích hợp.

Trong bài viết này, chúng ta sẽ tìm hiểu cách xây dựng một ứng dụng đầy đủ bằng cách sử dụng Next.js và Supabase . Chúng ta sẽ nói về cách thiết lập dự án Supabase, định cấu hình giao diện người dùng, triển khai xác thực và các chức năng.

Khái niệm của ứng dụng này là để người dùng theo dõi và tạo các hoạt động tập luyện dựa trên các thông số được chỉ định, chỉnh sửa các hoạt động này nếu có bất kỳ sai sót hoặc thay đổi cần thiết nào và xóa chúng nếu cần. Bắt đầu nào!

Giới thiệu về Next.js và Supabase

Next.js là một trong những cách dễ nhất và phổ biến nhất để xây dựng các ứng dụng React sẵn sàng sản xuất. Trong những năm gần đây, Next.js đã có sự tăng trưởng đáng kể theo cấp số nhân và nhiều công ty đã áp dụng nó để xây dựng các ứng dụng của họ.

Tại sao chúng ta nên sử dụng Supabase?

Supabase là một giải pháp thay thế mã nguồn mở, không máy chủ cho Firebase được xây dựng trên cơ sở dữ liệu PostgreSQL. Nó cung cấp tất cả các dịch vụ phụ trợ cần thiết để tạo một ứng dụng full-stack.

Với tư cách là người dùng, bạn có thể quản lý cơ sở dữ liệu của mình từ giao diện Supabase, từ việc tạo bảng và mối quan hệ để viết các truy vấn SQL và công cụ thời gian thực trên PostgreSQL.

Supabase đi kèm với các tính năng thực sự thú vị giúp việc phát triển ứng dụng toàn ngăn xếp của bạn trở nên dễ dàng hơn. Một số tính năng này là:

  • Bảo mật mức hàng (RLS) - Supabase đi kèm với tính năng PostgreSQL RLS cho phép bạn hạn chế các hàng trong bảng cơ sở dữ liệu của mình. Khi bạn tạo các chính sách, bạn tạo chúng trực tiếp bằng SQL
  • Cơ sở dữ liệu thời gian thực - Supabase có tính năng cập nhật trên cơ sở dữ liệu PostgreSQL có thể được sử dụng để lắng nghe các thay đổi trong thời gian thực
  • Supabase UI - Supabase có thư viện thành phần giao diện người dùng mã nguồn mở để tạo ứng dụng nhanh chóng và hiệu quả
  • Xác thực người dùng - Supabase tạo một auth.usersbảng ngay sau khi bạn tạo cơ sở dữ liệu của mình. Khi bạn tạo một ứng dụng, Supabase cũng sẽ chỉ định người dùng và ID ngay khi bạn đăng ký trên ứng dụng có thể được tham chiếu trong cơ sở dữ liệu. Đối với các phương pháp đăng nhập, có nhiều cách khác nhau để bạn có thể xác thực người dùng như email, mật khẩu, liên kết ma thuật, Google, GitHub, v.v.
  • Các hàm Edge - Các hàm Edge là các hàm TypeScript được phân phối toàn cầu ở rìa, gần với người dùng. Chúng có thể được sử dụng để thực hiện các chức năng như tích hợp với bên thứ ba hoặc lắng nghe WebHooks

Bắt đầu dự án của chúng tôi với Next.js

Để bắt đầu dự án của chúng tôi trong terminal với mẫu Next.js, chúng tôi sẽ chạy lệnh sau:

npx create-next-app nextjs-supabase

nextjs-supabaselà tên thư mục của ứng dụng của chúng tôi, nơi chúng tôi sẽ bao gồm mẫu ứng dụng Next.js.

Chúng tôi sẽ cần cài đặt gói ứng dụng Supabase để kết nối với ứng dụng Next.js của chúng tôi sau này. Chúng ta có thể làm như vậy bằng cách chạy một trong các lệnh sau:

yarn add @supabase/supabase-js

hoặc

npm i @supabase/supabase-js

Khi ứng dụng đã hoàn tất thiết lập, hãy mở thư mục trong trình chỉnh sửa mã yêu thích của bạn. Bây giờ, chúng tôi có thể xóa mẫu cơ bản trong /pages/index.jstệp của mình và thay thế bằng h1tiêu đề “Chào mừng bạn đến với Ứng dụng Workout”.

Sau khi hoàn tất, hãy chạy lệnh yarn devtrong terminal để khởi động ứng dụng của bạn tại http: // localhost: 3000 . Bạn sẽ thấy một trang như thế này:

Màn hình chào mừng

Thiết lập dự án Supabase và tạo bảng cơ sở dữ liệu

Để thiết lập dự án Supabase, hãy truy cập app.supabase.com để đăng nhập vào trang tổng quan ứng dụng bằng tài khoản GitHub của bạn.

Bảng điều khiển siêu dữ liệu

Sau khi đăng nhập, bạn có thể tạo tổ chức của mình và thiết lập một dự án mới trong tổ chức đó bằng cách nhấp vào Tất cả dự án .

Tất cả các dự án và màn hình tổ chức

Nhấp vào Dự án mới và đặt tên và mật khẩu cơ sở dữ liệu cho dự án của bạn. Nhấp vào nút Tạo dự án mới ; sẽ mất vài phút để dự án của bạn bắt đầu và chạy.

Tạo một dự án mới

Khi dự án đã được tạo, bạn sẽ thấy một bảng điều khiển như sau:

Workout Next Supabase Dashboard

Đối với hướng dẫn này, tôi đã tạo một dự án có tênworkout-next-supabase.

Bây giờ, hãy tạo bảng cơ sở dữ liệu của chúng ta bằng cách nhấp vào biểu tượng SQL Editor trên trang tổng quan của chúng tôi và nhấp vào Truy vấn mới . Nhập truy vấn SQL bên dưới vào trình soạn thảo và nhấp vào RUN để thực hiện truy vấn.

CREATE TABLE workouts (
 id bigint generated by default as identity primary key,
 user_id uuid references auth.users not null,
 user_email text,
 title text,
 loads text,
 reps text,
 inserted_at timestamp with time zone default timezone('utc'::text, now()) not null
);

alter table workouts enable row level security;

create policy "Individuals can create workouts." on workouts for
   insert with check (auth.uid() = user_id);

create policy "Individuals can update their own workouts." on workouts for
   update using (auth.uid() = user_id);

create policy "Individuals can delete their own workouts." on workouts for
   delete using (auth.uid() = user_id);

create policy "Workouts are public." on workouts for
   select using (true);

Thực thi truy vấn của bạn

Thao tác này sẽ tạo bảng tập luyện mà chúng tôi sẽ sử dụng để xây dựng ứng dụng CRUD của mình.

Cùng với việc tạo bảng, các quyền ở cấp độ hàng sẽ được bật để đảm bảo rằng chỉ những người dùng được ủy quyền mới có thể tạo, cập nhật hoặc xóa các chi tiết về bài tập của họ.

Để xem bảng tập luyện trông như thế nào, chúng ta có thể nhấp vào biểu tượng Trình chỉnh sửa bảng trên trang tổng quan để xem bảng tập luyện mà chúng ta vừa tạo.

Đối với ứng dụng này, chúng ta sẽ có bảy cột:

  • user_id
  • user_email
  • id
  • title
  • loads
  • reps
  • Date stamp

Trình chỉnh sửa bảng

Khi bảng và cột của chúng ta đã được thiết lập, bước tiếp theo là kết nối cơ sở dữ liệu Supabase với ứng dụng giao diện người dùng Next.js của chúng ta!

Kết nối Next.js với cơ sở dữ liệu Supabase

Để kết nối Supabase với ứng dụng Next.js của chúng tôi, chúng tôi sẽ cần URL dự ánAnon Key . Cả hai điều này có thể được tìm thấy trên bảng điều khiển cơ sở dữ liệu của chúng tôi. Để lấy hai phím này, hãy nhấp vào biểu tượng bánh răng để chuyển đến Cài đặt và sau đó nhấp vào API . Bạn sẽ thấy hai phím này hiển thị như sau:

Thiết lập url Api

Tất nhiên, chúng tôi không muốn để lộ những giá trị này một cách công khai trên trình duyệt hoặc kho lưu trữ của chúng tôi vì đó là thông tin nhạy cảm. Với lợi thế của chúng tôi, Next.js cung cấp hỗ trợ sẵn có cho các biến môi trường cho phép chúng tôi tạo một .env.localtệp trong thư mục gốc của dự án của chúng tôi. Điều này sẽ tải các biến môi trường của chúng tôi và hiển thị chúng với trình duyệt bằng cách thêm tiền tố vào NEXT_PUBLIC.

Bây giờ, hãy tạo một .env.localtệp trong thư mục gốc của dự án và bao gồm URL và khóa của chúng tôi trong tệp.

.env.local NEXT_PUBLIC_SUPABASE_URL = // dán url dự án của bạn vào đây NEXT_PUBLIC_SUPABASE_ANON_KEY = // dán khóa anon siêu dữ liệu của bạn vào đây

NB, Đừng quên đưa .env.localvào gitignoretệp của bạn để ngăn nó bị đẩy lên repo GitHub (và có sẵn cho mọi người xem) khi triển khai.

Bây giờ chúng ta hãy tạo tệp khách hàng Supabase của chúng ta bằng cách tạo một tệp được gọi supabase.jsở gốc của dự án của chúng ta. Bên trong supabase.jstệp, chúng tôi sẽ viết mã sau:

// supabase.js
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseKey);

Ở đây, chúng tôi đang nhập một createClienthàm từ Supabase và tạo một biến được gọi là supabase. Chúng tôi gọi createClienthàm và sau đó chuyển vào các tham số của chúng tôi: URL ( supabaseUrl) và Anon Key ( supabaseKey).

Bây giờ, chúng ta có thể gọi và sử dụng ứng dụng khách Supabase ở bất kỳ đâu trong dự án của mình!

Định cấu hình giao diện người dùng của ứng dụng của chúng tôi

Trước tiên, chúng ta cần định cấu hình ứng dụng của mình theo cách chúng ta muốn. Chúng tôi sẽ có một thanh điều hướng với tên dự án và các tùy chọn Đăng nhậpĐăng ký khi ứng dụng được tải lần đầu tiên. Khi người dùng đăng ký và đăng nhập, chúng tôi sẽ hiển thị thanh điều hướng để có các nút Home , LogoutCreate Workout .

Cũng sẽ có một chân trang trên mỗi trang trên trang web.

Để làm điều này, chúng tôi sẽ tạo một componentthư mục chứa Navbar.jsFooter.jscác tệp. Sau đó, bên trong _app.js, chúng tôi sẽ bọc pagesthành phần của mình bằng Navbarvà các Footerthành phần để chúng được hiển thị trên mọi trang của ứng dụng.

// _app.js
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
 return (
   <div>
     <Navbar/>
     <Component {...pageProps}/>
     <Footer />
   </div>
 );
}

export default MyApp;

Tôi đã tạo ý chính GitHub ở đây để xem hai thành phần này trông như thế nào cùng với các kiểu tôi đã sử dụng.

Bây giờ, trang chủ của chúng tôi sẽ trông như thế này:

Màn hình chính Adrenargy

Triển khai xác thực người dùng

Để triển khai xác thực người dùng, chúng tôi sẽ khởi tạo trạng thái người dùng trong _app.jstệp của mình và tạo một validateUserhàm để kiểm tra và xác thực người dùng. Sau đó, chúng tôi sẽ đặt trạng thái người dùng thành đối tượng phiên được trả về.

// _app.js

import { useState, useEffect } from "react";
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";
import { supabase } from "../utils/supabase";

function MyApp({ Component, pageProps }) {
 const [session, setSession] = useState(null);

 useEffect(() => {
   setSession(supabase.auth.session());
   supabase.auth.onAuthStateChange((_event, session) => {
     setSession(session);
   });
 }, []);
 return (
   <div>
     <Navbar session={session} />
     <Component {...pageProps} session={session} />
     <Footer />
   </div>
 );
}
export default MyApp;

Khi người dùng tải trang chủ của ứng dụng của chúng tôi, chúng tôi muốn hiển thị một nút để yêu cầu họ đăng nhập hoặc đăng ký. Khi nhấp vào nút Đăng nhập , nút này sẽ chuyển hướng người dùng đến một trang nơi người dùng có thể nhập email và mật khẩu của họ. Nếu họ là người dùng hiện tại và chi tiết đăng nhập hợp lệ, họ sẽ được chuyển hướng đến trang chủ.

Nếu người dùng có thông tin đăng nhập không hợp lệ, một thông báo cảnh báo sẽ hiển thị để cho người dùng biết về sự cố. Thay vào đó, họ sẽ được hiển thị một tùy chọn đăng ký.

Khi người dùng đăng ký, một email xác nhận sẽ được gửi đến email họ đã nhập. họ sẽ cần xác nhận email của mình bằng cách nhấp vào liên kết trong nội dung email.

Xác nhận Email Đăng ký

Bây giờ, khi chúng tôi nhấp vào nút Đăng nhập , chúng tôi sẽ được chuyển hướng đến trang người dùng đến trang này:

Trang đăng nhập

Bây giờ, chúng ta có thể nhấp vào nút Đăng ký và nhập email.

Trang đăng ký

Khi chúng tôi nhấp vào đây, một email sẽ được gửi để xác nhận địa chỉ email. Sau khi xác nhận, nó sẽ đăng nhập cho chúng tôi và chúng tôi sẽ thấy một trang như thế này:

Màn hình chào mừng mà không cần tập luyện

Lưu ý rằng nếu chúng tôi chưa đăng nhập, chúng tôi không thể xem bảng điều khiển hoạt động của mình, thấy nút để tạo bài tập mới hoặc đăng xuất. Đây là xác thực được đề cập ban đầu được cung cấp cho chúng tôi bởi Supabase!

Thực hiện các chức năng tập luyện

Bây giờ, chúng ta sẽ đi sâu vào việc tạo khả năng tạo, sửa đổi và xóa các bài tập của người dùng.

Tìm nạp tất cả các bài tập

Chúng tôi sẽ cần tìm nạp tất cả các bài tập mà chúng tôi sẽ tạo và hiển thị chúng trên trang chủ. Chúng tôi sẽ thực hiện việc này bên trong index.jstệp:

// /pages/index.js
import Head from "next/head";
import Link from "next/link";
import { useEffect, useState } from "react";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import WorkoutCard from "../components/WorkoutCard";

export default function Home({ session }) {
 const [workouts, setWorkouts] = useState([]);
 const [loading, setLoading] = useState(true);

 useEffect(() => {
   fetchWorkouts();
 }, []);

 const fetchWorkouts = async () => {
   const user = supabase.auth.user();
   try {
     setLoading(true);
     const { data, error } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id);

     if (error) throw error;
     setWorkouts(data);
   } catch (error) {
     alert(error.message);
   } finally {
     setLoading(false);
   }
 };

 if (loading) {
   return <div className={styles.loading}>Fetching Workouts...</div>;
 }
 return (
   <div className={styles.container}>
     <Head>
       <title>Nextjs x Supabase</title>
       <meta name="description" content="Generated by create next app" />
       <link rel="icon" href="/favicon.ico" />
     </Head>

     <div className={styles.home}>
       {!session?.user ? (
         <div>
           <p>
             Welcome to Adrenargy. Kindly log in to your account or sign in for
             a demo
           </p>
         </div>
       ) : (
         <div>
           <p className={styles.workoutHeading}>
             Hello <span className={styles.email}>{session.user.email}</span>,
             Welcome to your dashboard
           </p>
           {workouts?.length === 0 ? (
             <div className={styles.noWorkout}>
               <p>You have no workouts yet</p>
               <Link href="/create">
                 <button className={styles.button}>
                   {" "}
                   Create a New Workout
                 </button>
               </Link>
             </div>
           ) : (
             <div>
               <p className={styles.workoutHeading}>Here are your workouts</p>
               <WorkoutCard data={workouts}/>
             </div>
           )}
         </div>
       )}
     </div>
   </div>
 );
}

Trong thành phần này, chúng tôi đang hủy cấu sessiontrúc đối tượng mà chúng tôi đã chuyển từ các pageđạo cụ trong _app.jstệp và sử dụng đối tượng đó để xác thực người dùng được ủy quyền. Nếu không có người dùng, trang tổng quan sẽ không được hiển thị. Nếu có người dùng đăng nhập, bảng điều khiển các bài tập sẽ xuất hiện. Và nếu chưa có bài tập nào được tạo, một văn bản cho biết “Bạn chưa có bài tập nào” và nút để tạo một bài tập mới sẽ xuất hiện.

Để hiển thị các bài tập đã tạo của chúng tôi, chúng tôi có hai trạng thái:, workoutsmột mảng trống và một loadingtrạng thái nhận giá trị boolean là true. Chúng tôi đang sử dụng useEffectđể tìm nạp dữ liệu tập luyện từ cơ sở dữ liệu khi trang được tải.

Hàm fetchWorkoutsđược sử dụng để gọi cá thể Supabase để trả về tất cả dữ liệu từ các bảng tập luyện trong cơ sở dữ liệu của chúng tôi bằng selectphương pháp này. Các . eq()phương pháp lọc được sử dụng để lọc ra và chỉ trả về dữ liệu có id người dùng phù hợp với người dùng đăng nhập hiện tại. Sau đó, setWorkoutsđược đặt thành dữ liệu được gửi từ cơ sở dữ liệu và setLoadingđược đặt trở lại sau falsekhi chúng tôi tìm nạp dữ liệu của mình.

Nếu dữ liệu vẫn đang được tìm nạp, trang sẽ hiển thị “Tìm nạp các bài tập…” và nếu yêu cầu được thực hiện đến cơ sở dữ liệu của chúng tôi trả về mảng các bài tập của chúng tôi, chúng tôi muốn ánh xạ qua mảng và hiển thị WorkoutCardthành phần.

Trong WorkoutCardthành phần, chúng tôi đang hiển thị tiêu đề bài tập, tải, số lần tập và ngày giờ nó được tạo. Thời gian được tạo đang được định dạng bằng date-fnsthư viện mà bạn có thể xem tại đây . Chúng ta sẽ xem các thẻ của chúng ta trông như thế nào khi chúng ta bắt đầu tạo chúng trong phần tiếp theo.

// Workoutcard.js

import Link from "next/link";
import styles from "../styles/WorkoutCard.module.css";
import { BsTrash } from "react-icons/bs";
import { FiEdit } from "react-icons/fi";
import { formatDistanceToNow } from "date-fns/";

const WorkoutCard = ({ data }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>
       </div>
     ))}
   </div>
 );
};

export default WorkoutCard;

Tạo một bài tập mới

Bây giờ chúng tôi đã đăng nhập, trang tổng quan của chúng tôi mới và sạch sẽ. Để triển khai khả năng tạo một bài tập mới, chúng tôi sẽ thêm create.jsCreate.module.csscác tệp trong thư mục pagesstylestương ứng, đồng thời triển khai một số logic và kiểu dáng.

// /pages/create.js

import { supabase } from "../utils/supabase";
import { useState } from "react";
import styles from "../styles/Create.module.css";
import { useRouter } from "next/router";

const Create = () => {
 const initialState = {
   title: "",
   loads: "",
   reps: "",
 };

 const router = useRouter();
 const [workoutData, setWorkoutData] = useState(initialState);

 const { title, loads, reps } = workoutData;

 const handleChange = (e) => {
   setWorkoutData({ ...workoutData, [e.target.name]: e.target.value });
 };

 const createWorkout = async () => {
   try {
     const user = supabase.auth.user();

     const { data, error } = await supabase
       .from("workouts")
       .insert([
         {
           title,
           loads,
           reps,
           user_id: user?.id,
         },
       ])
       .single();
     if (error) throw error;
     alert("Workout created successfully");
     setWorkoutData(initialState);
     router.push("/");
   } catch (error) {
     alert(error.message);
   }
 };

 return (
   <>
     <div className={styles.container}>
       <div className={styles.form}>
         <p className={styles.title}>Create a New Workout</p>
         <label className={styles.label}>Title:</label>
         <input
           type="text"
           name="title"
           value={title}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter a title"
         />
         <label className={styles.label}>Load (kg):</label>
         <input
           type="text"
           name="loads"
           value={loads}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter weight load"
         />
         <label className={styles.label}>Reps:</label>
         <input
           type="text"
           name="reps"
           value={reps}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter number of reps"
         />

         <button className={styles.button} onClick={createWorkout}>
           Create Workout
         </button>
       </div>
     </div>
   </>
 );
};

export default Create;

Ở đây, phạm vi giao diện người dùng cơ bản là chúng ta sẽ có một biểu mẫu để tạo một bài tập mới. Biểu mẫu sẽ bao gồm ba trường (tiêu đề, tải và đại diện) như chúng tôi đã chỉ định khi tạo cơ sở dữ liệu của mình.

Một đối tượng trạng thái ban đầu được định nghĩa để xử lý tất cả các trường này đã được chuyển đến workoutsDatatrạng thái. Hàm onChangeđược sử dụng để xử lý các thay đổi của trường đầu vào.

Hàm createWorkoutsử dụng cá thể máy khách Supabase để tạo một bài tập mới bằng cách sử dụng các trường trạng thái ban đầu mà chúng tôi đã xác định và chèn nó vào bảng cơ sở dữ liệu.

Cuối cùng, chúng tôi có một bánh mì nướng thông báo cho chúng tôi biết khi bài tập mới của chúng tôi đã được tạo.

Sau đó, chúng tôi đặt dữ liệu biểu mẫu trở lại trạng thái chuỗi trống ban đầu khi bài tập của chúng tôi đã được tạo. Sau đó, chúng tôi đang sử dụng router.pushphương pháp này để điều hướng người dùng quay lại trang chủ.

Tạo dự án mới

Đã tạo thành công bài tập

Bảng điều khiển với bài tập Dumbell Press

Cập nhật bài tập

Để cập nhật bài tập, chúng tôi sẽ tạo một thư mục có tên edittrong pagesthư mục chứa [id].jstệp của chúng tôi. Chúng tôi sẽ tạo một biểu tượng liên kết chỉnh sửa trên thẻ thành phần tập luyện của chúng tôi liên kết đến trang này. Khi các thẻ được hiển thị trên trang chủ, chúng ta có thể nhấp vào biểu tượng chỉnh sửa này và nó sẽ đưa chúng ta đến trang chỉnh sửa của thẻ cụ thể đó.

Sau đó, chúng tôi sẽ tìm nạp thông tin chi tiết của thẻ tập luyện cần thiết để được cập nhật từ bảng tập luyện của chúng tôi bởi idchủ sở hữu được ủy quyền và của thẻ đó. Sau đó, chúng tôi sẽ tạo một updateWorkoutchức năng để cập nhật chi tiết thẻ tập luyện của chúng tôi:

// /pages/edit/[id].js
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import styles from "../../styles/Edit.module.css";
import { supabase } from "../../utils/supabase";

const Edit = () => {
 const [workout, setWorkout] = useState("");
 const router = useRouter();

 const { id } = router.query;
 useEffect(() => {
   const user = supabase.auth.user();
   const getWorkout = async () => {
     const { data } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id)
       .filter("id", "eq", id)
       .single();
     setWorkout(data);
   };
   getWorkout();
 }, [id]);

 const handleOnChange = (e) => {
   setWorkout({
     ...workout,
     [e.target.name]: e.target.value,
   });
 };

 const { title, loads, reps } = workout;
 const updateWorkout = async () => {
   const user = supabase.auth.user();
   const { data } = await supabase
     .from("workouts")
     .update({
       title,
       loads,
       reps,
     })
     .eq("id", id)
     .eq("user_id", user?.id);

   alert("Workout updated successfully");

   router.push("/");
 };
 return (
   <div className={styles.container}>
     <div className={styles.formContainer}>
       <h1 className={styles.title}>Edit Workout</h1>
       <label className={styles.label}> Title:</label>
       <input
         type="text"
         name="title"
         value={workout.title}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Load (kg):</label>
       <input
         type="text"
         name="loads"
         value={workout.loads}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Reps:</label>
       <input
         type="text"
         name="reps"
         value={workout.reps}
         onChange={handleOnChange}
         className={styles.updateInput}
       />

       <button onClick={updateWorkout} className={styles.updateButton}>
         Update Workout
       </button>
     </div>
   </div>
 );
};

export default Edit;

Đầu tiên, chúng tôi tạo một trạng thái để lưu trữ các chi tiết thẻ tập luyện sẽ được tìm nạp từ bảng của chúng tôi. Sau đó, chúng tôi trích xuất idthẻ của thẻ đó bằng cách sử dụng useRouterhook. Hàm getWorkoutgọi phiên bản máy khách Supabase để lọc idthẻ tập luyện đó và trả về dữ liệu (tiêu đề, số lần tải và số lần thực hiện).

Khi chi tiết thẻ tập luyện đã được trả lại, chúng tôi có thể tạo updateWorkoutchức năng của mình để sửa đổi các chi tiết bằng cách sử dụng .update()chức năng. Sau khi người dùng cập nhật bài tập và nhấp vào nút Cập nhật bài tập , một thông báo cảnh báo sẽ được gửi và người dùng sẽ được chuyển hướng trở lại trang chủ.

Hãy xem nó hoạt động như thế nào.

Nhấp vào biểu tượng chỉnh sửa để chuyển đến trang chỉnh sửa. Chúng tôi sẽ đổi tên tiêu đề từ “Dumbell Press” thành “Arm Curl”:

Chỉnh sửa Workout Dumbell Press

Chỉnh sửa bài tập thành công

Chỉnh sửa bài tập với máy cuộn cánh tay

Xóa bài tập

Để xóa một bài tập trên mỗi thẻ, chúng tôi sẽ tạo handleDeletehàm sẽ lấy idlàm đối số. Chúng tôi sẽ gọi phiên bản Supabase để xóa thẻ tập luyện bằng cách sử dụng

.delete()hàm số. Điều này .eq('id', id)chỉ định idhàng sẽ bị xóa trên bảng.

 const handleDelete = async (id) => {
   try {


     const user = supabase.auth.user();
     const { data, error } = await supabase
       .from("workouts")
       .delete()
       .eq("id", id)
       .eq("user_id", user?.id);
     fetchWorkouts();
     alert("Workout deleted successfully");
   } catch (error) {
     alert(error.message);
   }
 };

eq('user_id', user?.id)Được sử dụng để kiểm tra xem thẻ đang bị xóa có thuộc về người dùng cụ thể đó hay không . Hàm sẽ được chuyển đến WorkoutCardthành phần trong index.jstệp và bị hủy để sử dụng trong chính thành phần đó như sau:

const WorkoutCard = ({ data, handleDelete }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>

         <div className={styles.buttons}>
           <Link href={`/edit/${item.id}`}>
             <a className={styles.edit}>
               <FiEdit />
             </a>
           </Link>
           <button
             onClick={() => handleDelete(item.id)}
             className={styles.delete}
           >
             <BsTrash />
           </button>
         </div>
       </div>
     ))}
   </div>
 );
};

Bánh mì nướng cảnh báo sẽ được hiển thị khi thẻ đã được xóa thành công và người dùng sẽ được chuyển hướng đến trang chủ.

Triển khai cho Vercel

Bây giờ, chúng tôi phải triển khai ứng dụng của mình cho Vercel để bất kỳ ai trên Internet đều có thể sử dụng nó!

Để triển khai Vercel, trước tiên bạn phải đẩy mã của mình vào kho lưu trữ, đăng nhập vào bảng điều khiển Vercel, nhấp vào Tạo dự án mới và nhấp vào kho lưu trữ mà bạn vừa đẩy mã của mình vào.

Nhập các biến môi trường mà chúng tôi đã tạo trước đó cùng với các giá trị của chúng ( NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEY) trong trường Biến môi trường và nhấp vào Triển khai để triển khai ứng dụng của bạn vào phiên bản sản xuất.

Triển khai tới Vercel

Và chúng tôi đã có nó!

Sự kết luận

Cảm ơn bạn đã đọc! Tôi hy vọng hướng dẫn này cung cấp cho bạn kiến ​​thức bắt buộc cần thiết để tạo ứng dụng toàn ngăn xếp bằng Next.js và Supabase.

Bạn có thể tùy chỉnh kiểu cho trường hợp sử dụng của mình, vì hướng dẫn này chủ yếu tập trung vào logic của việc tạo một ứng dụng đầy đủ.

Nguồn: https://blog.logrocket.com/build-full-stack-app-next-js-supabase/

  #nextjs #supabase #fullstack 

Xây Dựng ứng Dụng Ngăn Xếp Đầy Đủ Với Next.js Và Supabase
Léon  Peltier

Léon Peltier

1660881600

Créez Une Application Complète Avec Next.js Et Supabase

Lorsqu'il s'agit de créer et de choisir des frameworks pour votre prochaine application full-stack, combiner Next.js avec Supabase est l'une des meilleures options avec lesquelles travailler à mon avis.

Supabase est une alternative open source à Firebase avec de nombreux outils puissants, y compris une authentification transparente . En tant que développeur, cela est essentiel pour créer une application complète réussie.

Outre l'authentification, Supabase est livré avec d'autres fonctionnalités, telles qu'une base de données Postgres, des abonnements en temps réel et un stockage d'objets. Je pense que Supabase est l'un des backend-as-a-services les plus faciles à démarrer ou à intégrer.

Dans cet article, nous allons apprendre à créer une application full-stack en utilisant Next.js et Supabase . Nous verrons comment configurer un projet Supabase, configurer l'interface utilisateur et implémenter l'authentification et les fonctionnalités.

Le concept de cette application est que les utilisateurs puissent suivre et créer des activités d'entraînement basées sur des paramètres spécifiés, modifier ces activités s'il y a des erreurs ou des modifications nécessaires, et les supprimer si nécessaire. Commençons!

Introduction à Next.js et Supabase

Next.js est l'un des moyens les plus simples et les plus populaires de créer des applications React prêtes pour la production. Ces dernières années, Next.js a connu une croissance exponentielle importante et de nombreuses entreprises l'ont adopté pour construire leurs applications.

Pourquoi devrions-nous utiliser Supabase ?

Supabase est une alternative open source sans serveur à Firebase construite sur la base de données PostgreSQL. Il fournit tous les services backend nécessaires pour créer une application complète.

En tant qu'utilisateur, vous pouvez gérer votre base de données à partir de l'interface Supabase, allant de la création de tables et de relations à l'écriture de vos requêtes SQL et de votre moteur en temps réel sur PostgreSQL.

Supabase est livré avec des fonctionnalités vraiment intéressantes qui facilitent encore plus le développement de votre application full-stack. Certaines de ces fonctionnalités sont :

  • Sécurité au niveau des lignes (RLS) - Supabase est livré avec la fonctionnalité PostgreSQL RLS qui vous permet de restreindre les lignes dans vos tables de base de données. Lorsque vous créez des politiques, vous les créez directement avec SQL
  • Base de données en temps réel - Supabase dispose d'une fonctionnalité de mise à jour sur la base de données PostgreSQL qui peut être utilisée pour écouter les modifications en temps réel
  • Supabase UI - Supabase dispose d'une bibliothèque de composants d'interface utilisateur open source pour créer des applications rapidement et efficacement
  • Authentification des utilisateurs – Supabase crée une auth.userstable dès que vous créez votre base de données. Lorsque vous créez une application, Supabase attribue également un utilisateur et un identifiant dès que vous vous inscrivez sur l'application qui peut être référencée dans la base de données. Pour les méthodes de connexion, il existe différentes manières d'authentifier les utilisateurs, telles que l'e-mail, le mot de passe, les liens magiques, Google, GitHub, etc.
  • Fonctions Edge – Les fonctions Edge sont des fonctions TypeScript distribuées globalement à la périphérie, à proximité des utilisateurs. Ils peuvent être utilisés pour exécuter des fonctions telles que l'intégration avec des tiers ou l'écoute de WebHooks

Initier notre projet avec Next.js

Pour lancer notre projet dans le terminal avec le template Next.js, nous allons exécuter la commande suivante :

npx create-next-app nextjs-supabase

nextjs-supabaseest le nom du dossier de notre application où nous engloberons le modèle d'application Next.js.

Nous devrons installer le package client Supabase pour nous connecter ultérieurement à notre application Next.js. Nous pouvons le faire en exécutant l'une des commandes suivantes :

yarn add @supabase/supabase-js

ou

npm i @supabase/supabase-js

Une fois l'application terminée, ouvrez le dossier dans votre éditeur de code préféré. Maintenant, nous pouvons supprimer le modèle de base de notre /pages/index.jsfichier et le remplacer par un h1titre indiquant "Bienvenue dans l'application d'entraînement".

Une fois cela fait, exécutez la commande yarn devdans le terminal pour démarrer votre application à http://localhost:3000 . Vous devriez voir une page comme celle-ci :

Ecran d'accueil

Mise en place d'un projet Supabase et création d'une table de base de données

Pour configurer un projet Supabase, visitez app.supabase.com pour vous connecter au tableau de bord de l'application à l'aide de votre compte GitHub.

Tableau de bord Supabase

Une fois connecté, vous pouvez créer votre organisation et configurer un nouveau projet au sein de celle-ci en cliquant sur Tous les projets .

Écran Tous les projets et organisation

Cliquez sur Nouveau projet et donnez à votre projet un nom et un mot de passe de base de données. Cliquez sur le bouton Créer un nouveau projet ; il faudra quelques minutes pour que votre projet soit opérationnel.

Créer un nouveau projet

Une fois le projet créé, vous devriez voir un tableau de bord comme celui-ci :

Entraînement suivant Tableau de bord Supabase

Pour ce tutoriel, j'ai déjà créé un projet nomméworkout-next-supabase.

Maintenant, créons notre table de base de données en cliquant sur l' icône de l' éditeur SQL sur notre tableau de bord et en cliquant sur Nouvelle requête . Entrez la requête SQL ci-dessous dans l'éditeur et cliquez sur RUN pour exécuter la requête.

CREATE TABLE workouts (
 id bigint generated by default as identity primary key,
 user_id uuid references auth.users not null,
 user_email text,
 title text,
 loads text,
 reps text,
 inserted_at timestamp with time zone default timezone('utc'::text, now()) not null
);

alter table workouts enable row level security;

create policy "Individuals can create workouts." on workouts for
   insert with check (auth.uid() = user_id);

create policy "Individuals can update their own workouts." on workouts for
   update using (auth.uid() = user_id);

create policy "Individuals can delete their own workouts." on workouts for
   delete using (auth.uid() = user_id);

create policy "Workouts are public." on workouts for
   select using (true);

Exécutez votre requête

Cela créera la table d'entraînement que nous utiliserons pour créer notre application CRUD.

Parallèlement à la création d'un tableau, des autorisations au niveau des lignes seront activées pour garantir que seuls les utilisateurs autorisés peuvent créer, mettre à jour ou supprimer les détails de leurs entraînements.

Pour voir à quoi ressemble le tableau d'entraînement, nous pouvons cliquer sur l' icône de l' éditeur de tableau sur le tableau de bord pour voir le tableau d'entraînement que nous venons de créer.

Pour cette application, nous aurons sept colonnes :

  • user_id
  • user_email
  • id
  • title
  • loads
  • reps
  • Date stamp

Éditeur de tableaux

Une fois notre table et nos colonnes définies, l'étape suivante consiste à connecter notre base de données Supabase à notre application frontale Next.js !

Connecter Next.js avec une base de données Supabase

Pour connecter Supabase à notre application Next.js, nous aurons besoin de notre URL de projet et de notre clé Anon . Ces deux éléments peuvent être trouvés sur notre tableau de bord de base de données. Pour obtenir ces deux clés, cliquez sur l'icône d'engrenage pour aller dans Paramètres puis cliquez sur API . Vous verrez ces deux clés apparaître comme ceci :

Configuration de l'URL de l'API

Bien sûr, nous ne voulons pas exposer ces valeurs publiquement sur le navigateur ou notre référentiel car il s'agit d'informations sensibles. À notre avantage, Next.js fournit un support intégré pour les variables d'environnement qui nous permettent de créer un .env.localfichier à la racine de notre projet. Cela chargera nos variables d'environnement et les exposera au navigateur en le préfixant avec NEXT_PUBLIC.

Maintenant, créons un .env.localfichier à la racine de notre projet et incluons notre URL et nos clés dans le fichier.

.env.local NEXT_PUBLIC_SUPABASE_URL= // collez l'url de votre projet ici NEXT_PUBLIC_SUPABASE_ANON_KEY= // collez votre clé supabase anon ici

NB, n'oubliez pas d'inclure .env.localdans votre gitignorefichier pour éviter qu'il ne soit poussé vers le référentiel GitHub (et disponible pour tout le monde) lors du déploiement.

Créons maintenant notre fichier client Supabase en créant un fichier appelé supabase.jsà la racine de notre projet. Dans le supabase.jsfichier, nous écrirons le code suivant :

// supabase.js
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseKey);

Ici, nous importons une createClientfonction de Supabase et créons une variable appelée supabase. Nous appelons la createClientfonction puis passons nos paramètres : URL ( supabaseUrl) et Anon Key ( supabaseKey).

Maintenant, nous pouvons appeler et utiliser le client Supabase n'importe où dans notre projet !

Configuration de l'interface utilisateur de notre application

Tout d'abord, nous devons configurer notre application pour qu'elle ressemble à ce que nous voulons. Nous aurons une barre de navigation avec le nom du projet et les options de connexion et d' inscription lors du premier chargement de l'application. Lorsqu'un utilisateur s'inscrit et se connecte, nous afficherons la barre de navigation avec les boutons Accueil , Déconnexion et Créer un entraînement .

Il y aura également un pied de page sur chaque page du site Web.

Pour ce faire, nous allons créer un componentdossier qui contiendra les fichiers Navbar.jset . Footer.jsEnsuite, à l'intérieur _app.jsde , nous encapsulerons notre pagescomposant avec les Navbarcomposants et Footerafin qu'ils soient affichés sur chaque page de l'application.

// _app.js
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
 return (
   <div>
     <Navbar/>
     <Component {...pageProps}/>
     <Footer />
   </div>
 );
}

export default MyApp;

J'ai créé un GitHub Gist ici pour voir à quoi ressemblent ces deux composants aux côtés des styles que j'ai utilisés.

Maintenant, notre page d'accueil devrait ressembler à ceci :

Écran d'accueil Adrénagie

Implémentation de l'authentification des utilisateurs

Pour implémenter l'authentification des utilisateurs, nous allons initialiser l'état de l'utilisateur dans notre _app.jsfichier et créer une validateUserfonction pour vérifier et valider un utilisateur. Nous définirons ensuite l'état de l'utilisateur sur l'objet de session renvoyé.

// _app.js

import { useState, useEffect } from "react";
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";
import { supabase } from "../utils/supabase";

function MyApp({ Component, pageProps }) {
 const [session, setSession] = useState(null);

 useEffect(() => {
   setSession(supabase.auth.session());
   supabase.auth.onAuthStateChange((_event, session) => {
     setSession(session);
   });
 }, []);
 return (
   <div>
     <Navbar session={session} />
     <Component {...pageProps} session={session} />
     <Footer />
   </div>
 );
}
export default MyApp;

Lorsqu'un utilisateur charge la page d'accueil de notre application, nous voulons afficher un bouton pour lui dire de se connecter ou de s'inscrire. Lorsque le bouton de connexion est cliqué, il doit rediriger l'utilisateur vers une page où l'utilisateur peut saisir son adresse e-mail et son mot de passe. S'il s'agit d'un utilisateur existant et que les informations de connexion sont valides, il sera redirigé vers la page d'accueil.

Si l'utilisateur a des informations d'identification non valides, un message d'alerte s'affichera pour informer l'utilisateur du problème. Une option d'inscription s'affichera à la place.

Lorsque l'utilisateur s'inscrit, un e-mail de confirmation est envoyé à l'adresse e-mail saisie. ils devront confirmer leur e-mail en cliquant sur le lien dans le corps de l'e-mail.

Confirmer l'e-mail d'inscription

Maintenant, lorsque nous cliquons sur le bouton Connexion , nous devrions être redirigés vers la page utilisateur vers cette page :

Page de connexion

Maintenant, nous pouvons cliquer sur le bouton S'inscrire et entrer un e-mail.

Page d'inscription

Une fois que nous avons cliqué dessus, un e-mail sera envoyé pour confirmer l'adresse e-mail. Après confirmation, il nous connectera et nous devrions voir une page comme celle-ci :

Écran de bienvenue sans entraînement pour le moment

Notez que si nous ne nous sommes pas connectés, nous ne pouvons pas voir notre tableau de bord d'activité, voir un bouton pour créer un nouvel entraînement ou nous déconnecter. C'est l'authentification mentionnée initialement qui nous est fournie par Supabase !

Mise en œuvre des fonctionnalités d'entraînement

Maintenant, nous allons plonger dans la création de la capacité d'un utilisateur à créer, modifier et supprimer ses entraînements.

Récupération de tous les entraînements

Nous devrons récupérer tous les entraînements que nous allons créer et les afficher sur la page d'accueil. Nous allons le faire à l'intérieur du index.jsfichier :

// /pages/index.js
import Head from "next/head";
import Link from "next/link";
import { useEffect, useState } from "react";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import WorkoutCard from "../components/WorkoutCard";

export default function Home({ session }) {
 const [workouts, setWorkouts] = useState([]);
 const [loading, setLoading] = useState(true);

 useEffect(() => {
   fetchWorkouts();
 }, []);

 const fetchWorkouts = async () => {
   const user = supabase.auth.user();
   try {
     setLoading(true);
     const { data, error } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id);

     if (error) throw error;
     setWorkouts(data);
   } catch (error) {
     alert(error.message);
   } finally {
     setLoading(false);
   }
 };

 if (loading) {
   return <div className={styles.loading}>Fetching Workouts...</div>;
 }
 return (
   <div className={styles.container}>
     <Head>
       <title>Nextjs x Supabase</title>
       <meta name="description" content="Generated by create next app" />
       <link rel="icon" href="/favicon.ico" />
     </Head>

     <div className={styles.home}>
       {!session?.user ? (
         <div>
           <p>
             Welcome to Adrenargy. Kindly log in to your account or sign in for
             a demo
           </p>
         </div>
       ) : (
         <div>
           <p className={styles.workoutHeading}>
             Hello <span className={styles.email}>{session.user.email}</span>,
             Welcome to your dashboard
           </p>
           {workouts?.length === 0 ? (
             <div className={styles.noWorkout}>
               <p>You have no workouts yet</p>
               <Link href="/create">
                 <button className={styles.button}>
                   {" "}
                   Create a New Workout
                 </button>
               </Link>
             </div>
           ) : (
             <div>
               <p className={styles.workoutHeading}>Here are your workouts</p>
               <WorkoutCard data={workouts}/>
             </div>
           )}
         </div>
       )}
     </div>
   </div>
 );
}

Dans ce composant, nous détruisons l' sessionobjet que nous avons transmis à partir des pageaccessoires du _app.jsfichier et l'utilisons pour valider les utilisateurs autorisés. S'il n'y a pas d'utilisateurs, le tableau de bord ne s'affichera pas. Si un utilisateur est connecté, le tableau de bord des entraînements apparaîtra. Et s'il n'y a pas d'entraînement créé, un texte disant "Vous n'avez pas encore d'entraînement" et un bouton pour en créer un nouveau apparaîtront.

Pour rendre nos entraînements créés, nous avons deux états : workouts, un tableau vide et un loadingétat qui prend une valeur booléenne de true. Nous utilisons useEffectpour récupérer les données d'entraînement de la base de données lorsque la page est chargée.

La fetchWorkoutsfonction est utilisée pour appeler l'instance Supabase pour renvoyer toutes les données des tables d'entraînement dans notre base de données à l'aide de la selectméthode. La . eq()La méthode filter est utilisée pour filtrer et renvoyer uniquement les données dont l'ID utilisateur correspond à l'utilisateur actuellement connecté. Ensuite, setWorkoutsest défini sur les données envoyées à partir de la base de données et setLoadingest rétabli falseune fois que nous avons récupéré nos données.

Si les données sont toujours en cours de récupération, la page doit afficher "Fetching Workouts…" et si la demande faite à notre base de données renvoie le tableau de nos entraînements, nous voulons mapper le tableau et rendre le WorkoutCardcomposant.

Dans le WorkoutCardcomposant, nous rendons le titre de l'entraînement, la charge, les répétitions, ainsi que la date et l'heure de sa création. L'heure créée est formatée à l'aide de la date-fnsbibliothèque que vous pouvez consulter ici . Nous verrons à quoi ressemblent nos cartes lorsque nous commencerons à les créer dans la section suivante.

// Workoutcard.js

import Link from "next/link";
import styles from "../styles/WorkoutCard.module.css";
import { BsTrash } from "react-icons/bs";
import { FiEdit } from "react-icons/fi";
import { formatDistanceToNow } from "date-fns/";

const WorkoutCard = ({ data }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>
       </div>
     ))}
   </div>
 );
};

export default WorkoutCard;

Création d'un nouvel entraînement

Maintenant que nous sommes connectés, notre tableau de bord est frais et propre. Pour implémenter la possibilité de créer un nouvel entraînement, nous ajouterons des fichiers create.jset dans le dossier et respectivement, et implémenterons une logique et un style.Create.module.csspagesstyles

// /pages/create.js

import { supabase } from "../utils/supabase";
import { useState } from "react";
import styles from "../styles/Create.module.css";
import { useRouter } from "next/router";

const Create = () => {
 const initialState = {
   title: "",
   loads: "",
   reps: "",
 };

 const router = useRouter();
 const [workoutData, setWorkoutData] = useState(initialState);

 const { title, loads, reps } = workoutData;

 const handleChange = (e) => {
   setWorkoutData({ ...workoutData, [e.target.name]: e.target.value });
 };

 const createWorkout = async () => {
   try {
     const user = supabase.auth.user();

     const { data, error } = await supabase
       .from("workouts")
       .insert([
         {
           title,
           loads,
           reps,
           user_id: user?.id,
         },
       ])
       .single();
     if (error) throw error;
     alert("Workout created successfully");
     setWorkoutData(initialState);
     router.push("/");
   } catch (error) {
     alert(error.message);
   }
 };

 return (
   <>
     <div className={styles.container}>
       <div className={styles.form}>
         <p className={styles.title}>Create a New Workout</p>
         <label className={styles.label}>Title:</label>
         <input
           type="text"
           name="title"
           value={title}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter a title"
         />
         <label className={styles.label}>Load (kg):</label>
         <input
           type="text"
           name="loads"
           value={loads}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter weight load"
         />
         <label className={styles.label}>Reps:</label>
         <input
           type="text"
           name="reps"
           value={reps}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter number of reps"
         />

         <button className={styles.button} onClick={createWorkout}>
           Create Workout
         </button>
       </div>
     </div>
   </>
 );
};

export default Create;

Ici, la portée de base de l'interface utilisateur est que nous aurons un formulaire pour créer un nouvel entraînement. Le formulaire sera composé de trois champs (titre, charge et reps) comme nous l'avons spécifié lors de la création de notre base de données.

Un objet d'état initial est défini pour gérer tous ces champs qui ont été passés à l' workoutsDataétat. La onChangefonction est utilisée pour gérer les modifications du champ de saisie.

La createWorkoutfonction utilise l'instance du client Supabase pour créer un nouvel entraînement à l'aide des champs d'état initiaux que nous avons définis et l'insère dans la table de la base de données.

Enfin, nous avons un toast d'alerte qui nous informe lorsque notre nouvel entraînement a été créé.

Ensuite, nous remettons les données du formulaire à l'état initial de la chaîne vide une fois que notre entraînement a été créé. Après cela, nous utilisons la router.pushméthode pour ramener l'utilisateur à la page d'accueil.

Créer un nouveau projet

Entraînement créé avec succès

Tableau de bord avec entraînements Dumbell Press

Mettre à jour un entraînement

Pour mettre à jour un entraînement, nous allons créer un dossier appelé editdans notre pagesdossier qui contiendra notre [id].jsfichier. Nous allons créer une icône de lien de modification sur notre fiche de composant d'entraînement qui renvoie à cette page. Lorsque les cartes sont rendues sur la page d'accueil, nous pouvons cliquer sur cette icône d'édition et cela nous amènera à la page d'édition de cette carte particulière.

Nous allons ensuite récupérer les détails de la carte d'entraînement nécessaire à mettre à jour à partir de notre table d'entraînement par son idpropriétaire et le propriétaire autorisé de la carte. Ensuite, nous allons créer une updateWorkoutfonction pour mettre à jour les détails de notre carte d'entraînement :

// /pages/edit/[id].js
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import styles from "../../styles/Edit.module.css";
import { supabase } from "../../utils/supabase";

const Edit = () => {
 const [workout, setWorkout] = useState("");
 const router = useRouter();

 const { id } = router.query;
 useEffect(() => {
   const user = supabase.auth.user();
   const getWorkout = async () => {
     const { data } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id)
       .filter("id", "eq", id)
       .single();
     setWorkout(data);
   };
   getWorkout();
 }, [id]);

 const handleOnChange = (e) => {
   setWorkout({
     ...workout,
     [e.target.name]: e.target.value,
   });
 };

 const { title, loads, reps } = workout;
 const updateWorkout = async () => {
   const user = supabase.auth.user();
   const { data } = await supabase
     .from("workouts")
     .update({
       title,
       loads,
       reps,
     })
     .eq("id", id)
     .eq("user_id", user?.id);

   alert("Workout updated successfully");

   router.push("/");
 };
 return (
   <div className={styles.container}>
     <div className={styles.formContainer}>
       <h1 className={styles.title}>Edit Workout</h1>
       <label className={styles.label}> Title:</label>
       <input
         type="text"
         name="title"
         value={workout.title}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Load (kg):</label>
       <input
         type="text"
         name="loads"
         value={workout.loads}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Reps:</label>
       <input
         type="text"
         name="reps"
         value={workout.reps}
         onChange={handleOnChange}
         className={styles.updateInput}
       />

       <button onClick={updateWorkout} className={styles.updateButton}>
         Update Workout
       </button>
     </div>
   </div>
 );
};

export default Edit;

Tout d'abord, nous créons un état pour stocker les détails de la carte d'entraînement qui seront extraits de notre table. Ensuite, on extrait le idde cette carte à l'aide du useRoutercrochet. La getWorkoutfonction appelle l'instance du client Supabase pour filtrer la idcarte d'entraînement et renvoie les données (titre, charges et répétitions).

Une fois les détails de la carte d'entraînement renvoyés, nous pouvons créer notre updateWorkoutfonction pour modifier les détails à l'aide de la .update()fonction. Une fois que l'entraînement a été mis à jour par l'utilisateur et que le bouton Mettre à jour l'entraînement est cliqué, un message d'alerte est envoyé et l'utilisateur sera redirigé vers la page d'accueil.

Voyons voir comment ça fonctionne.

Cliquez sur l'icône d'édition pour accéder à la page d'édition. Nous allons renommer le titre de "Dumbell Press" en "Arm Curl":

Modifier la presse d'haltères d'entraînement

Modifier l'entraînement réussi

Modifier l'entraînement avec la boucle des bras

Suppression d'un entraînement

Pour supprimer un entraînement sur chaque carte, nous allons créer la handleDeletefonction qui prendra le iden argument. Nous appellerons l'instance Supabase pour supprimer une carte d'entraînement à l'aide de la

.delete()fonction. Ceci .eq('id', id)spécifie la idligne à supprimer sur la table.

 const handleDelete = async (id) => {
   try {


     const user = supabase.auth.user();
     const { data, error } = await supabase
       .from("workouts")
       .delete()
       .eq("id", id)
       .eq("user_id", user?.id);
     fetchWorkouts();
     alert("Workout deleted successfully");
   } catch (error) {
     alert(error.message);
   }
 };

Le eq('user_id', user?.id)est utilisé pour vérifier si la carte qui est supprimée appartient à cet utilisateur particulier. La fonction sera transmise au WorkoutCardcomposant dans le index.jsfichier et déstructurée pour être utilisée dans le composant lui-même comme suit :

const WorkoutCard = ({ data, handleDelete }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>

         <div className={styles.buttons}>
           <Link href={`/edit/${item.id}`}>
             <a className={styles.edit}>
               <FiEdit />
             </a>
           </Link>
           <button
             onClick={() => handleDelete(item.id)}
             className={styles.delete}
           >
             <BsTrash />
           </button>
         </div>
       </div>
     ))}
   </div>
 );
};

Un toast d'alerte s'affichera une fois la carte supprimée avec succès et l'utilisateur sera redirigé vers la page d'accueil.

Déploiement à Vercel

Maintenant, nous devons déployer notre application sur Vercel pour que n'importe qui sur Internet puisse l'utiliser !

Pour déployer sur Vercel, vous devez d'abord pousser votre code vers votre référentiel, vous connecter à votre tableau de bord Vercel, cliquer sur Créer un nouveau projet et cliquer sur le référentiel dans lequel vous venez de pousser votre code.

Entrez les variables d'environnement que nous avons créées précédemment avec leurs valeurs ( NEXT_PUBLIC_SUPABASE_URLet NEXT_PUBLIC_SUPABASE_ANON_KEY) dans le champ Variable d'environnement et cliquez sur Déployer pour déployer votre application en production.

Déployer à Vercel

Et là, nous l'avons!

Conclusion

Merci pour la lecture! J'espère que ce tutoriel vous donnera les connaissances nécessaires pour créer une application full-stack en utilisant Next.js et Supabase.

Vous pouvez personnaliser le style en fonction de votre cas d'utilisation, car ce didacticiel se concentre principalement sur la logique de création d'une application complète.

Source : https://blog.logrocket.com/build-full-stack-app-next-js-supabase/

  #nextjs #supabase #fullstack 

Créez Une Application Complète Avec Next.js Et Supabase
曾 俊

曾 俊

1660879800

使用 Next.js 和 Supabase 构建一个全栈应用程序

在为您的下一个全栈应用程序构建和选择框架时,在我看来,将 Next.js 与 Supabase 结合使用是最好的选择之一。

Supabase 是一个开源的 Firebase 替代品,具有许多强大的工具,包括无缝身份验证作为开发人员,这是构建成功的全栈应用程序的关键。

除了身份验证,Supabase 还具有其他功能,例如 Postgres 数据库、实时订阅和对象存储。我相信 Supabase 是最容易上手或集成的后端即服务之一。

在本文中,我们将学习如何使用 Next.js 和 Supabase 构建一个全栈应用程序我们将讨论如何设置 Supbase 项目、配置 UI 以及实现身份验证和功能。

该应用程序的概念是让用户根据指定的参数跟踪和创建锻炼活动,如果有任何错误或必要的更改,则编辑这些活动,并在需要时将其删除。让我们开始吧!

Next.js 和 Supabase 简介

Next.js 是构建生产就绪的 React 应用程序的最简单和最流行的方法之一。近年来,Next.js 经历了显着的指数级增长,许多公司都采用它来构建他们的应用程序。

我们为什么要使用 Supabase?

Supabase 是基于 PostgreSQL 数据库构建的Firebase 的无服务器、开源替代品。它提供了创建全栈应用程序所需的所有后端服务。

作为用户,您可以从 Supbase 界面管理您的数据库,范围从创建表和关系到在 PostgreSQL 之上编写 SQL 查询和实时引擎。

Supabase 带有非常酷的功能,使您的全栈应用程序开发更加容易。其中一些功能包括:

  • 行级安全性 (RLS) – Supabase 带有 PostgreSQL RLS 功能,允许您限制数据库表中的行。创建策略时,直接使用 SQL 创建它们
  • 实时数据库——Supabase 在 PostgreSQL 数据库上有一个更新功能,可以用来监听实时变化
  • Supabase UI——Supabase 有一个开源的用户界面组件库,可以快速高效地创建应用程序
  • 用户身份验证 – Supbaseauth.users在您创建数据库后立即创建一个表。当您创建应用程序时,Supabase 也会在您注册应用程序后立即分配一个用户和 ID,该应用程序可以在数据库中引用。对于登录方法,您可以通过多种方式验证用户身份,例如电子邮件、密码、魔术链接、Google、GitHub 等
  • 边缘函数——边缘函数是分布在边缘的 TypeScript 函数,靠近用户。它们可用于执行与第三方集成或监听 WebHooks 等功能

使用 Next.js 启动我们的项目

要使用 Next.js 模板在终端中启动我们的项目,我们将运行以下命令:

npx create-next-app nextjs-supabase

nextjs-supabase是我们应用程序的文件夹名称,我们将在其中包含 Next.js 应用程序模板。

稍后我们需要安装 Supabase 客户端包以连接到我们的 Next.js 应用程序。我们可以通过运行以下任一命令来做到这一点:

yarn add @supabase/supabase-js

或者

npm i @supabase/supabase-js

应用程序完成设置后,在您喜欢的代码编辑器中打开该文件夹。现在,我们可以删除/pages/index.js文件中的基本模板,并将其替换为h1标题为“欢迎使用 Workout App”。

完成后,在终端中运行命令以在http://localhost:3000yarn dev启动您的应用程序。你应该看到这样的页面:

欢迎屏幕

设置 Supabase 项目并创建数据库表

要设置 Supabase 项目,请访问app.supabase.com以使用您的 GitHub 帐户登录应用仪表板。

Supabase 仪表板

登录后,您可以通过单击All Projects创建您的组织并在其中设置一个新项目。

所有项目和组织屏幕

单击新建项目并为您的项目指定名称和数据库密码。单击创建新项目按钮;您的项目需要几分钟才能启动并运行。

创建一个新项目

创建项目后,您应该会看到如下所示的仪表板:

Workout Next Supabase 仪表板

对于本教程,我已经创建了一个名为workout-next-supabase.

现在,让我们通过单击仪表板上的SQL 编辑器图标并单击New Query创建我们的数据库表。在编辑器中输入下面的 SQL 查询,然后单击RUN执行查询。

CREATE TABLE workouts (
 id bigint generated by default as identity primary key,
 user_id uuid references auth.users not null,
 user_email text,
 title text,
 loads text,
 reps text,
 inserted_at timestamp with time zone default timezone('utc'::text, now()) not null
);

alter table workouts enable row level security;

create policy "Individuals can create workouts." on workouts for
   insert with check (auth.uid() = user_id);

create policy "Individuals can update their own workouts." on workouts for
   update using (auth.uid() = user_id);

create policy "Individuals can delete their own workouts." on workouts for
   delete using (auth.uid() = user_id);

create policy "Workouts are public." on workouts for
   select using (true);

执行您的查询

这将创建我们将用于构建 CRUD 应用程序的锻炼表。

除了创建表格外,还将启用行级权限,以确保只有授权用户才能创建、更新或删除其锻炼的详细信息。

要查看锻炼表的外观,我们可以单击仪表板上的表格编辑器图标来查看我们刚刚创建的锻炼表。

对于这个应用程序,我们将有七列:

  • user_id
  • user_email
  • id
  • title
  • loads
  • reps
  • Date stamp

表格编辑器

一旦我们的表和列设置好了,下一步就是将我们的 Supabase 数据库与我们的 Next.js 前端应用程序连接起来!

将 Next.js 与 Supabase 数据库连接

要将 Supabase 与我们的 Next.js 应用程序连接起来,我们将需要我们的项目 URLAnon Key。这两个都可以在我们的数据库仪表板上找到。要获取这两个密钥,请单击齿轮图标转到设置,然后单击API。您会看到这两个键显示如下:

网址 API 设置

当然,我们不想在浏览器或我们的存储库上公开这些值,因为它是敏感信息。.env.local对我们来说,Next.js 提供了对环境变量的内置支持,允许我们在项目的根目录中创建文件。这将加载我们的环境变量,并通过前缀将它们暴露给浏览器NEXT_PUBLIC

.env.local现在,让我们在项目的根目录中创建一个文件,并在文件中包含我们的 URL 和密钥。

.env.local NEXT_PUBLIC_SUPABASE_URL= // 在此处粘贴您的项目 url NEXT_PUBLIC_SUPABASE_ANON_KEY= // 在此处粘贴您的 supbase 匿名密钥

注意,不要忘记包含.env.local在您的gitignore文件中,以防止在部署时将其推送到 GitHub 存储库(并且可供所有人查看)。

supabase.js现在让我们通过在项目的根目录创建一个名为的文件来创建我们的 Supabase 客户端文件。在该supabase.js文件中,我们将编写以下代码:

// supabase.js
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseKey);

在这里,我们createClient从 Supabase 导入一个函数并创建一个名为supabase. 我们调用该createClient函数,然后传入我们的参数:URL ( supabaseUrl) 和 Anon Key ( supabaseKey)。

现在,我们可以在项目的任何地方调用和使用 Supbase 客户端了!

配置我们应用的 UI

首先,我们需要配置我们的应用程序以使其看起来像我们想要的那样。我们将有一个带有项目名称的导航栏,以及首次加载应用程序时的登录注册选项。当用户注册并登录时,我们将显示导航栏,其中包含HomeLogoutCreate Workout按钮。

网站上的每一页也会有一个页脚。

为此,我们将创建一个component文件夹来存放Navbar.jsFooter.js文件。然后,在内部_app.js,我们将pages使用Navbar和 组件包装我们的组件,Footer以便它们显示在应用程序的每个页面上。

// _app.js
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
 return (
   <div>
     <Navbar/>
     <Component {...pageProps}/>
     <Footer />
   </div>
 );
}

export default MyApp;

我在这里创建了一个 GitHub 要点,以查看这两个组件与我使用的样式一起看起来像什么。

现在,我们的主页应该是这样的:

肾上腺素主屏幕

实现用户认证

为了实现用户身份验证,我们将在我们的_app.js文件中初始化用户状态并创建一个validateUser函数来检查和验证用户。然后我们将用户状态设置为返回的会话对象。

// _app.js

import { useState, useEffect } from "react";
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";
import { supabase } from "../utils/supabase";

function MyApp({ Component, pageProps }) {
 const [session, setSession] = useState(null);

 useEffect(() => {
   setSession(supabase.auth.session());
   supabase.auth.onAuthStateChange((_event, session) => {
     setSession(session);
   });
 }, []);
 return (
   <div>
     <Navbar session={session} />
     <Component {...pageProps} session={session} />
     <Footer />
   </div>
 );
}
export default MyApp;

当用户加载我们应用程序的主页时,我们希望显示一个按钮来告诉他们登录或注册。单击登录按钮时,它应该将用户重定向到用户可以输入其电子邮件和密码的页面。如果他们是现有用户并且登录详细信息有效,他们将被重定向到主页。

如果用户的凭据无效,则会显示一条警报消息以告知用户该问题。他们将看到一个注册选项。

当用户注册时,确认电子邮件将发送到他们输入的电子邮件。他们需要通过单击电子邮件正文中的链接来确认他们的电子邮件。

确认注册电子邮件

现在,当我们点击登录按钮时,我们应该被重定向到用户页面到这个页面:

登录页面

现在,我们可以单击“注册”按钮并输入电子邮件。

注册页面

单击此按钮后,将发送一封电子邮件以确认电子邮件地址。确认后,它将让我们登录,我们应该看到如下页面:

尚未进行锻炼的欢迎屏幕

请注意,如果我们尚未登录,我们将无法看到我们的活动仪表板、创建新锻炼的按钮或注销。这是最初提到的由 Supbase 提供给我们的身份验证!

实施锻炼功能

现在,我们将深入探讨创建用户创建、修改和删除锻炼的能力。

获取所有锻炼

我们需要获取我们将要创建的所有锻炼并将它们呈现在主页上。我们将在index.js文件中执行此操作:

// /pages/index.js
import Head from "next/head";
import Link from "next/link";
import { useEffect, useState } from "react";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import WorkoutCard from "../components/WorkoutCard";

export default function Home({ session }) {
 const [workouts, setWorkouts] = useState([]);
 const [loading, setLoading] = useState(true);

 useEffect(() => {
   fetchWorkouts();
 }, []);

 const fetchWorkouts = async () => {
   const user = supabase.auth.user();
   try {
     setLoading(true);
     const { data, error } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id);

     if (error) throw error;
     setWorkouts(data);
   } catch (error) {
     alert(error.message);
   } finally {
     setLoading(false);
   }
 };

 if (loading) {
   return <div className={styles.loading}>Fetching Workouts...</div>;
 }
 return (
   <div className={styles.container}>
     <Head>
       <title>Nextjs x Supabase</title>
       <meta name="description" content="Generated by create next app" />
       <link rel="icon" href="/favicon.ico" />
     </Head>

     <div className={styles.home}>
       {!session?.user ? (
         <div>
           <p>
             Welcome to Adrenargy. Kindly log in to your account or sign in for
             a demo
           </p>
         </div>
       ) : (
         <div>
           <p className={styles.workoutHeading}>
             Hello <span className={styles.email}>{session.user.email}</span>,
             Welcome to your dashboard
           </p>
           {workouts?.length === 0 ? (
             <div className={styles.noWorkout}>
               <p>You have no workouts yet</p>
               <Link href="/create">
                 <button className={styles.button}>
                   {" "}
                   Create a New Workout
                 </button>
               </Link>
             </div>
           ) : (
             <div>
               <p className={styles.workoutHeading}>Here are your workouts</p>
               <WorkoutCard data={workouts}/>
             </div>
           )}
         </div>
       )}
     </div>
   </div>
 );
}

在这个组件中,我们正在解构从文件中的 propssession传递的对象,并使用它来验证授权用户。如果没有用户,则不会显示仪表板。如果有用户登录,则会出现锻炼仪表板。如果没有创建锻炼,则会出现一条“您还没有锻炼”的文字和一个创建新锻炼的按钮。page_app.js

为了渲染我们创建的锻炼,我们有两个状态:workouts一个空数组和一个loading接受布尔值的状态true。我们useEffect用于在页面加载时从数据库中获取锻炼数据。

fetchWorkouts函数用于调用 Supbase 实例以使用该select方法从我们的数据库中的锻炼表中返回所有数据。这 。eq()filter 方法用于过滤掉并仅返回用户 id 与当前登录用户匹配的数据。然后,setWorkouts设置为从数据库发送的数据,并在我们获取数据setLoading后设置回。false

如果仍在获取数据,则页面应显示“Fetching Workouts...”,并且如果向我们的数据库发出的请求返回我们的锻炼数组,我们希望通过该数组进行映射并渲染WorkoutCard组件。

在该WorkoutCard组件中,我们正在渲染锻炼标题、负荷、次数以及它的创建日期和时间。创建的时间正在使用您可以在此处查看date-fns的库进行格式化。我们将在下一节开始创建卡片时查看卡片的外观。

// Workoutcard.js

import Link from "next/link";
import styles from "../styles/WorkoutCard.module.css";
import { BsTrash } from "react-icons/bs";
import { FiEdit } from "react-icons/fi";
import { formatDistanceToNow } from "date-fns/";

const WorkoutCard = ({ data }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>
       </div>
     ))}
   </div>
 );
};

export default WorkoutCard;

创建新的锻炼

现在我们已经登录,我们的仪表板是新鲜和干净的。为了实现创建新锻炼的能力,我们将分别在和文件夹中添加create.js和文件,并实现一些逻辑和样式。Create.module.csspagesstyles

// /pages/create.js

import { supabase } from "../utils/supabase";
import { useState } from "react";
import styles from "../styles/Create.module.css";
import { useRouter } from "next/router";

const Create = () => {
 const initialState = {
   title: "",
   loads: "",
   reps: "",
 };

 const router = useRouter();
 const [workoutData, setWorkoutData] = useState(initialState);

 const { title, loads, reps } = workoutData;

 const handleChange = (e) => {
   setWorkoutData({ ...workoutData, [e.target.name]: e.target.value });
 };

 const createWorkout = async () => {
   try {
     const user = supabase.auth.user();

     const { data, error } = await supabase
       .from("workouts")
       .insert([
         {
           title,
           loads,
           reps,
           user_id: user?.id,
         },
       ])
       .single();
     if (error) throw error;
     alert("Workout created successfully");
     setWorkoutData(initialState);
     router.push("/");
   } catch (error) {
     alert(error.message);
   }
 };

 return (
   <>
     <div className={styles.container}>
       <div className={styles.form}>
         <p className={styles.title}>Create a New Workout</p>
         <label className={styles.label}>Title:</label>
         <input
           type="text"
           name="title"
           value={title}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter a title"
         />
         <label className={styles.label}>Load (kg):</label>
         <input
           type="text"
           name="loads"
           value={loads}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter weight load"
         />
         <label className={styles.label}>Reps:</label>
         <input
           type="text"
           name="reps"
           value={reps}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter number of reps"
         />

         <button className={styles.button} onClick={createWorkout}>
           Create Workout
         </button>
       </div>
     </div>
   </>
 );
};

export default Create;

在这里,基本的 UI 范围是我们将有一个表单来创建一个新的锻炼。该表单将包含我们在创建数据库时指定的三个字段(标题、负载和代表)。

定义了一个初始状态对象来处理所有这些传递给workoutsData状态的字段。该onChange函数用于处理输入字段的更改。

createWorkout函数使用 Supbase 客户端实例使用我们定义的初始状态字段创建新的锻炼并将其插入到数据库表中。

最后,当我们创建了新的锻炼时,我们有一个警报 toast 通知我们。

然后,一旦我们的锻炼被创建,我们将表单数据设置回初始的空字符串状态。之后,我们使用该router.push方法将用户导航回主页。

创建新项目

锻炼成功创建

带有锻炼哑铃推举的仪表板

更新锻炼

要更新锻炼,我们将edit在我们的文件夹中创建一个名为的文件夹,该文件pages夹将保存我们的[id].js文件。我们将在链接到此页面的锻炼组件卡上创建一个编辑链接图标。当卡片呈现在主页上时,我们可以单击此编辑图标,它将带我们进入该特定卡片的编辑页面。

然后,我们将获取所需锻炼卡的详细信息,以由其id和卡的授权所有者从我们的锻炼表中更新。然后,我们将创建一个updateWorkout函数来更新我们的锻炼卡详细信息:

// /pages/edit/[id].js
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import styles from "../../styles/Edit.module.css";
import { supabase } from "../../utils/supabase";

const Edit = () => {
 const [workout, setWorkout] = useState("");
 const router = useRouter();

 const { id } = router.query;
 useEffect(() => {
   const user = supabase.auth.user();
   const getWorkout = async () => {
     const { data } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id)
       .filter("id", "eq", id)
       .single();
     setWorkout(data);
   };
   getWorkout();
 }, [id]);

 const handleOnChange = (e) => {
   setWorkout({
     ...workout,
     [e.target.name]: e.target.value,
   });
 };

 const { title, loads, reps } = workout;
 const updateWorkout = async () => {
   const user = supabase.auth.user();
   const { data } = await supabase
     .from("workouts")
     .update({
       title,
       loads,
       reps,
     })
     .eq("id", id)
     .eq("user_id", user?.id);

   alert("Workout updated successfully");

   router.push("/");
 };
 return (
   <div className={styles.container}>
     <div className={styles.formContainer}>
       <h1 className={styles.title}>Edit Workout</h1>
       <label className={styles.label}> Title:</label>
       <input
         type="text"
         name="title"
         value={workout.title}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Load (kg):</label>
       <input
         type="text"
         name="loads"
         value={workout.loads}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Reps:</label>
       <input
         type="text"
         name="reps"
         value={workout.reps}
         onChange={handleOnChange}
         className={styles.updateInput}
       />

       <button onClick={updateWorkout} className={styles.updateButton}>
         Update Workout
       </button>
     </div>
   </div>
 );
};

export default Edit;

首先,我们创建一个状态来存储将从我们的表中获取的锻炼卡详细信息。然后,我们使用钩子提取该id卡的。useRoutergetWorkout函数调用 Supabase 客户端实例来过滤该id锻炼卡并返回数据(标题、负荷和次数)。

返回锻炼卡详细信息后,我们可以创建updateWorkout函数以使用该函数修改详细信息.update()。一旦用户更新了锻炼并单击了更新锻炼按钮,就会发送一条警报消息,并且用户将被重定向回主页。

让我们看看它是如何工作的。

点击编辑图标进入编辑页面。我们将把标题从“Dumbell Press”重命名为“Arm Curl”:

编辑锻炼哑铃推举

编辑锻炼成功

用手臂卷曲编辑锻炼

删除锻炼

要删除每张卡片上的锻炼,我们将创建handleDeleteid作为参数的函数。我们将调用 Supbase 实例以使用

.delete()功能。这.eq('id', id)指定id要在表中删除的行。

 const handleDelete = async (id) => {
   try {


     const user = supabase.auth.user();
     const { data, error } = await supabase
       .from("workouts")
       .delete()
       .eq("id", id)
       .eq("user_id", user?.id);
     fetchWorkouts();
     alert("Workout deleted successfully");
   } catch (error) {
     alert(error.message);
   }
 };

用于检查正在删除的eq('user_id', user?.id)卡是否属于该特定用户。该函数将被传递给文件中的WorkoutCard组件index.js并解构以供组件本身使用,如下所示:

const WorkoutCard = ({ data, handleDelete }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>

         <div className={styles.buttons}>
           <Link href={`/edit/${item.id}`}>
             <a className={styles.edit}>
               <FiEdit />
             </a>
           </Link>
           <button
             onClick={() => handleDelete(item.id)}
             className={styles.delete}
           >
             <BsTrash />
           </button>
         </div>
       </div>
     ))}
   </div>
 );
};

成功删除卡后,将显示警报 Toast,并将用户重定向到主页。

部署到 Vercel

现在,我们必须将我们的应用程序部署到 Vercel,以便 Internet 上的任何人都可以使用它!

要部署到 Vercel,您必须首先将代码推送到存储库,登录到 Vercel 仪表板,单击Create New Project,然后单击刚刚将代码推送到的存储库。

在Environment Variable字段中输入我们之前创建的环境变量及其值 (NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEY),然后单击Deploy以将您的应用程序部署到生产环境。

部署到 Vercel

我们终于得到它了!

结论

感谢您的阅读!我希望本教程为您提供使用 Next.js 和 Supabase 创建全栈应用程序所需的知识。

您可以根据用例自定义样式,因为本教程主要关注创建全栈应用程序的逻辑。

来源:https ://blog.logrocket.com/build-full-stack-app-next-js-supabase/

  #nextjs #supabase #fullstack 

使用 Next.js 和 Supabase 构建一个全栈应用程序

Next.js と Supabase を使用してフルスタック アプリを構築する

次のフルスタック アプリケーションのフレームワークを構築して選択する場合、Next.js と Supabase を組み合わせることは、私の意見では最良の選択肢の 1 つです。

Supabase は、シームレスな認証など、多くの強力なツールを備えたオープン ソースの Firebase の代替手段です。開発者として、これはフルスタック アプリケーションを成功させるための鍵です。

認証に加えて、Supabase には、Postgres データベース、リアルタイム サブスクリプション、オブジェクト ストレージなどの他の機能が付属しています。Supabase は、開始または統合するのに最も簡単なサービスとしてのバックエンドの 1 つだと思います。

この記事では、Next.js と Supabase を使用してフルスタック アプリを構築する方法を学習しますSupabase プロジェクトをセットアップし、UI を構成し、認証と機能を実装する方法について説明します。

このアプリのコンセプトは、ユーザーが指定されたパラメーターに基づいてトレーニング活動を追跡および作成し、間違いや必要な変更があればこれらの活動を編集し、必要に応じて削除することです。始めましょう!

Next.js と Supabase の紹介

Next.js は、本番対応の React アプリケーションを構築するための最も簡単で最も一般的な方法の 1 つです。ここ数年、Next.js は急激な成長を遂げており、多くの企業がそれを採用してアプリケーションを構築しています。

Supabase を使用する理由

Supabase は、PostgreSQL データベース上に構築された、Firebase に代わるサーバーレスのオープンソースの代替手段です。フルスタック アプリケーションの作成に必要なすべてのバックエンド サービスを提供します。

ユーザーは、テーブルやリレーションシップの作成から、PostgreSQL 上の SQL クエリやリアルタイム エンジンの作成に至るまで、Supabase インターフェイスからデータベースを管理できます。

Supabase には、フルスタック アプリケーションの開発をさらに容易にする非常に優れた機能が備わっています。これらの機能の一部は次のとおりです。

  • 行レベル セキュリティ (RLS) – Supabase には、データベース テーブル内の行を制限できる PostgreSQL RLS 機能が付属しています。ポリシーを作成するときは、SQL で直接作成します
  • リアルタイム データベース – Supabase には、リアルタイムの変更をリッスンするために使用できる PostgreSQL データベースの更新機能があります。
  • Supabase UI – Supabase には、アプリケーションを迅速かつ効率的に作成するためのオープンソースのユーザー インターフェイス コンポーネント ライブラリがあります。
  • ユーザー認証 – データベースを作成するとすぐに、Supabase がauth.usersテーブルを作成します。アプリケーションを作成すると、データベース内で参照できるアプリに登録するとすぐに、Supabase によってユーザーと ID も割り当てられます。ログイン方法については、メール、パスワード、マジック リンク、Google、GitHub など、さまざまな方法でユーザーを認証できます。
  • エッジ関数 – エッジ関数は、ユーザーに近いエッジでグローバルに分散される TypeScript 関数です。サード パーティとの統合や WebHook のリッスンなどの機能を実行するために使用できます。

Next.js でプロジェクトを開始する

Next.js テンプレートを使用してターミナルでプロジェクトを開始するには、次のコマンドを実行します。

npx create-next-app nextjs-supabase

nextjs-supabaseは、Next.js アプリ テンプレートを含むアプリのフォルダー名です。

後で Next.js アプリに接続するには、Supabase クライアント パッケージをインストールする必要があります。これを行うには、次のコマンドのいずれかを実行します。

yarn add @supabase/supabase-js

また

npm i @supabase/supabase-js

アプリのセットアップが完了したら、お気に入りのコード エディターでフォルダーを開きます。ここで、ファイル内の基本テンプレートを削除し、「ワークアウト アプリへようこそ」という見出しに/pages/index.js置き換えることができます。h1

それが完了したら、ターミナルでコマンドを実行して、 http://localhost:3000yarn devでアプリを起動します。次のようなページが表示されます。

ようこそ画面

Supabase プロジェクトのセットアップとデータベース テーブルの作成

Supabase プロジェクトを設定するには、app.supabase.com にアクセスて、GitHub アカウントを使用してアプリ ダッシュボードにサインインします。

スーパーベース ダッシュボード

ログインしたら、組織を作成し、[すべてのプロジェクト] をクリックして組織内に新しいプロジェクトを設定できます。

すべてのプロジェクトと組織画面

[新しいプロジェクト]をクリックし、プロジェクトに名前とデータベース パスワードを指定します。[新しいプロジェクトを作成] ボタンをクリックします。プロジェクトが起動して実行されるまで数分かかります。

新しいプロジェクトを作成する

プロジェクトが作成されると、次のようなダッシュボードが表示されます。

Workout Next Supabase ダッシュボード

このチュートリアルでは、という名前のプロジェクトを既に作成しています。workout-next-supabase.

それでは、ダッシュボードのSQL Editorアイコンをクリックし、 New Queryをクリックして、データベース テーブルを作成しましょう。以下の SQL クエリをエディタに入力し、[ RUN ] をクリックしてクエリを実行します。

CREATE TABLE workouts (
 id bigint generated by default as identity primary key,
 user_id uuid references auth.users not null,
 user_email text,
 title text,
 loads text,
 reps text,
 inserted_at timestamp with time zone default timezone('utc'::text, now()) not null
);

alter table workouts enable row level security;

create policy "Individuals can create workouts." on workouts for
   insert with check (auth.uid() = user_id);

create policy "Individuals can update their own workouts." on workouts for
   update using (auth.uid() = user_id);

create policy "Individuals can delete their own workouts." on workouts for
   delete using (auth.uid() = user_id);

create policy "Workouts are public." on workouts for
   select using (true);

クエリを実行する

これにより、CRUD アプリケーションの構築に使用するワークアウト テーブルが作成されます。

テーブルの作成に加えて、行レベルのアクセス許可が有効になり、承認されたユーザーのみがワークアウトの詳細を作成、更新、または削除できるようになります。

ワークアウト テーブルがどのように表示されるかを確認するには、ダッシュボードの[テーブル エディター] アイコンをクリックして、作成したワークアウト テーブルを表示します。

このアプリケーションでは、7 つの列があります。

  • user_id
  • user_email
  • id
  • title
  • loads
  • reps
  • Date stamp

テーブル エディター

テーブルと列を設定したら、次のステップは、Supabase データベースを Next.js フロントエンド アプリケーションに接続することです。

Next.js を Supabase データベースに接続する

Supabase を Next.js アプリに接続するには、Project URLAnon Keyが必要です。これらは両方とも、データベース ダッシュボードで見つけることができます。これら 2 つのキーを取得するには、歯車アイコンをクリックして[設定]に移動し、[ API ] をクリックします。これら 2 つのキーが次のように表示されます。

URL API の設定

もちろん、これらの値は機密情報であるため、ブラウザやリポジトリで公開したくありません。.env.local私たちの利点として、Next.js は、プロジェクトのルートにファイルを作成できるようにする環境変数の組み込みサポートを提供します。これにより、環境変数が読み込まれ、接頭辞 を付けてブラウザーに公開されNEXT_PUBLICます。

それでは、.env.localプロジェクトのルートにファイルを作成し、URL とキーをファイルに含めましょう。

.env.local NEXT_PUBLIC_SUPABASE_URL= // プロジェクトの URL をここに貼り付けます NEXT_PUBLIC_SUPABASE_ANON_KEY= // スーパーベースの anon キーをここに貼り付けます

注意:デプロイ時にファイルが GitHub リポジトリにプッシュされないようにする (そして誰でも見ることができるようになる) のを防ぐために、ファイル.env.localに含めることを忘れないでください。gitignore

supabase.js次に、プロジェクトのルートで呼び出されるファイルを作成して、Supabase クライアント ファイルを作成しましょう。ファイル内にsupabase.js、次のコードを記述します。

// supabase.js
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseKey);

ここでは、createClientSupabase から関数をインポートし、 という変数を作成していますsupabase。関数を呼び出して、 URL ( ) と Anon Key ( )createClientというパラメータを渡します。supabaseUrlsupabaseKey

これで、プロジェクトのどこでも Supabase クライアントを呼び出して使用できるようになりました。

アプリの UI を構成する

まず、アプリを希望どおりに表示するように構成する必要があります。アプリが最初にロードされると、プロジェクト名とログインおよびサインアップオプションを含むナビゲーション バーが表示されます。ユーザーがサインアップしてログインすると、ナビゲーション バーに [ホーム] 、 [ログアウト] 、および[ワークアウトの作成]ボタンが表示されます。

また、ウェブサイトのすべてのページにフッターがあります。

これを行うには、およびファイルを格納するcomponentフォルダーを作成します。次に、 内でコンポーネントをおよびコンポーネントでラップし、アプリのすべてのページに表示されるようにします。Navbar.jsFooter.js_app.jspagesNavbarFooter

// _app.js
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
 return (
   <div>
     <Navbar/>
     <Component {...pageProps}/>
     <Footer />
   </div>
 );
}

export default MyApp;

私が使用したスタイルとこれら 2 つのコンポーネントがどのように見えるかを確認するために、ここに GitHub 要点を作成しました。

これで、ホームページは次のようになります。

アドレナジー ホーム画面

ユーザー認証の実装

ユーザー認証を実装するには、ファイル内のユーザー状態を初期化し、ユーザーをチェックして検証する関数を_app.js作成します。validateUser次に、返されたセッション オブジェクトにユーザー状態を設定します。

// _app.js

import { useState, useEffect } from "react";
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";
import { supabase } from "../utils/supabase";

function MyApp({ Component, pageProps }) {
 const [session, setSession] = useState(null);

 useEffect(() => {
   setSession(supabase.auth.session());
   supabase.auth.onAuthStateChange((_event, session) => {
     setSession(session);
   });
 }, []);
 return (
   <div>
     <Navbar session={session} />
     <Component {...pageProps} session={session} />
     <Footer />
   </div>
 );
}
export default MyApp;

ユーザーがアプリのホームページを読み込んだときに、ログインまたはサインアップするように指示するボタンを表示したいと考えています。[ログイン] ボタンをクリックすると、ユーザーが電子メールとパスワードを入力できるページにユーザーをリダイレクトする必要があります。それらが既存のユーザーであり、ログインの詳細が有効である場合、それらはホームページにリダイレクトされます。

ユーザーが無効な認証情報を持っている場合、アラート メッセージが表示され、ユーザーに問題が通知されます。代わりにサインアップ オプションが表示されます。

ユーザーがサインアップすると、入力したメールアドレスに確認メールが送信されます。電子メールの本文にあるリンクをクリックして、電子メールを確認する必要があります。

登録メールの確認

ここで、[ログイン] ボタンをクリックすると、次のページのユーザー ページにリダイレクトされます。

ログインページ

これで、[サインアップ] ボタンをクリックして、電子メールを入力できます。

サインアップページ

これをクリックすると、メールアドレスを確認するためのメールが送信されます。確認すると、ログインし、次のようなページが表示されます。

まだワークアウトがないウェルカム画面

サインインしていない場合、アクティビティ ダッシュボードを表示したり、新しいワークアウトを作成するボタンを表示したり、ログアウトしたりできないことに注意してください。これは、最初に述べた、Supabase によって提供された認証です。

ワークアウト機能の実装

ここで、ワークアウトを作成、変更、および削除するユーザー機能の作成について詳しく説明します。

すべてのワークアウトを取得しています

作成するすべてのワークアウトを取得して、ホームページにレンダリングする必要があります。index.jsファイル内でこれを行います。

// /pages/index.js
import Head from "next/head";
import Link from "next/link";
import { useEffect, useState } from "react";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import WorkoutCard from "../components/WorkoutCard";

export default function Home({ session }) {
 const [workouts, setWorkouts] = useState([]);
 const [loading, setLoading] = useState(true);

 useEffect(() => {
   fetchWorkouts();
 }, []);

 const fetchWorkouts = async () => {
   const user = supabase.auth.user();
   try {
     setLoading(true);
     const { data, error } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id);

     if (error) throw error;
     setWorkouts(data);
   } catch (error) {
     alert(error.message);
   } finally {
     setLoading(false);
   }
 };

 if (loading) {
   return <div className={styles.loading}>Fetching Workouts...</div>;
 }
 return (
   <div className={styles.container}>
     <Head>
       <title>Nextjs x Supabase</title>
       <meta name="description" content="Generated by create next app" />
       <link rel="icon" href="/favicon.ico" />
     </Head>

     <div className={styles.home}>
       {!session?.user ? (
         <div>
           <p>
             Welcome to Adrenargy. Kindly log in to your account or sign in for
             a demo
           </p>
         </div>
       ) : (
         <div>
           <p className={styles.workoutHeading}>
             Hello <span className={styles.email}>{session.user.email}</span>,
             Welcome to your dashboard
           </p>
           {workouts?.length === 0 ? (
             <div className={styles.noWorkout}>
               <p>You have no workouts yet</p>
               <Link href="/create">
                 <button className={styles.button}>
                   {" "}
                   Create a New Workout
                 </button>
               </Link>
             </div>
           ) : (
             <div>
               <p className={styles.workoutHeading}>Here are your workouts</p>
               <WorkoutCard data={workouts}/>
             </div>
           )}
         </div>
       )}
     </div>
   </div>
 );
}

このコンポーネントでは、ファイル内の propssessionから渡したオブジェクトを分解し、それを使用して承認されたユーザーを検証します。ユーザーがいない場合、ダッシュボードは表示されません。ユーザーがログインしている場合、ワークアウトのダッシュボードが表示されます。また、ワークアウトが作成されていない場合は、「まだワークアウトがありません」というテキストと、新しいワークアウトを作成するためのボタンが表示されます。page_app.js

workouts作成したワークアウトをレンダリングするために、空の配列である とloading、ブール値の を受け取る状態の2 つの状態がありtrueます。useEffectページがロードされたときにデータベースからワークアウト データを取得するために使用しています。

このfetchWorkouts関数は、メソッドを使用してデータベース内のワークアウト テーブルからすべてのデータを返すために、Supabase インスタンスを呼び出すために使用されますselect。。eq()filter メソッドは、現在ログインしているユーザーと一致するユーザー ID を持つデータのみを除外して返すために使用されます。次に、setWorkoutsデータベースから送信されたデータに設定され、データをフェッチするsetLoadingと元に戻されます。false

データがまだフェッチされている場合、ページには「Fetching Workouts…」と表示されます。データベースへのリクエストでワークアウトの配列が返された場合は、配列をマッピングしてWorkoutCardコンポーネントをレンダリングします。

コンポーネントではWorkoutCard、ワークアウトのタイトル、負荷、担当者、作成日時をレンダリングしています。作成された時刻は、ここで確認できるdate-fnsライブラリを使用してフォーマットされています。次のセクションで、カードの作成を開始したときにカードがどのように見えるかを確認します。

// Workoutcard.js

import Link from "next/link";
import styles from "../styles/WorkoutCard.module.css";
import { BsTrash } from "react-icons/bs";
import { FiEdit } from "react-icons/fi";
import { formatDistanceToNow } from "date-fns/";

const WorkoutCard = ({ data }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>
       </div>
     ))}
   </div>
 );
};

export default WorkoutCard;

新しいワークアウトの作成

ログインしたので、ダッシュボードは新鮮できれいです。新しいワークアウトを作成する機能を実装するには、 andフォルダーにそれぞれcreate.jsCreate.module.cssファイルを追加し、いくつかのロジックとスタイリングを実装します。pagesstyles

// /pages/create.js

import { supabase } from "../utils/supabase";
import { useState } from "react";
import styles from "../styles/Create.module.css";
import { useRouter } from "next/router";

const Create = () => {
 const initialState = {
   title: "",
   loads: "",
   reps: "",
 };

 const router = useRouter();
 const [workoutData, setWorkoutData] = useState(initialState);

 const { title, loads, reps } = workoutData;

 const handleChange = (e) => {
   setWorkoutData({ ...workoutData, [e.target.name]: e.target.value });
 };

 const createWorkout = async () => {
   try {
     const user = supabase.auth.user();

     const { data, error } = await supabase
       .from("workouts")
       .insert([
         {
           title,
           loads,
           reps,
           user_id: user?.id,
         },
       ])
       .single();
     if (error) throw error;
     alert("Workout created successfully");
     setWorkoutData(initialState);
     router.push("/");
   } catch (error) {
     alert(error.message);
   }
 };

 return (
   <>
     <div className={styles.container}>
       <div className={styles.form}>
         <p className={styles.title}>Create a New Workout</p>
         <label className={styles.label}>Title:</label>
         <input
           type="text"
           name="title"
           value={title}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter a title"
         />
         <label className={styles.label}>Load (kg):</label>
         <input
           type="text"
           name="loads"
           value={loads}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter weight load"
         />
         <label className={styles.label}>Reps:</label>
         <input
           type="text"
           name="reps"
           value={reps}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter number of reps"
         />

         <button className={styles.button} onClick={createWorkout}>
           Create Workout
         </button>
       </div>
     </div>
   </>
 );
};

export default Create;

ここで、基本的な UI スコープは、新しいワークアウトを作成するためのフォームを持つことです。データベースの作成時に指定したように、フォームは 3 つのフィールド (タイトル、負荷、担当者) で構成されます。

状態に渡されたこれらすべてのフィールドを処理するために、初期状態オブジェクトが定義されますworkoutsData。このonChange関数は、入力フィールドの変更を処理するために使用されます。

このcreateWorkout関数は、Supabase クライアント インスタンスを使用して、定義した初期状態フィールドを使用して新しいワークアウトを作成し、それをデータベース テーブルに挿入します。

最後に、新しいワークアウトが作成されたときに通知するアラート トーストがあります。

次に、ワークアウトが作成されたら、フォーム データを最初の空の文字列の状態に戻します。その後、router.pushメソッドを使用して、ユーザーをホームページに戻します。

新しいプロジェクトを作成

ワークアウトが正常に作成されました

ワークアウト ダンベル プレスを備えたダッシュボード

ワークアウトの更新

ワークアウトを更新するには、ファイルを保持するフォルダーedit内にという名前のフォルダーを作成します。ワークアウト コンポーネント カードに、このページにリンクする編集リンク アイコンを作成します。カードがホームページに表示されたら、この編集アイコンをクリックすると、その特定のカードの編集ページに移動します。pages[id].js

次に、必要なワークアウト カードの詳細をワークアウト テーブルから取得し、idカードの承認された所有者によって取得します。次に、updateWorkoutワークアウト カードの詳細を更新する関数を作成します。

// /pages/edit/[id].js
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import styles from "../../styles/Edit.module.css";
import { supabase } from "../../utils/supabase";

const Edit = () => {
 const [workout, setWorkout] = useState("");
 const router = useRouter();

 const { id } = router.query;
 useEffect(() => {
   const user = supabase.auth.user();
   const getWorkout = async () => {
     const { data } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id)
       .filter("id", "eq", id)
       .single();
     setWorkout(data);
   };
   getWorkout();
 }, [id]);

 const handleOnChange = (e) => {
   setWorkout({
     ...workout,
     [e.target.name]: e.target.value,
   });
 };

 const { title, loads, reps } = workout;
 const updateWorkout = async () => {
   const user = supabase.auth.user();
   const { data } = await supabase
     .from("workouts")
     .update({
       title,
       loads,
       reps,
     })
     .eq("id", id)
     .eq("user_id", user?.id);

   alert("Workout updated successfully");

   router.push("/");
 };
 return (
   <div className={styles.container}>
     <div className={styles.formContainer}>
       <h1 className={styles.title}>Edit Workout</h1>
       <label className={styles.label}> Title:</label>
       <input
         type="text"
         name="title"
         value={workout.title}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Load (kg):</label>
       <input
         type="text"
         name="loads"
         value={workout.loads}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Reps:</label>
       <input
         type="text"
         name="reps"
         value={workout.reps}
         onChange={handleOnChange}
         className={styles.updateInput}
       />

       <button onClick={updateWorkout} className={styles.updateButton}>
         Update Workout
       </button>
     </div>
   </div>
 );
};

export default Edit;

まず、テーブルからフェッチされるワークアウト カードの詳細を格納する状態を作成します。次に、フックidを使用してそのカードのを抽出します。useRouterこのgetWorkout関数は、Supabase クライアント インスタンスを呼び出してidそのワークアウト カードをフィルター処理し、データ (タイトル、負荷、回数) を返します。

ワークアウト カードの詳細が返されたら、updateWorkout関数を使用して詳細を変更する関数を作成できます.update()。ユーザーがワークアウトを更新し、 [ワークアウトの更新] ボタンをクリックすると、警告メッセージが送信され、ユーザーはホームページにリダイレクトされます。

それがどのように機能するか見てみましょう。

編集アイコンをクリックして、編集ページに移動します。タイトルを「ダンベル プレス」から「アーム カール」に変更します。

ワークアウト ダンベル プレスの編集

ワークアウトの編集成功

アームカールでワークアウトを編集

ワークアウトの削除

各カードのワークアウトを削除するには、引数としてhandleDeleteを受け取る関数を作成します。idSupabase インスタンスを呼び出して、ワークアウト カードを削除します。

.delete()関数。テーブルで削除する行を.eq('id', id)指定します。id

 const handleDelete = async (id) => {
   try {


     const user = supabase.auth.user();
     const { data, error } = await supabase
       .from("workouts")
       .delete()
       .eq("id", id)
       .eq("user_id", user?.id);
     fetchWorkouts();
     alert("Workout deleted successfully");
   } catch (error) {
     alert(error.message);
   }
 };

eq('user_id', user?.id)、削除されているカードがその特定のユーザーに属しているかどうかを確認するために使用されます。WorkoutCard関数はファイル内のコンポーネントに渡され、index.js次のようにコンポーネント自体で使用するために分解されます。

const WorkoutCard = ({ data, handleDelete }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>

         <div className={styles.buttons}>
           <Link href={`/edit/${item.id}`}>
             <a className={styles.edit}>
               <FiEdit />
             </a>
           </Link>
           <button
             onClick={() => handleDelete(item.id)}
             className={styles.delete}
           >
             <BsTrash />
           </button>
         </div>
       </div>
     ))}
   </div>
 );
};

カードが正常に削除されるとアラート トーストが表示され、ユーザーはホームページにリダイレクトされます。

Vercel へのデプロイ

次に、アプリケーションを Vercel にデプロイして、インターネット上の誰もが使用できるようにする必要があります。

Vercel にデプロイするには、まずコードをリポジトリにプッシュし、Vercel ダッシュボードにログインして、[ Create New Project ]をクリックし、コードをプッシュしたばかりのリポジトリをクリックする必要があります。

前に作成した環境変数とその値 (NEXT_PUBLIC_SUPABASE_URLおよびNEXT_PUBLIC_SUPABASE_ANON_KEY) を[環境変数] フィールドに入力し、[デプロイ] をクリックしてアプリを運用環境にデプロイします。

ヴェルセルに配備

そして、ここにあります!

結論

読んでくれてありがとう!このチュートリアルで、Next.js と Supabase を使用してフルスタック アプリケーションを作成するために必要な知識が得られることを願っています。

このチュートリアルは主にフルスタック アプリケーションを作成するロジックに焦点を当てているため、ユース ケースに合わせてスタイルをカスタマイズできます。

ソース: https://blog.logrocket.com/build-full-stack-app-next-js-supabase/

 #nextjs #supabase #fullstack 

Next.js と Supabase を使用してフルスタック アプリを構築する

Cree Una Aplicación De Pila Completa Con Next.js Y Supabase

Cuando se trata de crear y elegir marcos para su próxima aplicación de pila completa, combinar Next.js con Supabase es una de las mejores opciones para trabajar en mi opinión.

Supabase es una alternativa de Firebase de código abierto con muchas herramientas poderosas, incluida la autenticación sin problemas . Como desarrollador, esto es clave para crear una aplicación full-stack exitosa.

Además de la autenticación, Supabase viene con otras funciones, como una base de datos de Postgres, suscripciones en tiempo real y almacenamiento de objetos. Creo que Supabase es uno de los back-end como servicios más fáciles de comenzar o integrar.

En este artículo, aprenderemos a crear una aplicación de pila completa con Next.js y Supabase . Hablaremos sobre cómo configurar un proyecto de Supabase, configurar la interfaz de usuario e implementar la autenticación y las funcionalidades.

El concepto de esta aplicación es que los usuarios realicen un seguimiento y creen actividades de entrenamiento basadas en parámetros específicos, editen estas actividades si hay errores o cambios necesarios y las eliminen si es necesario. ¡Empecemos!

Introducción a Next.js y Supabase

Next.js es una de las formas más fáciles y populares de crear aplicaciones React listas para producción. En los últimos años, Next.js ha experimentado un crecimiento exponencial significativo y muchas empresas lo han adoptado para crear sus aplicaciones.

¿Por qué debemos usar Supabase?

Supabase es una alternativa de código abierto y sin servidor a Firebase construida sobre la base de datos PostgreSQL. Proporciona todos los servicios de back-end necesarios para crear una aplicación de pila completa.

Como usuario, puede administrar su base de datos desde la interfaz de Supabase, desde la creación de tablas y relaciones hasta la escritura de consultas SQL y el motor en tiempo real sobre PostgreSQL.

Supabase viene con funciones realmente geniales que hacen que el desarrollo de su aplicación de pila completa sea aún más fácil. Algunas de estas características son:

  • Seguridad de nivel de fila (RLS): Supabase viene con la función PostgreSQL RLS que le permite restringir filas en las tablas de su base de datos. Cuando crea políticas, las crea directamente con SQL
  • Base de datos en tiempo real: Supabase tiene una función de actualización en la base de datos PostgreSQL que se puede usar para escuchar cambios en tiempo real
  • Interfaz de usuario de Supabase: Supabase tiene una biblioteca de componentes de interfaz de usuario de código abierto para crear aplicaciones de forma rápida y eficiente.
  • Autenticación de usuario: Supabase crea una auth.userstabla tan pronto como crea su base de datos. Cuando crea una aplicación, Supabase también asignará un usuario y una identificación tan pronto como se registre en la aplicación a la que se puede hacer referencia dentro de la base de datos. Para los métodos de inicio de sesión, hay diferentes formas de autenticar a los usuarios, como correo electrónico, contraseña, enlaces mágicos, Google, GitHub y más.
  • Funciones perimetrales: las funciones perimetrales son funciones de TypeScript distribuidas globalmente en el perímetro, cerca de los usuarios. Se pueden utilizar para realizar funciones como la integración con terceros o la escucha de WebHooks.

Iniciando nuestro proyecto con Next.js

Para iniciar nuestro proyecto en la terminal con la plantilla Next.js ejecutaremos el siguiente comando:

npx create-next-app nextjs-supabase

nextjs-supabasees el nombre de la carpeta de nuestra aplicación donde incluiremos la plantilla de la aplicación Next.js.

Tendremos que instalar el paquete de cliente de Supabase para conectarnos a nuestra aplicación Next.js más adelante. Podemos hacerlo ejecutando cualquiera de los siguientes comandos:

yarn add @supabase/supabase-js

o

npm i @supabase/supabase-js

Una vez que la aplicación haya terminado de configurarse, abra la carpeta en su editor de código favorito. Ahora, podemos eliminar la plantilla básica de nuestro /pages/index.jsarchivo y reemplazarla con un h1encabezado que diga "Bienvenido a la aplicación Workout".

Una vez hecho esto, ejecute el comando yarn deven la terminal para iniciar su aplicación en http://localhost:3000 . Deberías ver una página como esta:

Pantalla de bienvenida

Configuración de un proyecto Supabase y creación de una tabla de base de datos

Para configurar un proyecto de Supabase, visite app.supabase.com para iniciar sesión en el panel de la aplicación con su cuenta de GitHub.

Tablero Supabase

Una vez que inicie sesión, puede crear su organización y configurar un nuevo proyecto dentro de ella haciendo clic en Todos los proyectos .

Pantalla de todos los proyectos y organización

Haga clic en Nuevo proyecto y asigne a su proyecto un nombre y una contraseña de base de datos. Haga clic en el botón Crear un nuevo proyecto ; su proyecto tardará un par de minutos en estar en funcionamiento.

Crear un nuevo proyecto

Una vez que se haya creado el proyecto, debería ver un tablero como este:

Panel de Supabase de Workout Next

Para este tutorial, ya creé un proyecto llamadoworkout-next-supabase.

Ahora, creemos nuestra tabla de base de datos haciendo clic en el ícono del Editor SQL en nuestro tablero y haciendo clic en Nueva consulta . Ingrese la consulta SQL a continuación en el editor y haga clic en EJECUTAR para ejecutar la consulta.

CREATE TABLE workouts (
 id bigint generated by default as identity primary key,
 user_id uuid references auth.users not null,
 user_email text,
 title text,
 loads text,
 reps text,
 inserted_at timestamp with time zone default timezone('utc'::text, now()) not null
);

alter table workouts enable row level security;

create policy "Individuals can create workouts." on workouts for
   insert with check (auth.uid() = user_id);

create policy "Individuals can update their own workouts." on workouts for
   update using (auth.uid() = user_id);

create policy "Individuals can delete their own workouts." on workouts for
   delete using (auth.uid() = user_id);

create policy "Workouts are public." on workouts for
   select using (true);

Ejecute su consulta

Esto creará la tabla de entrenamiento que usaremos para construir nuestra aplicación CRUD.

Además de crear una tabla, se habilitarán permisos de nivel de fila para garantizar que solo los usuarios autorizados puedan crear, actualizar o eliminar los detalles de sus entrenamientos.

Para ver cómo se ve la tabla de ejercicios, podemos hacer clic en el ícono del Editor de tablas en el tablero para ver la tabla de ejercicios que acabamos de crear.

Para esta aplicación, tendremos siete columnas:

  • user_id
  • user_email
  • id
  • title
  • loads
  • reps
  • Date stamp

Editor de tablas

Una vez que nuestra tabla y columnas están configuradas, el siguiente paso es conectar nuestra base de datos Supabase con nuestra aplicación frontend Next.js.

Conexión de Next.js con una base de datos Supabase

Para conectar Supabase con nuestra aplicación Next.js, necesitaremos la URL de nuestro proyecto y Anon Key . Ambos se pueden encontrar en nuestro panel de base de datos. Para obtener estas dos claves, haga clic en el ícono de ajustes para ir a Configuración y luego haga clic en API . Verás que estas dos teclas aparecen así:

Configuración de API de URL

Por supuesto, no queremos exponer estos valores públicamente en el navegador o en nuestro repositorio, ya que se trata de información confidencial. Para nuestra ventaja, Next.js brinda soporte incorporado para variables de entorno que nos permiten crear un .env.localarchivo en la raíz de nuestro proyecto. Esto cargará nuestras variables de entorno y las expondrá al navegador con el prefijo NEXT_PUBLIC.

Ahora, creemos un .env.localarchivo en la raíz de nuestro proyecto e incluyamos nuestra URL y claves en el archivo.

.env.local NEXT_PUBLIC_SUPABASE_URL= // pega aquí la url de tu proyecto NEXT_PUBLIC_SUPABASE_ANON_KEY= // pega aquí la clave anon de supabase

NB, no olvide incluirlo .env.localen su gitignorearchivo para evitar que se envíe al repositorio de GitHub (y esté disponible para que todos lo vean) al implementarlo.

Ahora vamos a crear nuestro archivo de cliente de Supabase creando un archivo llamado supabase.jsraíz de nuestro proyecto. Dentro del supabase.jsarchivo, escribiremos el siguiente código:

// supabase.js
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseKey);

Aquí, estamos importando una createClientfunción de Supabase y creando una variable llamada supabase. Llamamos a la createClientfunción y luego pasamos nuestros parámetros: URL ( supabaseUrl) y Anon Key ( supabaseKey).

¡Ahora podemos llamar y usar el cliente de Supabase en cualquier parte de nuestro proyecto!

Configurando la interfaz de usuario de nuestra aplicación

Primero, necesitamos configurar nuestra aplicación para que se vea como queremos. Tendremos una barra de navegación con el nombre del proyecto y las opciones Iniciar sesión y Registrarse cuando la aplicación se cargue por primera vez. Cuando un usuario se registre e inicie sesión, mostraremos la barra de navegación para tener los botones Inicio , Cerrar sesión y Crear entrenamiento .

También habrá un pie de página en cada página del sitio web.

Para ello, crearemos una componentcarpeta que albergará los archivos Navbar.jsy . Footer.jsLuego, dentro _app.jsde , envolveremos nuestro pagescomponente con Navbary los Footercomponentes para que se muestren en todas las páginas de la aplicación.

// _app.js
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
 return (
   <div>
     <Navbar/>
     <Component {...pageProps}/>
     <Footer />
   </div>
 );
}

export default MyApp;

Creé una esencia de GitHub aquí para ver cómo se ven estos dos componentes junto con los estilos que usé.

Ahora, nuestra página de inicio debería verse así:

Pantalla de inicio de adrenalina

Implementación de la autenticación de usuario

Para implementar la autenticación de usuario, inicializaremos el estado del usuario en nuestro _app.jsarchivo y crearemos una validateUserfunción para verificar y validar un usuario. Luego estableceremos el estado de usuario en el objeto de sesión que se devuelve.

// _app.js

import { useState, useEffect } from "react";
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";
import { supabase } from "../utils/supabase";

function MyApp({ Component, pageProps }) {
 const [session, setSession] = useState(null);

 useEffect(() => {
   setSession(supabase.auth.session());
   supabase.auth.onAuthStateChange((_event, session) => {
     setSession(session);
   });
 }, []);
 return (
   <div>
     <Navbar session={session} />
     <Component {...pageProps} session={session} />
     <Footer />
   </div>
 );
}
export default MyApp;

Cuando un usuario carga la página de inicio de nuestra aplicación, queremos mostrar un botón para indicarle que inicie sesión o se registre. Cuando se hace clic en el botón Iniciar sesión , debe redirigir al usuario a una página donde el usuario puede ingresar su correo electrónico y contraseña. Si es un usuario existente y los detalles de inicio de sesión son válidos, será redirigido a la página de inicio.

Si el usuario tiene credenciales no válidas, aparecerá un mensaje de alerta para informarle sobre el problema. En su lugar, se les mostrará una opción de registro.

Cuando el usuario se registre, se enviará un correo electrónico de confirmación al correo electrónico que ingresó. deberán confirmar su correo electrónico haciendo clic en el enlace en el cuerpo del correo electrónico.

Confirmar correo electrónico de registro

Ahora, cuando hacemos clic en el botón Iniciar sesión , deberíamos ser redirigidos a la página del usuario a esta página:

Página de inicio de sesión

Ahora, podemos hacer clic en el botón Registrarse e ingresar un correo electrónico.

Página de registro

Una vez que hagamos clic aquí, se enviará un correo electrónico para confirmar la dirección de correo electrónico. Al confirmar, nos iniciará sesión y deberíamos ver una página como esta:

Pantalla de bienvenida sin entrenamientos todavía

Tenga en cuenta que si no hemos iniciado sesión, no podemos ver nuestro panel de actividad, ver un botón para crear un nuevo entrenamiento o cerrar sesión. ¡Esta fue la autenticación mencionada inicialmente que nos proporcionó Supabase!

Implementación de funcionalidades de entrenamiento

Ahora, nos sumergiremos en la creación de la capacidad de un usuario para crear, modificar y eliminar sus entrenamientos.

Obteniendo todos los entrenamientos

Tendremos que buscar todos los entrenamientos que crearemos y mostrarlos en la página de inicio. Haremos esto dentro del index.jsarchivo:

// /pages/index.js
import Head from "next/head";
import Link from "next/link";
import { useEffect, useState } from "react";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import WorkoutCard from "../components/WorkoutCard";

export default function Home({ session }) {
 const [workouts, setWorkouts] = useState([]);
 const [loading, setLoading] = useState(true);

 useEffect(() => {
   fetchWorkouts();
 }, []);

 const fetchWorkouts = async () => {
   const user = supabase.auth.user();
   try {
     setLoading(true);
     const { data, error } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id);

     if (error) throw error;
     setWorkouts(data);
   } catch (error) {
     alert(error.message);
   } finally {
     setLoading(false);
   }
 };

 if (loading) {
   return <div className={styles.loading}>Fetching Workouts...</div>;
 }
 return (
   <div className={styles.container}>
     <Head>
       <title>Nextjs x Supabase</title>
       <meta name="description" content="Generated by create next app" />
       <link rel="icon" href="/favicon.ico" />
     </Head>

     <div className={styles.home}>
       {!session?.user ? (
         <div>
           <p>
             Welcome to Adrenargy. Kindly log in to your account or sign in for
             a demo
           </p>
         </div>
       ) : (
         <div>
           <p className={styles.workoutHeading}>
             Hello <span className={styles.email}>{session.user.email}</span>,
             Welcome to your dashboard
           </p>
           {workouts?.length === 0 ? (
             <div className={styles.noWorkout}>
               <p>You have no workouts yet</p>
               <Link href="/create">
                 <button className={styles.button}>
                   {" "}
                   Create a New Workout
                 </button>
               </Link>
             </div>
           ) : (
             <div>
               <p className={styles.workoutHeading}>Here are your workouts</p>
               <WorkoutCard data={workouts}/>
             </div>
           )}
         </div>
       )}
     </div>
   </div>
 );
}

En este componente, desestructuramos el sessionobjeto que pasamos de los pageaccesorios en el _app.jsarchivo y lo usamos para validar a los usuarios autorizados. Si no hay usuarios, el tablero no se mostrará. Si hay un usuario logueado, aparecerá el panel de entrenamientos. Y si no hay entrenamientos creados, aparecerá un texto que dice “Todavía no tienes ningún entrenamiento” y un botón para crear uno nuevo.

Para representar nuestros entrenamientos creados, tenemos dos estados: workouts, una matriz vacía y un loadingestado que toma un valor booleano de true. Estamos utilizando useEffectpara obtener los datos de los entrenamientos de la base de datos cuando se carga la página.

La fetchWorkoutsfunción se usa para llamar a la instancia de Supabase para devolver todos los datos de las tablas de entrenamiento en nuestra base de datos usando el selectmétodo. Los . eq()El método de filtro se usa para filtrar y devolver solo los datos con la identificación de usuario que coincide con el usuario conectado actual. Luego, setWorkoutsse establece en los datos enviados desde la base de datos y setLoadingse vuelve a establecer falseuna vez que recuperamos nuestros datos.

Si aún se están obteniendo los datos, la página debería mostrar "Obteniendo entrenamientos..." y si la solicitud realizada a nuestra base de datos devuelve la matriz de nuestros entrenamientos, queremos mapear a través de la matriz y representar el WorkoutCardcomponente.

En el WorkoutCardcomponente, representamos el título del entrenamiento, la carga, las repeticiones y la fecha y hora en que se creó. El tiempo creado se está formateando usando la date-fnsbiblioteca que puede consultar aquí . Veremos cómo se ven nuestras tarjetas cuando comencemos a crearlas en la siguiente sección.

// Workoutcard.js

import Link from "next/link";
import styles from "../styles/WorkoutCard.module.css";
import { BsTrash } from "react-icons/bs";
import { FiEdit } from "react-icons/fi";
import { formatDistanceToNow } from "date-fns/";

const WorkoutCard = ({ data }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>
       </div>
     ))}
   </div>
 );
};

export default WorkoutCard;

Creando un nuevo entrenamiento

Ahora que hemos iniciado sesión, nuestro tablero está nuevo y limpio. Para implementar la capacidad de crear un nuevo entrenamiento, agregaremos create.jsy Create.module.cssarchivos en la carpeta pagesy stylesrespectivamente, e implementaremos algo de lógica y estilo.

// /pages/create.js

import { supabase } from "../utils/supabase";
import { useState } from "react";
import styles from "../styles/Create.module.css";
import { useRouter } from "next/router";

const Create = () => {
 const initialState = {
   title: "",
   loads: "",
   reps: "",
 };

 const router = useRouter();
 const [workoutData, setWorkoutData] = useState(initialState);

 const { title, loads, reps } = workoutData;

 const handleChange = (e) => {
   setWorkoutData({ ...workoutData, [e.target.name]: e.target.value });
 };

 const createWorkout = async () => {
   try {
     const user = supabase.auth.user();

     const { data, error } = await supabase
       .from("workouts")
       .insert([
         {
           title,
           loads,
           reps,
           user_id: user?.id,
         },
       ])
       .single();
     if (error) throw error;
     alert("Workout created successfully");
     setWorkoutData(initialState);
     router.push("/");
   } catch (error) {
     alert(error.message);
   }
 };

 return (
   <>
     <div className={styles.container}>
       <div className={styles.form}>
         <p className={styles.title}>Create a New Workout</p>
         <label className={styles.label}>Title:</label>
         <input
           type="text"
           name="title"
           value={title}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter a title"
         />
         <label className={styles.label}>Load (kg):</label>
         <input
           type="text"
           name="loads"
           value={loads}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter weight load"
         />
         <label className={styles.label}>Reps:</label>
         <input
           type="text"
           name="reps"
           value={reps}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter number of reps"
         />

         <button className={styles.button} onClick={createWorkout}>
           Create Workout
         </button>
       </div>
     </div>
   </>
 );
};

export default Create;

Aquí, el alcance básico de la interfaz de usuario es que tendremos un formulario para crear un nuevo entrenamiento. El formulario constará de tres campos (título, carga y representantes) como especificamos al crear nuestra base de datos.

Se define un objeto de estado inicial para manejar todos estos campos que se pasaron al workoutsDataestado. La onChangefunción se utiliza para manejar los cambios del campo de entrada.

La createWorkoutfunción usa la instancia del cliente Supabase para crear un nuevo entrenamiento usando los campos de estado inicial que definimos e insertarlo en la tabla de la base de datos.

Por último, tenemos un brindis de alerta que nos informa cuando se ha creado nuestro nuevo entrenamiento.

Luego, volvemos a establecer los datos del formulario en el estado inicial de cadena vacía una vez que se ha creado nuestro entrenamiento. Después de eso, estamos usando el router.pushmétodo para que el usuario regrese a la página de inicio.

Crear nuevo proyecto

Entrenamiento creado con éxito

Tablero con entrenamientos Dumbell Press

Actualización de un entrenamiento

Para actualizar un entrenamiento, crearemos una carpeta llamada editdentro de nuestra pagescarpeta que contendrá nuestro [id].jsarchivo. Crearemos un icono de enlace de edición en nuestra tarjeta de componente de entrenamiento que enlaza con esta página. Cuando las tarjetas se representan en la página de inicio, podemos hacer clic en este icono de edición y nos llevará a la página de edición de esa tarjeta en particular.

Luego buscaremos los detalles de la tarjeta de entrenamiento necesaria para ser actualizados desde nuestra tabla de entrenamientos por él idy el propietario autorizado de la tarjeta. Luego, crearemos una updateWorkoutfunción para actualizar los detalles de nuestra tarjeta de entrenamiento:

// /pages/edit/[id].js
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import styles from "../../styles/Edit.module.css";
import { supabase } from "../../utils/supabase";

const Edit = () => {
 const [workout, setWorkout] = useState("");
 const router = useRouter();

 const { id } = router.query;
 useEffect(() => {
   const user = supabase.auth.user();
   const getWorkout = async () => {
     const { data } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id)
       .filter("id", "eq", id)
       .single();
     setWorkout(data);
   };
   getWorkout();
 }, [id]);

 const handleOnChange = (e) => {
   setWorkout({
     ...workout,
     [e.target.name]: e.target.value,
   });
 };

 const { title, loads, reps } = workout;
 const updateWorkout = async () => {
   const user = supabase.auth.user();
   const { data } = await supabase
     .from("workouts")
     .update({
       title,
       loads,
       reps,
     })
     .eq("id", id)
     .eq("user_id", user?.id);

   alert("Workout updated successfully");

   router.push("/");
 };
 return (
   <div className={styles.container}>
     <div className={styles.formContainer}>
       <h1 className={styles.title}>Edit Workout</h1>
       <label className={styles.label}> Title:</label>
       <input
         type="text"
         name="title"
         value={workout.title}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Load (kg):</label>
       <input
         type="text"
         name="loads"
         value={workout.loads}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Reps:</label>
       <input
         type="text"
         name="reps"
         value={workout.reps}
         onChange={handleOnChange}
         className={styles.updateInput}
       />

       <button onClick={updateWorkout} className={styles.updateButton}>
         Update Workout
       </button>
     </div>
   </div>
 );
};

export default Edit;

Primero, creamos un estado para almacenar los detalles de la tarjeta de entrenamiento que se obtendrán de nuestra tabla. Luego, extraemos el idde esa tarjeta usando el useRoutergancho. La getWorkoutfunción llama a la instancia del cliente de Supabase para filtrar la idde esa tarjeta de entrenamiento y devuelve los datos (título, cargas y repeticiones).

Una vez que se han devuelto los detalles de la tarjeta de entrenamiento, podemos crear nuestra updateWorkoutfunción para modificar los detalles usando la .update()función. Una vez que el usuario ha actualizado el entrenamiento y se hace clic en el botón Actualizar entrenamiento , se envía un mensaje de alerta y el usuario será redirigido a la página de inicio.

Vamos a ver cómo funciona.

Haga clic en el icono de edición para ir a la página de edición. Cambiaremos el nombre del título de "Prensa con mancuernas" a "Doblar brazos":

Editar entrenamiento Press con mancuernas

Editar entrenamiento exitoso

Editar entrenamiento con curl de brazos

Eliminación de un entrenamiento

Para eliminar un entrenamiento en cada tarjeta, crearemos la handleDeletefunción que tomará idcomo argumento. Llamaremos a la instancia de Supabase para eliminar una tarjeta de entrenamiento usando el

.delete()función. Esto .eq('id', id)especifica el valor idde la fila que se eliminará en la tabla.

 const handleDelete = async (id) => {
   try {


     const user = supabase.auth.user();
     const { data, error } = await supabase
       .from("workouts")
       .delete()
       .eq("id", id)
       .eq("user_id", user?.id);
     fetchWorkouts();
     alert("Workout deleted successfully");
   } catch (error) {
     alert(error.message);
   }
 };

El eq('user_id', user?.id)se utiliza para verificar si la tarjeta que se está eliminando pertenece a ese usuario en particular. La función se pasará al WorkoutCardcomponente en el index.jsarchivo y se desestructurará para su uso en el propio componente de la siguiente manera:

const WorkoutCard = ({ data, handleDelete }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>

         <div className={styles.buttons}>
           <Link href={`/edit/${item.id}`}>
             <a className={styles.edit}>
               <FiEdit />
             </a>
           </Link>
           <button
             onClick={() => handleDelete(item.id)}
             className={styles.delete}
           >
             <BsTrash />
           </button>
         </div>
       </div>
     ))}
   </div>
 );
};

Se mostrará un brindis de alerta una vez que la tarjeta se haya eliminado con éxito y el usuario será redirigido a la página de inicio.

Implementación en Vercel

¡Ahora, tenemos que implementar nuestra aplicación en Vercel para que cualquiera en Internet pueda usarla!

Para implementar en Vercel, primero debe enviar su código a su repositorio, iniciar sesión en su panel de control de Vercel, hacer clic en Crear nuevo proyecto y hacer clic en el repositorio al que acaba de enviar su código.

Ingrese las variables de entorno que creamos anteriormente junto con sus valores ( NEXT_PUBLIC_SUPABASE_URLy NEXT_PUBLIC_SUPABASE_ANON_KEY) en el campo Variable de entorno y haga clic en Implementar para implementar su aplicación en producción.

Implementar en Vercel

¡Y ahí lo tenemos!

Conclusión

¡Gracias por leer! Espero que este tutorial le brinde los conocimientos necesarios para crear una aplicación de pila completa con Next.js y Supabase.

Puede personalizar el estilo para su caso de uso, ya que este tutorial se centra principalmente en la lógica de crear una aplicación de pila completa.

Fuente: https://blog.logrocket.com/build-full-stack-app-next-js-supabase/

    #nextjs #supabase #fullstack 

Cree Una Aplicación De Pila Completa Con Next.js Y Supabase

Crie Um Aplicativo Full Stack Com Next.js E Supabase

Quando se trata de construir e escolher frameworks para seu próximo aplicativo full-stack, combinar Next.js com Supabase é uma das melhores opções para trabalhar na minha opinião.

O Supabase é uma alternativa de código aberto ao Firebase com muitas ferramentas poderosas, incluindo autenticação perfeita . Como desenvolvedor, isso é fundamental para criar um aplicativo full-stack bem-sucedido.

Além da autenticação, o Supabase vem com outros recursos, como banco de dados Postgres, assinaturas em tempo real e armazenamento de objetos. Acredito que o Supabase é um dos serviços de back-end como um serviço mais fáceis de começar ou integrar.

Neste artigo, aprenderemos como criar um aplicativo full-stack usando Next.js e Supabase . Falaremos sobre como configurar um projeto Supabase, configurar a interface do usuário e implementar autenticação e funcionalidades.

O conceito deste aplicativo é que os usuários rastreiem e criem atividades de treino com base em parâmetros especificados, editem essas atividades se houver erros ou alterações necessárias e as excluam, se necessário. Vamos começar!

Introdução ao Next.js e Supabase

Next.js é uma das maneiras mais fáceis e populares de construir aplicativos React prontos para produção. Nos últimos anos, o Next.js experimentou um crescimento exponencial significativo e muitas empresas o adotaram para criar seus aplicativos.

Por que devemos usar Supabase?

O Supabase é uma alternativa de código aberto e sem servidor ao Firebase , construída sobre o banco de dados PostgreSQL. Ele fornece todos os serviços de back-end necessários para criar um aplicativo full-stack.

Como usuário, você pode gerenciar seu banco de dados a partir da interface do Supabase, desde a criação de tabelas e relacionamentos até a gravação de suas consultas SQL e mecanismo em tempo real no PostgreSQL.

O Supabase vem com recursos muito legais que facilitam ainda mais o desenvolvimento de aplicativos full-stack. Algumas dessas características são:

  • Segurança em nível de linha (RLS) – O Supabase vem com o recurso PostgreSQL RLS que permite restringir linhas em suas tabelas de banco de dados. Ao criar políticas, você as cria diretamente com SQL
  • Banco de dados em tempo real – Supabase possui um recurso de atualização no banco de dados PostgreSQL que pode ser usado para ouvir alterações em tempo real
  • Supabase UI – O Supabase possui uma biblioteca de componentes de interface de usuário de código aberto para criar aplicativos de forma rápida e eficiente
  • Autenticação do usuário – O Supabase cria uma auth.userstabela assim que você cria seu banco de dados. Ao criar um aplicativo, o Supabase também atribuirá um usuário e ID assim que você se registrar no aplicativo que pode ser referenciado no banco de dados. Para métodos de login, existem diferentes maneiras de autenticar usuários, como e-mail, senha, links mágicos, Google, GitHub e muito mais
  • Funções de borda – As funções de borda são funções TypeScript distribuídas globalmente na borda, próximas aos usuários. Eles podem ser usados ​​para executar funções como integração com terceiros ou escuta de WebHooks

Iniciando nosso projeto com Next.js

Para iniciar nosso projeto no terminal com o template Next.js, executaremos o seguinte comando:

npx create-next-app nextjs-supabase

nextjs-supabaseé o nome da pasta do nosso aplicativo onde englobaremos o modelo de aplicativo Next.js.

Precisaremos instalar o pacote do cliente Supabase para nos conectarmos ao nosso aplicativo Next.js posteriormente. Podemos fazer isso executando um dos seguintes comandos:

yarn add @supabase/supabase-js

ou

npm i @supabase/supabase-js

Depois que o aplicativo terminar de configurar, abra a pasta em seu editor de código favorito. Agora, podemos remover o modelo básico em nosso /pages/index.jsarquivo e substituí-lo por um h1título dizendo “Bem-vindo ao aplicativo de treino”.

Feito isso, execute o comando yarn devno terminal para iniciar seu aplicativo em http://localhost:3000 . Você deve ver uma página como esta:

Tela de boas vindas

Configurando um projeto Supabase e criando uma tabela de banco de dados

Para configurar um projeto Supabase, visite app.supabase.com para entrar no painel do aplicativo usando sua conta do GitHub.

Painel Supabase

Depois de fazer login, você pode criar sua organização e configurar um novo projeto dentro dela clicando em Todos os projetos .

Todos os projetos e tela de organização

Clique em New Project e dê ao seu projeto um nome e uma senha de banco de dados. Clique no botão Criar um novo projeto ; levará alguns minutos para que seu projeto esteja funcionando.

Criar um novo projeto

Depois que o projeto for criado, você deverá ver um painel como este:

Painel Supabase do próximo treino

Para este tutorial, já criei um projeto chamadoworkout-next-supabase.

Agora, vamos criar nossa tabela de banco de dados clicando no ícone SQL Editor em nosso painel e clicando em New Query . Insira a consulta SQL abaixo no editor e clique em EXECUTAR para executar a consulta.

CREATE TABLE workouts (
 id bigint generated by default as identity primary key,
 user_id uuid references auth.users not null,
 user_email text,
 title text,
 loads text,
 reps text,
 inserted_at timestamp with time zone default timezone('utc'::text, now()) not null
);

alter table workouts enable row level security;

create policy "Individuals can create workouts." on workouts for
   insert with check (auth.uid() = user_id);

create policy "Individuals can update their own workouts." on workouts for
   update using (auth.uid() = user_id);

create policy "Individuals can delete their own workouts." on workouts for
   delete using (auth.uid() = user_id);

create policy "Workouts are public." on workouts for
   select using (true);

Execute sua consulta

Isso criará a tabela de exercícios que usaremos para construir nosso aplicativo CRUD.

Além de criar uma tabela, as permissões em nível de linha serão habilitadas para garantir que apenas usuários autorizados possam criar, atualizar ou excluir os detalhes de seus treinos.

Para conferir a aparência da tabela de exercícios, podemos clicar no ícone Editor de Tabelas no painel para ver a tabela de exercícios que acabamos de criar.

Para esta aplicação, teremos sete colunas:

  • user_id
  • user_email
  • id
  • title
  • loads
  • reps
  • Date stamp

Editor de Tabelas

Uma vez que nossa tabela e colunas estejam definidas, o próximo passo é conectar nosso banco de dados Supabase com nosso aplicativo frontend Next.js!

Conectando Next.js com um banco de dados Supabase

Para conectar o Supabase ao nosso aplicativo Next.js, precisaremos do URL do projeto e da chave Anon . Ambos podem ser encontrados em nosso painel de banco de dados. Para obter essas duas chaves, clique no ícone de engrenagem para acessar Configurações e clique em API . Você verá essas duas chaves aparecerem assim:

Configuração da API de URL

Obviamente, não queremos expor esses valores publicamente no navegador ou em nosso repositório, pois são informações confidenciais. Para nossa vantagem, o Next.js fornece suporte embutido para variáveis ​​de ambiente que nos permitem criar um .env.localarquivo na raiz do nosso projeto. Isso carregará nossas variáveis ​​de ambiente e as exporá ao navegador prefixando-as com NEXT_PUBLIC.

Agora, vamos criar um .env.localarquivo na raiz do nosso projeto e incluir nossa URL e chaves no arquivo.

.env.local NEXT_PUBLIC_SUPABASE_URL= // cole a url do seu projeto aqui NEXT_PUBLIC_SUPABASE_ANON_KEY= // cole sua chave anon do supabase aqui

NB, não se esqueça de incluir .env.localem seu gitignorearquivo para evitar que ele seja enviado para o repositório do GitHub (e disponível para todos verem) durante a implantação.

Agora vamos criar nosso arquivo cliente Supabase criando um arquivo chamado supabase.jsna raiz do nosso projeto. Dentro do supabase.jsarquivo, escreveremos o seguinte código:

// supabase.js
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseKey);

Aqui, estamos importando uma createClientfunção do Supabase e criando uma variável chamada supabase. Chamamos a createClientfunção e passamos nossos parâmetros: URL ( supabaseUrl) e Anon Key ( supabaseKey).

Agora, podemos chamar e usar o cliente Supabase em qualquer lugar do nosso projeto!

Configurando a IU do nosso aplicativo

Primeiro, precisamos configurar nosso aplicativo para ter a aparência que queremos. Teremos uma barra de navegação com o nome do projeto e as opções de Login e Inscrição quando o aplicativo for carregado pela primeira vez. Quando um usuário se inscrever e efetuar login, exibiremos a barra de navegação com os botões Home , Logout e Create Workout .

Haverá também um rodapé em todas as páginas do site.

Para fazer isso, criaremos uma componentpasta que abrigará os arquivos Navbar.jse . Footer.jsEm seguida, dentro _app.jsde , envolveremos nosso pagescomponente com o Navbare os Footercomponentes para que sejam exibidos em todas as páginas do aplicativo.

// _app.js
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
 return (
   <div>
     <Navbar/>
     <Component {...pageProps}/>
     <Footer />
   </div>
 );
}

export default MyApp;

Eu criei um GitHub gist aqui para ver como esses dois componentes se parecem com os estilos que usei.

Agora, nossa página inicial deve ficar assim:

Tela inicial de adrenargia

Implementando a autenticação do usuário

Para implementar a autenticação do usuário, inicializaremos o estado do usuário em nosso _app.jsarquivo e criaremos uma validateUserfunção para verificar e validar um usuário. Em seguida, definiremos o estado do usuário para o objeto de sessão retornado.

// _app.js

import { useState, useEffect } from "react";
import Footer from "../components/Footer";
import Navbar from "../components/Navbar";
import "../styles/globals.css";
import { supabase } from "../utils/supabase";

function MyApp({ Component, pageProps }) {
 const [session, setSession] = useState(null);

 useEffect(() => {
   setSession(supabase.auth.session());
   supabase.auth.onAuthStateChange((_event, session) => {
     setSession(session);
   });
 }, []);
 return (
   <div>
     <Navbar session={session} />
     <Component {...pageProps} session={session} />
     <Footer />
   </div>
 );
}
export default MyApp;

Quando um usuário carrega a página inicial do nosso aplicativo, queremos exibir um botão para instruí-lo a fazer login ou se inscrever. Ao clicar no botão Login , ele deve redirecionar o usuário para uma página onde o usuário pode inserir seu e-mail e senha. Se for um usuário existente e os detalhes de login forem válidos, eles serão redirecionados para a página inicial.

Se o usuário tiver credenciais inválidas, uma mensagem de alerta será exibida para informar o usuário sobre o problema. Em vez disso, eles verão uma opção de inscrição.

Quando o usuário se inscrever, um e-mail de confirmação será enviado para o e-mail inserido. eles precisarão confirmar o e-mail clicando no link no corpo do e-mail.

Confirmar e-mail de inscrição

Agora, quando clicamos no botão Login , devemos ser redirecionados para a página do usuário para esta página:

Página de login

Agora, podemos clicar no botão Sign up e inserir um e-mail.

Página de inscrição

Assim que clicarmos, um e-mail será enviado para confirmar o endereço de e-mail. Ao confirmar, ele fará o login e devemos ver uma página como esta:

Tela de boas-vindas sem treinos ainda

Observe que, se não tivermos feito login, não poderemos ver nosso painel de atividades, ver um botão para criar um novo treino ou sair. Esta foi a autenticação mencionada inicialmente que nos foi fornecida pela Supabase!

Implementando funcionalidades de treino

Agora, vamos nos aprofundar na criação da capacidade do usuário de criar, modificar e excluir seus treinos.

Buscando todos os treinos

Precisaremos buscar todos os treinos que vamos criar e renderizá-los na página inicial. Faremos isso dentro do index.jsarquivo:

// /pages/index.js
import Head from "next/head";
import Link from "next/link";
import { useEffect, useState } from "react";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import WorkoutCard from "../components/WorkoutCard";

export default function Home({ session }) {
 const [workouts, setWorkouts] = useState([]);
 const [loading, setLoading] = useState(true);

 useEffect(() => {
   fetchWorkouts();
 }, []);

 const fetchWorkouts = async () => {
   const user = supabase.auth.user();
   try {
     setLoading(true);
     const { data, error } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id);

     if (error) throw error;
     setWorkouts(data);
   } catch (error) {
     alert(error.message);
   } finally {
     setLoading(false);
   }
 };

 if (loading) {
   return <div className={styles.loading}>Fetching Workouts...</div>;
 }
 return (
   <div className={styles.container}>
     <Head>
       <title>Nextjs x Supabase</title>
       <meta name="description" content="Generated by create next app" />
       <link rel="icon" href="/favicon.ico" />
     </Head>

     <div className={styles.home}>
       {!session?.user ? (
         <div>
           <p>
             Welcome to Adrenargy. Kindly log in to your account or sign in for
             a demo
           </p>
         </div>
       ) : (
         <div>
           <p className={styles.workoutHeading}>
             Hello <span className={styles.email}>{session.user.email}</span>,
             Welcome to your dashboard
           </p>
           {workouts?.length === 0 ? (
             <div className={styles.noWorkout}>
               <p>You have no workouts yet</p>
               <Link href="/create">
                 <button className={styles.button}>
                   {" "}
                   Create a New Workout
                 </button>
               </Link>
             </div>
           ) : (
             <div>
               <p className={styles.workoutHeading}>Here are your workouts</p>
               <WorkoutCard data={workouts}/>
             </div>
           )}
         </div>
       )}
     </div>
   </div>
 );
}

Neste componente, estamos desestruturando o sessionobjeto que passamos das pageprops no _app.jsarquivo e usando isso para validar usuários autorizados. Se não houver usuários, o painel não será exibido. Se houver um usuário logado, o painel de treinos aparecerá. E se não houver treinos criados, aparecerá um texto dizendo “Você não tem treino ainda” e um botão para criar um novo.

Para renderizar nossos treinos criados, temos dois estados: workouts, um array vazio e um loadingestado que recebe um valor booleano de true. Estamos usando useEffectpara buscar os dados de treinos do banco de dados quando a página é carregada.

A fetchWorkoutsfunção é usada para chamar a instância do Supabase para retornar todos os dados das tabelas de treino em nosso banco de dados usando o selectmétodo. O . eq()O método filter é usado para filtrar e retornar apenas os dados com o ID do usuário correspondente ao usuário conectado no momento. Em seguida, setWorkoutsé definido para os dados enviados do banco de dados e setLoadingé definido de volta falsequando buscamos nossos dados.

Se os dados ainda estiverem sendo buscados, a página deverá exibir “Fetching Workouts…” e se a requisição feita ao nosso banco de dados retornar o array dos nossos treinos, queremos mapear pelo array e renderizar o WorkoutCardcomponente.

No WorkoutCardcomponente, estamos renderizando o título do treino, carga, repetições e a data e hora em que foi criado. A hora criada está sendo formatada usando a date-fnsbiblioteca que você pode conferir aqui . Veremos como nossos cartões ficarão quando começarmos a criá-los na próxima seção.

// Workoutcard.js

import Link from "next/link";
import styles from "../styles/WorkoutCard.module.css";
import { BsTrash } from "react-icons/bs";
import { FiEdit } from "react-icons/fi";
import { formatDistanceToNow } from "date-fns/";

const WorkoutCard = ({ data }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>
       </div>
     ))}
   </div>
 );
};

export default WorkoutCard;

Criando um novo treino

Agora que efetuamos login, nosso painel está atualizado e limpo. Para implementar a capacidade de criar um novo treino, adicionaremos create.jse Create.module.cssarquivos na pasta pagese stylesrespectivamente, e implementaremos alguma lógica e estilo.

// /pages/create.js

import { supabase } from "../utils/supabase";
import { useState } from "react";
import styles from "../styles/Create.module.css";
import { useRouter } from "next/router";

const Create = () => {
 const initialState = {
   title: "",
   loads: "",
   reps: "",
 };

 const router = useRouter();
 const [workoutData, setWorkoutData] = useState(initialState);

 const { title, loads, reps } = workoutData;

 const handleChange = (e) => {
   setWorkoutData({ ...workoutData, [e.target.name]: e.target.value });
 };

 const createWorkout = async () => {
   try {
     const user = supabase.auth.user();

     const { data, error } = await supabase
       .from("workouts")
       .insert([
         {
           title,
           loads,
           reps,
           user_id: user?.id,
         },
       ])
       .single();
     if (error) throw error;
     alert("Workout created successfully");
     setWorkoutData(initialState);
     router.push("/");
   } catch (error) {
     alert(error.message);
   }
 };

 return (
   <>
     <div className={styles.container}>
       <div className={styles.form}>
         <p className={styles.title}>Create a New Workout</p>
         <label className={styles.label}>Title:</label>
         <input
           type="text"
           name="title"
           value={title}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter a title"
         />
         <label className={styles.label}>Load (kg):</label>
         <input
           type="text"
           name="loads"
           value={loads}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter weight load"
         />
         <label className={styles.label}>Reps:</label>
         <input
           type="text"
           name="reps"
           value={reps}
           onChange={handleChange}
           className={styles.input}
           placeholder="Enter number of reps"
         />

         <button className={styles.button} onClick={createWorkout}>
           Create Workout
         </button>
       </div>
     </div>
   </>
 );
};

export default Create;

Aqui, o escopo básico da interface do usuário é que teremos um formulário para criar um novo treino. O formulário consistirá em três campos (título, carga e representantes) conforme especificamos ao criar nosso banco de dados.

Um objeto de estado inicial é definido para lidar com todos esses campos que foram passados ​​para o workoutsDataestado. A onChangefunção é usada para lidar com as alterações do campo de entrada.

A createWorkoutfunção usa a instância do cliente Supabase para criar um novo treino usando os campos de estado inicial que definimos e inseri-lo na tabela do banco de dados.

Por fim, temos um brinde de alerta que nos informa quando nosso novo treino foi criado.

Em seguida, definimos os dados do formulário de volta ao estado inicial da string vazia depois que nosso treino foi criado. Depois disso, estamos usando o router.pushmétodo para levar o usuário de volta à página inicial.

Criar novo projeto

Treino criado com sucesso

Painel com exercícios Dumbell Press

Atualizando um treino

Para atualizar um treino, criaremos uma pasta chamada editdentro de nossa pagespasta que conterá nosso [id].jsarquivo. Criaremos um ícone de link de edição em nosso cartão de componente de treino com link para esta página. Quando os cartões são renderizados na página inicial, podemos clicar neste ícone de edição e isso nos levará à página de edição desse cartão em particular.

Em seguida, buscaremos os detalhes do cartão de treino necessário para serem atualizados em nossa tabela de treinos por ele ide pelo proprietário autorizado do cartão. Em seguida, criaremos uma updateWorkoutfunção para atualizar os detalhes do cartão de treino:

// /pages/edit/[id].js
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import styles from "../../styles/Edit.module.css";
import { supabase } from "../../utils/supabase";

const Edit = () => {
 const [workout, setWorkout] = useState("");
 const router = useRouter();

 const { id } = router.query;
 useEffect(() => {
   const user = supabase.auth.user();
   const getWorkout = async () => {
     const { data } = await supabase
       .from("workouts")
       .select("*")
       .eq("user_id", user?.id)
       .filter("id", "eq", id)
       .single();
     setWorkout(data);
   };
   getWorkout();
 }, [id]);

 const handleOnChange = (e) => {
   setWorkout({
     ...workout,
     [e.target.name]: e.target.value,
   });
 };

 const { title, loads, reps } = workout;
 const updateWorkout = async () => {
   const user = supabase.auth.user();
   const { data } = await supabase
     .from("workouts")
     .update({
       title,
       loads,
       reps,
     })
     .eq("id", id)
     .eq("user_id", user?.id);

   alert("Workout updated successfully");

   router.push("/");
 };
 return (
   <div className={styles.container}>
     <div className={styles.formContainer}>
       <h1 className={styles.title}>Edit Workout</h1>
       <label className={styles.label}> Title:</label>
       <input
         type="text"
         name="title"
         value={workout.title}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Load (kg):</label>
       <input
         type="text"
         name="loads"
         value={workout.loads}
         onChange={handleOnChange}
         className={styles.updateInput}
       />
       <label className={styles.label}> Reps:</label>
       <input
         type="text"
         name="reps"
         value={workout.reps}
         onChange={handleOnChange}
         className={styles.updateInput}
       />

       <button onClick={updateWorkout} className={styles.updateButton}>
         Update Workout
       </button>
     </div>
   </div>
 );
};

export default Edit;

Primeiro, criamos um estado para armazenar os detalhes do cartão de treino que serão buscados em nossa tabela. Em seguida, extraímos o iddesse cartão usando o useRoutergancho. A getWorkoutfunção chama a instância do cliente Supabase para filtrar o idcartão de treino e retorna os dados (título, cargas e representantes).

Depois que os detalhes do cartão de treino forem retornados, podemos criar nossa updateWorkoutfunção para modificar os detalhes usando a .update()função. Assim que o treino for atualizado pelo usuário e o botão Atualizar treino for clicado, uma mensagem de alerta será enviada e o usuário será redirecionado para a página inicial.

Vamos ver como isso funciona.

Clique no ícone de edição para ir para a página de edição. Vamos renomear o título de “Dumbell Press” para “Arm Curl”:

Editar treino com halteres

Editar treino bem-sucedido

Editar treino com flexão de braço

Apagando um treino

Para excluir um treino em cada cartão, criaremos a handleDeletefunção que receberá o idcomo argumento. Chamaremos a instância do Supabase para excluir um cartão de treino usando o

.delete()função. Isso .eq('id', id)especifica a idlinha a ser excluída na tabela.

 const handleDelete = async (id) => {
   try {


     const user = supabase.auth.user();
     const { data, error } = await supabase
       .from("workouts")
       .delete()
       .eq("id", id)
       .eq("user_id", user?.id);
     fetchWorkouts();
     alert("Workout deleted successfully");
   } catch (error) {
     alert(error.message);
   }
 };

O eq('user_id', user?.id)é usado para verificar se o cartão que está sendo excluído pertence a esse usuário específico. A função será passada para o WorkoutCardcomponente no index.jsarquivo e desestruturada para uso no próprio componente da seguinte forma:

const WorkoutCard = ({ data, handleDelete }) => {
 return (
   <div className={styles.workoutContainer}>
     {data?.map((item) => (
       <div key={item.id} className={styles.container}>
         <p className={styles.title}>
           {" "}
           Title: {""}
           {item.title}
         </p>
         <p className={styles.load}>
           {" "}
           Load(kg): {"  "}
           {item.loads}
         </p>
         <p className={styles.reps}>Reps:{item.reps}</p>
         <p className={styles.time}>
           created:{" "}
           {formatDistanceToNow(new Date(item.inserted_at), {
             addSuffix: true,
           })}
         </p>

         <div className={styles.buttons}>
           <Link href={`/edit/${item.id}`}>
             <a className={styles.edit}>
               <FiEdit />
             </a>
           </Link>
           <button
             onClick={() => handleDelete(item.id)}
             className={styles.delete}
           >
             <BsTrash />
           </button>
         </div>
       </div>
     ))}
   </div>
 );
};

Um brinde de alerta será exibido assim que o cartão for excluído com sucesso e o usuário será redirecionado para a página inicial.

Implantando no Vercel

Agora, temos que implantar nosso aplicativo no Vercel para que qualquer pessoa na Internet possa usá-lo!

Para implantar no Vercel, você deve primeiro enviar seu código para seu repositório, fazer login no painel do Vercel, clicar em Criar novo projeto e clicar no repositório para o qual você acabou de enviar seu código.

Insira as variáveis ​​de ambiente que criamos anteriormente junto com seus valores ( NEXT_PUBLIC_SUPABASE_URLe NEXT_PUBLIC_SUPABASE_ANON_KEY) no campo Variável de ambiente e clique em Implantar para implantar seu aplicativo na produção.

Implantar no Vercel

E aí temos que!

Conclusão

Obrigado por ler! Espero que este tutorial forneça o conhecimento necessário para criar um aplicativo full-stack usando Next.js e Supabase.

Você pode personalizar o estilo para seu caso de uso, pois este tutorial se concentra principalmente na lógica de criação de um aplicativo full-stack.

Fonte: https://blog.logrocket.com/build-full-stack-app-next-js-supabase/

    #nextjs #supabase #fullstack 

Crie Um Aplicativo Full Stack Com Next.js E Supabase

Build A Full Stack App with Next.js and Supabase

When it comes to building and choosing frameworks for your next full-stack application, combining Next.js with Supabase is one of the best options to work with in my opinion.

Supabase is an open source Firebase alternative with a lot of powerful tools, including seamless authentication. As a developer, this is key to building a successful full-stack application.

Alongside authentication, Supabase comes with other features, such as a Postgres database, real-time subscriptions, and object storage. I believe that Supabase is one of the easiest backend-as-a-services to get started or integrate with.

In this article, we will learn how to build a full-stack app using Next.js and Supabase. We’ll talk about how to set up a Supabase project, configure the UI, and implement authentication and functionalities.

The concept of this app is for users to track and create workout activities based on specified parameters, edit these activities if there are any mistakes or necessary changes, and delete them if needed. Let’s get started!

See more at: https://blog.logrocket.com/build-full-stack-app-next-js-supabase/

#nextjs #supabase #fullstack 

Build A Full Stack App with Next.js and Supabase

A Next.js Starter with TypeScript, Supabase and TailwindCSS

This project is a Next.js starter with:

  • TypeScript
  • Supabase
  • TailwindCSS

It is basically what is presented in the Supabase + Next.js quickstart, just with TypeScript, and some minimal UI with TailwindCSS.

Usage:

  • Clone this repository or use it as a template
  • Rename .env.local.template to .env.local and set the two environement variables using your Supabase configuration
  • Follow the instuctions from the Supabase + Next.js quickstart to set up the database schema.

Below, the original README generated by Next.js:

This is a Next.js project bootstrapped with create-next-app.

Getting Started

First, run the development server:

npm run dev
# or
yarn dev

Open http://localhost:3000 with your browser to see the result.

You can start editing the page by modifying pages/index.js. The page auto-updates as you edit the file.

API routes can be accessed on http://localhost:3000/api/hello. This endpoint can be edited in pages/api/hello.js.

The pages/api directory is mapped to /api/*. Files in this directory are treated as API routes instead of React pages.

Learn More

To learn more about Next.js, take a look at the following resources:

You can check out the Next.js GitHub repository - your feedback and contributions are welcome!

Deploy on Vercel

The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.

Check out our Next.js deployment documentation for more details.

Download details:

Author: scastiel
Source code: https://github.com/scastiel/nextjs-typescript-supabase-tailwind-starter?

#nextjs #react #reactjs #javascript #supabase #tailwindcss 

A Next.js Starter with TypeScript, Supabase and TailwindCSS
高橋  花子

高橋 花子

1655712000

Supabaseでのエッジ関数の使用:完全なガイド

サーバーレスコンピューティングは、ソフトウェア開発の世界で人気のあるトピックであり、それには正当な理由があります。柔軟に拡張できるアプリケーションを構築および実行するための、より効率的で費用効果の高い方法を約束します。

Supabaseはサーバーレスクラウドプラットフォームであり、開発者はサーバーなしで洗練されたWebアプリやモバイルアプリを構築できます。 Supabaseは最近、サーバーレス機能をアプリケーションに追加する簡単な方法を探している人のためのオプションとしてEdgeFunctionsを導入しました。エッジ関数はTypeScriptで記述されており、Supabase CLIを使用して29の地理的地域に配布および展開し、世界中のユーザーにリーチできます。

執筆時点では、Edge FunctionsはSupabaseでまだ実験段階であり、重大な変更が行われる可能性があります。それでも、この機能は、大量のサーバーリソースなしでより強力な機能を構築する手段として、開発者の間で急速に普及しています。

しかし、エッジ関数はどのように機能しますか?そして、サーバーレスコードをどのように記述しますか?この記事では、そのすべてとそれ以上をカバーします!

先に進む前に、まずEdge Functionsが内部でどのように機能するか、およびSupabaseが関数コードを実行するプラットフォームをどのように管理するかを調べてみましょう。

SupabaseEdgeFunctionsプラットフォームを理解する

Supabase Edge Functionsは、安全なDeno環境で実行されますこれらは、 Deno Deployがホストするサービスを使用して手動で関与する必要なしに、わずか数秒で世界中に展開されます。これはすべてSupabaseによって処理されるため、基盤となるテクノロジーを気にすることなく、アプリケーションのロジックに完全に集中できます。

Supabase Edge Functionが着信要求を受信すると、要求は最初に「リレー」に到着します。リレーはリクエストのAPIゲートウェイとして機能し、ヘッダーで渡されたJWTを認証します。また、ロギングやレート制限などの追加機能も提供します。

関数の要求を受信した後、Relayは、デプロイメントIDと呼ばれる一意の識別子とともに関数に関する情報を取得し、それをDenoDeployプラットフォームに渡します。このプラットフォームは、機能コードを安全に実行し、応答をリレーに返します。リレーは、エンドユーザーによって受信されます。

それでは、サンプルのエッジ関数を作成してデプロイしましょう。

入門

Supabase Edge Functionsの使用を開始するには、最初にSupabase CLIをインストールし、プロジェクトをセットアップする必要があります。次の手順を実行します:

  1. SupabaseCLIをインストールします。npm install -g supabase
  2. 次のコマンドを使用してCLIにログインします。supabase login
  3. 次のコマンドを使用してSupabaseプロジェクトを初期化します。supabase init
  4. 次のように、ローカルプロジェクトをリモートSupabaseプロジェクトにリンクします。supabase link --project-ref <your-project-ref>

エッジ関数の作成

Supabaseを使用して新しいエッジ関数を作成するには、プロジェクト内で次のコマンドを実行します。

supabase functions new hello

ここでは、という関数を作成していますhello

これにより、Supabaseフォルダー内にボイラープレート関数コードが作成されます/functions/hello/index.ts

import { serve } from "https://deno.land/std@0.131.0/http/server.ts";

console.log("Hello from Functions!");

serve(async (req) => {
  const { name } = await req.json();
  const data = {
    message: `Hello ${name}!`,
  };

  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" },
  });
});

上記のブロックでわかるように、デフォルトの関数コードは非常に単純で、すぐにデプロイできます。このserve関数はHTTPサーバーを作成し、着信要求のリッスンを開始します。

エッジ関数の展開

Supabaseを使用してEdgeFunctionをデプロイするには、次のコマンドを実行します。

supabase functions deploy hello

このfunctions deployコマンドは、関数コードをパッケージ化し、リモートのSupabaseプロジェクトにデプロイします。Supabaseダッシュボードの[呼び出し]で、プロジェクト名のURL(以下を参照)をクリックして詳細を確認します。

リクエストをコピーしてcurl、端末から機能をテストできます。

エッジ関数をローカルで実行する

Edge Functionをローカルで開発して実行するには、Dockerを使用してローカルマシンにSupabaseをセットアップする必要があります。システムにSupabaseを設定する際のヘルプについては、このガイドを確認してください。

次のコマンドを実行して、Supabaseプロジェクトを開始します。

supabase start

次に、次のhelloように関数を開始します。

supabase functions serve hello

このコマンドは、関数のローカルサーバーを起動し、ローカルホストポート54321でリッスンします。

関数を呼び出すにはcurl、端末からリクエストを送信します。

curl --request POST 'http://localhost:54321/functions/v1/hello' \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
  --header 'Content-Type: application/json' \
  --data '{ "name":"Vijit" }'

Bearerトークンは認証のヘッダーに必要です。プロジェクトのANONキー、SERVICE_ROLEキー、またはユーザーのJWTトークンにすることができます。

実世界のユースケース

Supabase Edge Functionsは、単純なCRUDアプリを構築するだけではありません。また、任意のデータベースに接続し、データをリアルタイムで処理し、複雑なワークフローを構築することもできます。

実際のユースケースを見てみましょう。TwilioメッセージングAPIを使用してSMSを送信するsend-message新しいエッジ関数を作成します。

関数を作成するsend-messageには、次のコマンドを実行します。

supabase functions new send-message

デフォルトの機能コードは次の場所にあります。/functions/send-message/index.ts

TwilioメッセージングAPIを使用するには、TwilioアカウントのSIDキー、認証トークン、およびSMSの送信に使用される仮想番号が必要です。

プロジェクト内にファイルを作成し、.envファイルに次の値を追加します。

// .env
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=

注意:これらのクレデンシャルをコードで公開したり、.envファイルをGitHubの履歴に追加したりしないでください。

次に、SMSペイロードを表すインターフェイスを定義します。

// ./send-message/types/sms.interface.ts 
export interface Sms {
  [index: string]: string;
  From: string;
  To: string;
  Body: string;
}

TwilioSms次に、 TwilioメッセージングAPIを使用してSMSを送信するヘルパークラスを作成します。クラスコンストラクターは、アカウントSIDと認証トークンを受け入れます。

SIDと認証トークンは一緒にエンコードされ、APIリクエストで認証ヘッダーとして渡されます。

// ./send-message/helpers/twilio-sms.ts 
import * as base64 from "https://denopkg.com/chiefbiiko/base64/mod.ts";
import { Sms } from "../types/sms.interface.ts";

export class TwilioSms {
  private authorizationHeader: string;

  constructor(private accountSID: string, authToken: string) {
    this.authorizationHeader =
      "Basic " +
      base64.fromUint8Array(
        new TextEncoder().encode(accountSID + ":" + authToken)
      );
  }

  async sendSms(payload: Sms): Promise<any> {
    const res = await fetch(
      "https://api.twilio.com/2010-04-01/Accounts/" +
        this.accountSID +
        "/Messages.json",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
          Authorization: this.authorizationHeader,
        },
        body: new URLSearchParams(payload).toString(),
      }
    );

    const data = await res.json();

    return data;
  }
}

メインの関数ハンドラーで、メソッドを使用して環境変数をロードし、ヘルパーからクラスDeno.env.get()をインポートする必要があります。TwilioSms

次に、このsendSms()メソッドを使用して、リクエスト本文で指定された特定の携帯電話番号にテキストメッセージを送信します。

// ./send-message/index.ts 
import { serve } from "https://deno.land/std@0.131.0/http/server.ts";

import { TwilioSms } from "./helpers/twilio-sms.ts";

const accountSid = Deno.env.get("TWILIO_ACCOUNT_SID") || "";
const authToken = Deno.env.get("TWILIO_AUTH_TOKEN") || "";
const fromMobile = Deno.env.get("TWILIO_PHONE_NUMBER") || "";

serve(async (req) => {
  const { textMessage, toMobile } = await req.json();

  const twilioClient = new TwilioSms(accountSid, authToken);

  const message = await twilioClient.sendSms({
    Body: textMessage,
    From: fromMobile,
    To: toMobile,
  });

  console.log({ message });

  const data = {
    isSuccess: false,
  };

  if (message.status === "queued") {
    data.isSuccess = true;
  }

  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" },
  });
});

関数をローカルでテストするには、supabase functions serveコマンドを実行.envし、パラメーターにファイルパスを渡して--env-file、関数が環境変数にアクセスできるようにします。

supabase functions serve send-message --env-file ./supabase/.env

次に、コマンドを使用しcurlて関数を呼び出します。

curl -i --location --request POST 'http://localhost:54321/functions/v1/' \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
  --header 'Content-Type: application/json' \
  --data '{ "textMessage":"Hello Developer!", "toMobile": "+91XXXXXXXXXX" }'

ローカル環境変数をリモートSupabaseプロジェクトにプッシュするには、次のsupabase secrets setコマンドを実行します。

supabase secrets set --env-file ./supabase/.env

関数がローカルでテストされ、デプロイする準備ができたら、次のsupabase functions deployコマンドを実行します。

supabase functions deploy send-message

制限事項

Supabase Edge Functionsには多くの利点がありますが、この新機能で何ができるかについてはまだいくつかの制限があります。データ集約的で実行に時間がかかる可能性のあるサービスの場合は、Supabaseのデータベース関数を使用することを選択する必要があります。

執筆時点では、Edge Functionsはまだ実験段階であり、将来のアップデートで重大な変更が行われる予定です。現在、Edge Functionsは、ポート25、465、または587へのアウトバウンド接続を確立できません。また、この機能はPOSTリクエストのみをサポートし、HTMLレスポンスはサポートしません。最後に、ローカル開発では一度に1つのエッジ関数のみを提供できます。

結論

Supabase Edge Functionsは、アプリの機能を拡張するための優れたソリューションです。それらを使用することで、通常は別のサーバー側アプリケーションを必要とする機能をアプリに追加できます。

この記事では、Supabase Edge Functionsがどのように機能するかを調査し、Edge Functionを作成、デプロイ、および実行する方法について説明しました。また、Edge Functionsを使用してサーバーなしでSMSを送信する、実際のユースケースについても説明しました。

インフラストラクチャを自分で管理せずにアプリにカスタム機能を追加する方法を探している場合は、SupabaseEdgeFunctionsをチェックする価値があります。この記事が、SupabaseとEdgeFunctionsを使用して独自のアプリケーションを構築するのに役立つことを願っています。

このストーリーは、もともとhttps://blog.logrocket.com/using-edge-functions-supabase-complete-guide/で公開されました

#edge #supabase 

Supabaseでのエッジ関数の使用:完全なガイド
Diego  Elizondo

Diego Elizondo

1655712000

Uso De Funciones Edge En Supabase: Una Guía Completa

La computación sin servidor es un tema popular en el mundo del desarrollo de software, ¡y por una buena razón! Promete una forma más eficiente y rentable de crear y ejecutar aplicaciones que escalan de manera elástica.

Supabase es una plataforma en la nube sin servidor que permite a los desarrolladores crear aplicaciones web y móviles sofisticadas sin servidores. Supabase ha presentado recientemente Edge Functions  como una opción para aquellos que buscan una manera fácil de agregar funciones sin servidor a sus aplicaciones. Las funciones Edge están escritas en TypeScript y se pueden distribuir e implementar con Supabase CLI en 29 regiones geográficas para llegar a usuarios de todo el mundo.

En el momento de redactar este informe, las funciones de borde aún son experimentales en Supabase y es probable que haya cambios importantes. Aún así, esta función se está volviendo popular rápidamente entre los desarrolladores como un medio para crear funciones más potentes sin recursos masivos del servidor.

Pero, ¿cómo funcionan las funciones Edge? Y, ¿cómo se escribe código sin servidor? ¡En este artículo, cubriremos todo eso y más!

Antes de continuar, primero exploremos cómo funcionan las funciones de Edge bajo el capó y cómo Supabase administra la plataforma que ejecuta el código de la función.

Descripción de la plataforma Supabase Edge Functions

Las funciones de borde de Supabase se ejecutan en el entorno seguro de Deno . Se implementan en todo el mundo en cuestión de segundos sin necesidad de intervención manual mediante el servicio alojado Deno Deploy . Todo esto lo maneja Supabase, por lo que puede concentrarse completamente en la lógica de su aplicación sin preocuparse por las tecnologías subyacentes.

Cuando una función de borde de Supabase recibe una solicitud entrante, la solicitud llega primero a un "relé". El relé actúa como una puerta de enlace API para la solicitud y autentica el JWT pasado en los encabezados. También proporciona algunas funcionalidades adicionales como el registro y la limitación de velocidad.

Después de recibir la solicitud de una función, Relay recupera información sobre la función junto con un identificador único, llamado Id. de implementación, y lo pasa a la plataforma Deno Deploy. Esta plataforma ejecuta de forma segura el código de función y devuelve la respuesta al relé, que luego es recibida por el usuario final.

Ahora, creemos e implementemos una función Edge de muestra.

Empezando

Para comenzar con Supabase Edge Functions, primero deberá instalar Supabase CLI y configurar un proyecto. Sigue estos pasos:

  1. Instale la CLI de Supabase:npm install -g supabase
  2. Inicie sesión en la CLI usando el comando:supabase login
  3. Inicialice el proyecto Supabase con el comando:supabase init
  4. Vincule su proyecto local al proyecto Supabase remoto, así:supabase link --project-ref <your-project-ref>

Creación de funciones de borde

Para crear una nueva función Edge con Supabase, ejecute el siguiente comando dentro de su proyecto:

supabase functions new hello

Aquí, estamos creando una función llamada hello.

Esto crea un código de función repetitivo dentro de su carpeta Supabase en: /functions/hello/index.ts.

import { serve } from "https://deno.land/std@0.131.0/http/server.ts";

console.log("Hello from Functions!");

serve(async (req) => {
  const { name } = await req.json();
  const data = {
    message: `Hello ${name}!`,
  };

  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" },
  });
});

Como puede ver en el bloque anterior, el código de función predeterminado es bastante simple y está listo para implementarse. La servefunción crea un servidor HTTP y comienza a escuchar las solicitudes entrantes.

Implementación de funciones perimetrales

Para implementar una función Edge con Supabase, ejecute el siguiente comando:

supabase functions deploy hello

El functions deploycomando empaquetará su código de función y lo implementará en el proyecto Supabase remoto. En el panel de control de Supabase, en Invocar, haga clic en la URL con el nombre de su proyecto (ver más abajo) para encontrar más detalles.

Puede copiar la curlsolicitud para probar su función desde la terminal.

Ejecutar funciones perimetrales localmente

Para desarrollar y ejecutar Edge Function localmente, deberá usar Docker para configurar Supabase en su máquina local. Puede consultar esta guía para obtener ayuda para configurar Supabase en su sistema.

Inicie el proyecto Supabase ejecutando el siguiente comando:

supabase start

A continuación, inicie la hellofunción, así:

supabase functions serve hello

Este comando iniciará un servidor local para la función y escuchará en el puerto localhost 54321.

Para invocar la función, haga una curlsolicitud desde su terminal:

curl --request POST 'http://localhost:54321/functions/v1/hello' \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
  --header 'Content-Type: application/json' \
  --data '{ "name":"Vijit" }'

El Bearertoken se requiere en el encabezado para la autenticación. Puede ser la clave de su proyecto ANON, la SERVICE_ROLEclave o el token JWT de un usuario.

Caso de uso del mundo real

Supabase Edge Functions puede ayudarlo a hacer más que simplemente crear una aplicación CRUD simple; también le permiten conectarse a cualquier base de datos, procesar datos en tiempo real e incluso crear flujos de trabajo complejos.

Echemos un vistazo a un caso de uso práctico. Crearemos una nueva función Edge send-message, que enviará un SMS mediante la API de mensajería de Twilio .

Para crear la send-messagefunción, ejecute el siguiente comando:

supabase functions new send-message

Encontrará el código de función predeterminado en/functions/send-message/index.ts

Para utilizar la API de mensajería de Twilio, necesitará la clave SID de la cuenta de Twilio, un token de autenticación y un número virtual que se utilizará para enviar el SMS.

Cree un .envarchivo dentro del proyecto y agregue los siguientes valores al archivo:

// .env
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=

NB, asegúrese de no exponer estas credenciales en su código o agregar el .envarchivo a su historial de GitHub.

A continuación, defina una interfaz para representar la carga útil de SMS:

// ./send-message/types/sms.interface.ts 
export interface Sms {
  [index: string]: string;
  From: string;
  To: string;
  Body: string;
}

Ahora, cree una clase auxiliar TwilioSmspara enviar un SMS utilizando la API de mensajería de Twilio. El constructor de la clase aceptará el SID de la cuenta y el token de autenticación.

El SID y los tokens de autenticación se codifican juntos y se pasarán como un encabezado de autorización en la solicitud de la API.

// ./send-message/helpers/twilio-sms.ts 
import * as base64 from "https://denopkg.com/chiefbiiko/base64/mod.ts";
import { Sms } from "../types/sms.interface.ts";

export class TwilioSms {
  private authorizationHeader: string;

  constructor(private accountSID: string, authToken: string) {
    this.authorizationHeader =
      "Basic " +
      base64.fromUint8Array(
        new TextEncoder().encode(accountSID + ":" + authToken)
      );
  }

  async sendSms(payload: Sms): Promise<any> {
    const res = await fetch(
      "https://api.twilio.com/2010-04-01/Accounts/" +
        this.accountSID +
        "/Messages.json",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
          Authorization: this.authorizationHeader,
        },
        body: new URLSearchParams(payload).toString(),
      }
    );

    const data = await res.json();

    return data;
  }
}

En el controlador de función principal, deberá cargar las variables de entorno utilizando el Deno.env.get()método e importar la TwilioSmsclase de los ayudantes.

A continuación, utilice el sendSms()método para enviar el mensaje de texto al número de móvil especificado en el cuerpo de la solicitud.

// ./send-message/index.ts 
import { serve } from "https://deno.land/std@0.131.0/http/server.ts";

import { TwilioSms } from "./helpers/twilio-sms.ts";

const accountSid = Deno.env.get("TWILIO_ACCOUNT_SID") || "";
const authToken = Deno.env.get("TWILIO_AUTH_TOKEN") || "";
const fromMobile = Deno.env.get("TWILIO_PHONE_NUMBER") || "";

serve(async (req) => {
  const { textMessage, toMobile } = await req.json();

  const twilioClient = new TwilioSms(accountSid, authToken);

  const message = await twilioClient.sendSms({
    Body: textMessage,
    From: fromMobile,
    To: toMobile,
  });

  console.log({ message });

  const data = {
    isSuccess: false,
  };

  if (message.status === "queued") {
    data.isSuccess = true;
  }

  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" },
  });
});

Para probar la función localmente, ejecute el supabase functions servecomando y pase la .envruta del archivo en el --env-fileparámetro para que la función pueda acceder a las variables de entorno.

supabase functions serve send-message --env-file ./supabase/.env

Ahora, use el curlcomando para invocar la función.

curl -i --location --request POST 'http://localhost:54321/functions/v1/' \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
  --header 'Content-Type: application/json' \
  --data '{ "textMessage":"Hello Developer!", "toMobile": "+91XXXXXXXXXX" }'

Para enviar sus variables de entorno locales al proyecto Supabase remoto, ejecute el supabase secrets setcomando:

supabase secrets set --env-file ./supabase/.env

Una vez que su función se pruebe localmente y esté lista para implementarse, ejecute el supabase functions deploycomando:

supabase functions deploy send-message

Limitaciones

Las funciones de Supabase Edge brindan muchos beneficios, pero aún existen algunas limitaciones en cuanto a lo que puede hacer con esta nueva característica. Para los servicios que consumen muchos datos y su ejecución puede llevar mucho tiempo, debe optar por utilizar las funciones de base de datos de Supabase .

En el momento de escribir este artículo, las funciones de Edge aún son experimentales y habrá cambios importantes en futuras actualizaciones. Edge Functions no puede realizar conexiones salientes a los puertos 25, 465 o 587 en este momento. Además, esta función solo admite solicitudes POST y no respuestas HTML. Por último, solo se puede servir una función perimetral a la vez en el desarrollo local.

Conclusión

Supabase Edge Functions es una excelente solución para ampliar la funcionalidad de su aplicación. Al usarlos, puede agregar funciones a su aplicación que normalmente requerirían una aplicación del lado del servidor separada.

En este artículo, investigamos cómo funcionan las funciones Edge de Supabase y observamos cómo crear, implementar y ejecutar una función Edge. También analizamos un caso de uso del mundo real, utilizando Edge Functions para enviar un SMS sin un servidor.

Si está buscando una manera de agregar funciones personalizadas a su aplicación sin tener que administrar la infraestructura usted mismo, definitivamente vale la pena echarle un vistazo a Supabase Edge Functions. Espero que este artículo lo ayude a comenzar a usar Supabase y Edge Functions para crear sus propias aplicaciones.

Esta historia se publicó originalmente en https://blog.logrocket.com/using-edge-functions-supabase-complete-guide/

#edge #supabase 

Uso De Funciones Edge En Supabase: Una Guía Completa
John David

John David

1653535747

Supabase: The Open Source Firebase Alternative

Supabase

Supabase is an open source Firebase alternative. We're building the features of Firebase using enterprise-grade open source tools.

  • Hosted Postgres Database. Docs
  • Authentication and Authorization. Docs
  • Auto-generated APIs.
  • Functions.
    • Database Functions. Docs
    • Edge Functions Docs
  • File Storage. Docs
  • Dashboard

Supabase Dashboard

Documentation

For full documentation, visit supabase.com/docs

To see how to Contribute, visit Getting Started

Community & Support

  • Community Forum. Best for: help with building, discussion about database best practices.
  • GitHub Issues. Best for: bugs and errors you encounter using Supabase.
  • Email Support. Best for: problems with your database or infrastructure.
  • Discord. Best for: sharing your applications and hanging out with the community.

Status

  • Alpha: We are testing Supabase with a closed set of customers
  • Public Alpha: Anyone can sign up over at app.supabase.io. But go easy on us, there are a few kinks
  • Public Beta: Stable enough for most non-enterprise use-cases
  • Public: Production-ready

We are currently in Public Beta. Watch "releases" of this repo to get notified of major updates.

Watch this repo


How it works

Supabase is a combination of open source tools. We’re building the features of Firebase using enterprise-grade, open source products. If the tools and communities exist, with an MIT, Apache 2, or equivalent open license, we will use and support that tool. If the tool doesn't exist, we build and open source it ourselves. Supabase is not a 1-to-1 mapping of Firebase. Our aim is to give developers a Firebase-like developer experience using open source tools.

Architecture

Supabase is a hosted platform. You can sign up and start using Supabase without installing anything. You can also self-host and develop locally.

Architecture

  • PostgreSQL is an object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance.
  • Realtime is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Realtime polls Postgres' built-in replication functionality for database changes, converts changes to JSON, then broadcasts the JSON over websockets to authorized clients.
  • PostgREST is a web server that turns your PostgreSQL database directly into a RESTful API
  • Storage provides a RESTful interface for managing Files stored in S3, using Postgres to manage permissions.
  • postgres-meta is a RESTful API for managing your Postgres, allowing you to fetch tables, add roles, and run queries, etc.
  • GoTrue is an SWT based API for managing users and issuing SWT tokens.
  • Kong is a cloud-native API gateway.

Client libraries

Our approach for client libraries is modular. Each sub-library is a standalone implementation for a single external system. This is one of the ways we support existing tools.

LanguageClientFeature-Clients (bundled in Supabase client)
 SupabasePostgRESTGoTrueRealtimeStorage
⚡️ Official ⚡️
JavaScript (TypeScript)supabase-jspostgrest-jsgotrue-jsrealtime-jsstorage-js
💚 Community 💚
C#supabase-csharppostgrest-csharpgotrue-csharprealtime-csharpstorage-csharp
Dart (Flutter)supabase-dartpostgrest-dartgotrue-dartrealtime-dartstorage-dart
Go-postgrest-go---
Java--gotrue-java--
Kotlin-postgrest-ktgotrue-kt--
Pythonsupabase-pypostgrest-pygotrue-pyrealtime-py-
Rubysupabase-rbpostgrest-rb---
Rust-postgrest-rs---
Swiftsupabase-swiftpostgrest-swiftgotrue-swiftrealtime-swiftstorage-swift

 

Translations

Download Details: 
Author: supabase
Source Code: https://github.com/supabase/supabase 
License: Apache-2.0 license
#supabase #firebase 

Supabase: The Open Source Firebase Alternative
Felix Kling

Felix Kling

1653535683

What Is Supabase?

Supabase

Supabase is an open source Firebase alternative. We're building the features of Firebase using enterprise-grade open source tools.

  • Hosted Postgres Database. Docs
  • Authentication and Authorization. Docs
  • Auto-generated APIs.
  • Functions.
    • Database Functions. Docs
    • Edge Functions Docs
  • File Storage. Docs
  • Dashboard

Supabase Dashboard

Documentation

For full documentation, visit supabase.com/docs

To see how to Contribute, visit Getting Started

Community & Support

  • Community Forum. Best for: help with building, discussion about database best practices.
  • GitHub Issues. Best for: bugs and errors you encounter using Supabase.
  • Email Support. Best for: problems with your database or infrastructure.
  • Discord. Best for: sharing your applications and hanging out with the community.

Status

  • Alpha: We are testing Supabase with a closed set of customers
  • Public Alpha: Anyone can sign up over at app.supabase.io. But go easy on us, there are a few kinks
  • Public Beta: Stable enough for most non-enterprise use-cases
  • Public: Production-ready

We are currently in Public Beta. Watch "releases" of this repo to get notified of major updates.

Watch this repo


How it works

Supabase is a combination of open source tools. We’re building the features of Firebase using enterprise-grade, open source products. If the tools and communities exist, with an MIT, Apache 2, or equivalent open license, we will use and support that tool. If the tool doesn't exist, we build and open source it ourselves. Supabase is not a 1-to-1 mapping of Firebase. Our aim is to give developers a Firebase-like developer experience using open source tools.

Architecture

Supabase is a hosted platform. You can sign up and start using Supabase without installing anything. You can also self-host and develop locally.

Architecture

  • PostgreSQL is an object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance.
  • Realtime is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Realtime polls Postgres' built-in replication functionality for database changes, converts changes to JSON, then broadcasts the JSON over websockets to authorized clients.
  • PostgREST is a web server that turns your PostgreSQL database directly into a RESTful API
  • Storage provides a RESTful interface for managing Files stored in S3, using Postgres to manage permissions.
  • postgres-meta is a RESTful API for managing your Postgres, allowing you to fetch tables, add roles, and run queries, etc.
  • GoTrue is an SWT based API for managing users and issuing SWT tokens.
  • Kong is a cloud-native API gateway.

Client libraries

Our approach for client libraries is modular. Each sub-library is a standalone implementation for a single external system. This is one of the ways we support existing tools.

LanguageClientFeature-Clients (bundled in Supabase client)
 SupabasePostgRESTGoTrueRealtimeStorage
⚡️ Official ⚡️
JavaScript (TypeScript)supabase-jspostgrest-jsgotrue-jsrealtime-jsstorage-js
💚 Community 💚
C#supabase-csharppostgrest-csharpgotrue-csharprealtime-csharpstorage-csharp
Dart (Flutter)supabase-dartpostgrest-dartgotrue-dartrealtime-dartstorage-dart
Go-postgrest-go---
Java--gotrue-java--
Kotlin-postgrest-ktgotrue-kt--
Pythonsupabase-pypostgrest-pygotrue-pyrealtime-py-
Rubysupabase-rbpostgrest-rb---
Rust-postgrest-rs---
Swiftsupabase-swiftpostgrest-swiftgotrue-swiftrealtime-swiftstorage-swift

 

Translations

Download Details: 
Author: supabase
Source Code: https://github.com/supabase/supabase 
License: Apache-2.0 license
#supabase #firebase 

What Is Supabase?
Jacob Banks

Jacob Banks

1652947771

How to Build the Reddit 2.0 CLONE with REACT & NEXT.js

Let's build Reddit 2.0 with NEXT.JS! (React, SQL, Supabase, StepZen, GraphQL, TypeScript,Tailwind)

The much anticipated build is FINALLY HERE! 
Join me as I build the Reddit 2.0 CLONE with REACT & NEXT.js, you'll learn how to do the following in this build:

👉 You’ll learn how to create a GraphQL backend directly connected to your PostgreSQL Supabase database effortlessly with Stepzen!
👉 You’ll be able to build a Login authentication flow with the Reddit API using NextAuth
👉 You’ll learn how to implement your first SQL relational database (Including how to use Primary & Foreign Keys)!
👉 You’ll learn how to write robust code with Typescript!
👉 You’ll learn how to leverage Next.js Server Side Rendering to get a FASTER page load time!
👉 You’ll learn to create your own API backend endpoints to communicate safely with your frontend!
👉 You’ll learn about Tailwind CSS and how to build this awesome Responsive website!
👉 You’ll be able to Deploy to Vercel and have the site online by the end of this tutorial!
+ SO MUCH MORE!

🖥️ CODE
Get the code for my builds here: https://links.papareact.com/github 

🕐 TIMESTAMPS:
[COMING SOON]

#reactjs #nextjs #typescript #tailwindcss #postgresql #sql #databases #stepzen #graphql #supabase

How to Build the Reddit 2.0 CLONE with REACT & NEXT.js