# Supabase Integration

A comprehensive npm package to integrate Supabase with JavaScript, TypeScript, and React. This package provides ready-to-use services for authentication and basic CRUD operations on your Supabase database.

## Features

- **Authentication:** Sign up, sign in, sign out, OAuth integration (e.g., Google, GitHub).
- **Database Operations:** Create, Read, Update, Delete (CRUD) operations on your Supabase tables.
- **Call Postgres Functions:** Execute stored procedures in your Supabase database.

## Installation

```bash
npm install supabase-integration
```

## Usage

### Initialization

Before using the package, you need to initialize it with your Supabase project credentials. Replace `'https://your-supabase-url.supabase.co'` and `'your-anon-key'` with your actual Supabase URL and anonymous key.

```javascript
// JavaScript or TypeScript
import SupabaseIntegration from "supabase-integration";

const supabase = new SupabaseIntegration({
  supabaseUrl: "https://your-supabase-url.supabase.co",
  supabaseKey: "your-anon-key",
});
```

```jsx
// React (typically done in a separate file like supabaseClient.js or supabaseClient.ts)
import SupabaseIntegration from "supabase-integration";

const supabase = new SupabaseIntegration({
  supabaseUrl: "https://your-supabase-url.supabase.co",
  supabaseKey: "your-anon-key",
});

export default supabase;
```

---

### JavaScript

#### Authentication

**Sign Up**

```javascript
const { data, error } = await supabase.auth.signUp({
  email: "user@example.com",
  password: "securepassword",
});

if (error) {
  console.error("Sign Up Error:", error.message);
} else {
  console.log("Sign Up Success:", data);
}
```

**Sign In**

```javascript
const { data, error } = await supabase.auth.signIn({
  email: "user@example.com",
  password: "securepassword",
});

if (error) {
  console.error("Sign In Error:", error.message);
} else {
  console.log("Sign In Success:", data);
}
```

**Sign In with OAuth**

```javascript
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: "google", // e.g., 'google', 'github'
  redirectTo: "https://your-app.com/welcome",
  scopes: "openid email profile",
  queryParams: { prompt: "consent" },
});

if (error) {
  console.error("OAuth Sign In Error:", error.message);
} else {
  console.log("OAuth Sign In Success:", data);
  // The user will be redirected to the OAuth provider's sign-in page.
}
```

**Sign Out**

```javascript
const { error } = await supabase.auth.signOut();

if (error) {
  console.error("Sign Out Error:", error.message);
} else {
  console.log("Sign Out Success");
}
```

#### CRUD Operations

**Create (Insert)**

```javascript
const { data: insertData, error: insertError } =
  await supabase.database.insertData(
    "users",
    {
      email: "newuser@example.com",
      name: "New User",
    },
    { returnData: true }
  );

if (insertError) {
  console.error("Insert Error:", insertError.message);
} else {
  console.log("Insert Success:", insertData);
}
```

**Read (Fetch)**

```javascript
const { data: fetchData, error: fetchError } =
  await supabase.database.fetchData("users", {
    select: "id, email, name",
    order: "id:asc",
    limit: 10,
  });

if (fetchError) {
  console.error("Fetch Error:", fetchError.message);
} else {
  console.log("Fetch Success:", fetchData);
}
```

**Update**

```javascript
const { data: updateData, error: updateError } =
  await supabase.database.updateData(
    "users",
    { name: "Updated User" },
    { id: 1 },
    { returnData: true }
  );

if (updateError) {
  console.error("Update Error:", updateError.message);
} else {
  console.log("Update Success:", updateData);
}
```

**Delete**

```javascript
const { data: deleteData, error: deleteError } =
  await supabase.database.deleteData("users", { id: 1 }, { returnData: true });

if (deleteError) {
  console.error("Delete Error:", deleteError.message);
} else {
  console.log("Delete Success:", deleteData);
}
```

**Call a Postgres Function**

```javascript
const { data, error } = await supabase.database.callFunction(
  "your_function_name",
  {
    arg1: "value1",
    arg2: "value2",
  }
);

if (error) {
  console.error("Function Call Error:", error.message);
} else {
  console.log("Function Call Success:", data);
}
```

---

### TypeScript

Using the package with TypeScript provides enhanced type safety and IntelliSense support.

#### Initialization

```typescript
import SupabaseIntegration from "supabase-integration";

interface SupabaseConfig {
  supabaseUrl: string;
  supabaseKey: string;
}

const config: SupabaseConfig = {
  supabaseUrl: "https://your-supabase-url.supabase.co",
  supabaseKey: "your-anon-key",
};

const supabase = new SupabaseIntegration(config);
```

#### Authentication

**Sign Up**

```typescript
interface SignUpResponse {
  data: {
    user: {
      id: string;
      email: string;
      // other user fields
    };
    session: any; // Define more specific types as needed
  } | null;
  error: any; // Define more specific error types as needed
}

const { data, error }: SignUpResponse = await supabase.auth.signUp({
  email: "user@example.com",
  password: "securepassword",
});

if (error) {
  console.error("Sign Up Error:", error.message);
} else {
  console.log("Sign Up Success:", data);
}
```

**Sign In**

```typescript
interface SignInResponse {
  data: {
    session: any; // Define more specific types as needed
    user: {
      id: string;
      email: string;
      // other user fields
    };
  } | null;
  error: any;
}

const { data, error }: SignInResponse = await supabase.auth.signIn({
  email: "user@example.com",
  password: "securepassword",
});

if (error) {
  console.error("Sign In Error:", error.message);
} else {
  console.log("Sign In Success:", data);
}
```

**Sign In with OAuth**

```typescript
interface OAuthOptions {
  provider: "google" | "github" | string;
  redirectTo?: string;
  scopes?: string;
  queryParams?: Record<string, string>;
}

const oauthOptions: OAuthOptions = {
  provider: "github",
  redirectTo: "https://your-app.com/welcome",
};

const { data, error } = await supabase.auth.signInWithOAuth(oauthOptions);

if (error) {
  console.error("OAuth Sign In Error:", error.message);
} else {
  console.log("OAuth Sign In Success:", data);
}
```

**Sign Out**

```typescript
const { error } = await supabase.auth.signOut();

if (error) {
  console.error("Sign Out Error:", error.message);
} else {
  console.log("Sign Out Success");
}
```

#### CRUD Operations

**Create (Insert)**

```typescript
interface User {
  id: string;
  email: string;
  name: string;
}

interface InsertResponse {
  data: User[] | null;
  error: any;
}

const { data: insertData, error: insertError }: InsertResponse =
  await supabase.database.insertData(
    "users",
    {
      email: "newuser@example.com",
      name: "New User",
    },
    { returnData: true }
  );

if (insertError) {
  console.error("Insert Error:", insertError.message);
} else {
  console.log("Insert Success:", insertData);
}
```

**Read (Fetch)**

```typescript
interface FetchResponse<T> {
  data: T[] | null;
  error: any;
}

interface User {
  id: string;
  email: string;
  name: string;
}

const { data: fetchData, error: fetchError }: FetchResponse<User> =
  await supabase.database.fetchData("users", {
    select: "id, email, name",
    order: "id:asc",
    limit: 10,
  });

if (fetchError) {
  console.error("Fetch Error:", fetchError.message);
} else {
  console.log("Fetch Success:", fetchData);
}
```

**Update**

```typescript
interface UpdateResponse<T> {
  data: T[] | null;
  error: any;
}

interface User {
  id: string;
  email: string;
  name: string;
}

const { data: updateData, error: updateError }: UpdateResponse<User> =
  await supabase.database.updateData(
    "users",
    { name: "Updated User" },
    { id: "1" },
    { returnData: true }
  );

if (updateError) {
  console.error("Update Error:", updateError.message);
} else {
  console.log("Update Success:", updateData);
}
```

**Delete**

```typescript
interface DeleteResponse<T> {
  data: T[] | null;
  error: any;
}

interface User {
  id: string;
  email: string;
  name: string;
}

const { data: deleteData, error: deleteError }: DeleteResponse<User> =
  await supabase.database.deleteData(
    "users",
    { id: "1" },
    { returnData: true }
  );

if (deleteError) {
  console.error("Delete Error:", deleteError.message);
} else {
  console.log("Delete Success:", deleteData);
}
```

**Call a Postgres Function**

```typescript
interface FunctionResponse {
  data: any;
  error: any;
}

const { data, error }: FunctionResponse = await supabase.database.callFunction(
  "your_function_name",
  {
    arg1: "value1",
    arg2: "value2",
  }
);

if (error) {
  console.error("Function Call Error:", error.message);
} else {
  console.log("Function Call Success:", data);
}
```

---

### React

Integrating `supabase-integration` with React allows you to manage authentication and CRUD operations seamlessly within your React components. Below are the steps and examples to help you get started.

#### 1. **Initialize Supabase in a Separate File**

Create a file named `supabaseClient.js` or `supabaseClient.ts` in your project's `src` directory.

```javascript
// src/supabaseClient.js

import SupabaseIntegration from "supabase-integration";

const supabase = new SupabaseIntegration({
  supabaseUrl: "https://your-supabase-url.supabase.co",
  supabaseKey: "your-anon-key",
});

export default supabase;
```

#### 2. **Set Up a React Context (Optional but Recommended)**

Using React Context allows you to provide the `supabase` instance throughout your application without prop drilling.

```jsx
// src/contexts/SupabaseContext.jsx

import React, { createContext, useContext, useState, useEffect } from "react";
import supabase from "../supabaseClient";

const SupabaseContext = createContext();

export const SupabaseProvider = ({ children }) => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // Listen to authentication state changes
    const unsubscribe = supabase.auth.onAuthStateChange((event, session) => {
      setUser(session?.user || null);
    });

    // Fetch the current session
    supabase.auth.fetchData().then(({ data }) => {
      setUser(data?.user || null);
    });

    return () => {
      unsubscribe();
    };
  }, []);

  return (
    <SupabaseContext.Provider value={{ supabase, user }}>
      {children}
    </SupabaseContext.Provider>
  );
};

export const useSupabase = () => {
  return useContext(SupabaseContext);
};
```

#### 3. **Wrap Your Application with the SupabaseProvider**

```jsx
// src/index.jsx

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { SupabaseProvider } from "./contexts/SupabaseContext";

ReactDOM.render(
  <React.StrictMode>
    <SupabaseProvider>
      <App />
    </SupabaseProvider>
  </React.StrictMode>,
  document.getElementById("root")
);
```

#### 4. **Use Supabase in Your Components**

**AuthButtons Component**

```jsx
// src/components/AuthButtons.jsx

import React from "react";
import { useSupabase } from "../contexts/SupabaseContext";

const AuthButtons = () => {
  const { supabase } = useSupabase();

  const handleGoogleSignIn = async () => {
    const { data, error } = await supabase.auth.signInWithOAuth({
      provider: "google",
      redirectTo: "https://your-app.com/welcome",
    });

    if (error) {
      console.error("OAuth Sign In Error:", error.message);
      alert(`Sign in failed: ${error.message}`);
    } else {
      console.log("OAuth Sign In Success:", data);
      // The user will be redirected to the OAuth provider's sign-in page.
    }
  };

  const handleSignOut = async () => {
    const { error } = await supabase.auth.signOut();

    if (error) {
      console.error("Sign Out Error:", error.message);
      alert(`Sign out failed: ${error.message}`);
    } else {
      console.log("Sign Out Success");
    }
  };

  return (
    <div>
      <button onClick={handleGoogleSignIn}>Sign In with Google</button>
      <button onClick={handleSignOut}>Sign Out</button>
    </div>
  );
};

export default AuthButtons;
```

**App Component**

```jsx
// src/App.jsx

import React from "react";
import { useSupabase } from "./contexts/SupabaseContext";
import AuthButtons from "./components/AuthButtons";

const App = () => {
  const { user } = useSupabase();

  return (
    <div>
      <h1>Supabase Integration Example</h1>
      {user ? <p>Welcome, {user.email}</p> : <p>Please sign in</p>}
      <AuthButtons />
    </div>
  );
};

export default App;
```

**CRUD Operations Example**

```jsx
// src/components/UserList.jsx

import React, { useEffect, useState } from "react";
import { useSupabase } from "../contexts/SupabaseContext";

const UserList = () => {
  const { supabase } = useSupabase();
  const [users, setUsers] = useState([]);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUsers = async () => {
      const { data, error } = await supabase.database.fetchData("users", {
        select: "id, email, name",
        order: "id:asc",
        limit: 10,
      });

      if (error) {
        setError(error.message);
      } else {
        setUsers(data);
      }
    };

    fetchUsers();
  }, [supabase]);

  if (error) {
    return <p>Error fetching users: {error}</p>;
  }

  return (
    <div>
      <h2>User List</h2>
      {users.length === 0 ? (
        <p>No users found.</p>
      ) : (
        <ul>
          {users.map((user) => (
            <li key={user.id}>
              {user.email} - {user.name}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default UserList;
```

---

## Configuration

This package does **not** rely on environment files (`.env`). Instead, configuration values are passed directly through the `SupabaseIntegration` constructor as shown in the initialization examples above.

---

## Contributing

Contributions are welcome! Please open an issue or submit a pull request for any improvements or bug fixes.

---

## License

[MIT](LICENSE)

---

## Example Project Structure

After integrating with React, your project structure might look like this:

```
your-react-app/
├── src/
│   ├── components/
│   │   ├── AuthButtons.jsx
│   │   └── UserList.jsx
│   ├── contexts/
│   │   └── SupabaseContext.jsx
│   ├── supabaseClient.js
│   ├── App.jsx
│   └── index.jsx
├── package.json
├── tsconfig.json
├── README.md
└── .gitignore
```
