Building a Fullstack SaaS with React, Supabase, and Stripe
Introduction
Building a SaaS application from scratch can be daunting. This article simplifies the process by guiding you through building a basic SaaS with React, Supabase, and Stripe. React handles the user interface, Supabase provides a scalable backend, and Stripe manages payments.
Setting up Supabase
First, create a new project on Supabase. You'll need your Supabase URL and anon key. Store these in your .env
file:
SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
Create a users
table with at least an id
(UUID, primary key) and email
column. Enable Row Level Security (RLS) and configure policies to allow users to only access their own data.
React Frontend with Authentication
Use create-react-app
or your preferred React setup. Install @supabase/supabase-js
:
npm install @supabase/supabase-js
Create a supabaseClient.js
:
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.REACT_APP_SUPABASE_URL;
const supabaseAnonKey = process.env.REACT_APP_SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
Implement authentication using Supabase's auth
methods. Here's a basic sign-up component:
import { useState } from 'react';
import { supabase } from './supabaseClient';
function SignUp() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
const { data, error } = await supabase.auth.signUp({
email: email,
password: password,
});
if (error) {
console.error('Error signing up:', error);
} else {
console.log('Sign up successful:', data);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} />
<input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} />
<button type="submit">Sign Up</button>
</form>
);
}
export default SignUp;
Integrating Stripe for Payments
Install stripe
and @stripe/react-stripe-js
:
npm install @stripe/stripe-js @stripe/react-stripe-js
Create a Stripe account and obtain your Publishable Key and Secret Key. Define your pricing plans within Stripe.
On your server (e.g., a Supabase Edge Function or a Node.js API), create an endpoint that uses the Stripe Secret Key to create a Stripe Checkout Session. This session will redirect the user to Stripe to enter their payment information.
Example (using Node.js and Express):
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const express = require('express');
const app = express();
app.post('/create-checkout-session', async (req, res) => {
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: 'YOUR_STRIPE_PRICE_ID', // Replace with your actual Price ID
quantity: 1,
},
],
mode: 'subscription',
success_url: `${YOUR_DOMAIN}/success`,
cancel_url: `${YOUR_DOMAIN}/cancel`,
});
res.redirect(303, session.url);
});
app.listen(4242, () => console.log('Running on port 4242'));
In your React app, use the @stripe/react-stripe-js
to redirect the user to Stripe. You'll need to load Stripe.js with your publishable key:
import { loadStripe } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js';
const stripePromise = loadStripe('YOUR_STRIPE_PUBLISHABLE_KEY');
function App() {
return (
<Elements stripe={stripePromise}>
{/* Your components that use Stripe */}
</Elements>
);
}
Create a button that calls the /create-checkout-session
endpoint.
Handling Webhooks
Configure Stripe webhooks to listen for events like checkout.session.completed
and customer.subscription.created
. Use these webhooks to update your Supabase database to reflect the user's subscription status. This ensures that you can control access to features based on subscription level. Securely verify the webhook signatures to prevent tampering.
Conclusion
This guide provides a basic framework for building a SaaS application. You can expand upon this foundation by adding more features, implementing more robust error handling, and optimizing performance. Remember to prioritize security best practices throughout the development process.