@supabase/supabase-js, aws-amplify, firebase, and parse-server are all client-side SDKs that enable frontend applications to interact with backend services without writing traditional server code. They provide real-time data synchronization, authentication, file storage, and database access from the browser or mobile clients. While they share high-level goals, their architecture, cloud dependencies, data models, and developer workflows differ significantly. @supabase/supabase-js is built on open-source PostgreSQL with real-time via replication; aws-amplify integrates deeply with AWS services like AppSync and Cognito; firebase offers Google-managed NoSQL databases with live sync; and parse-server is a self-hosted, open-source BaaS originally created by Facebook that uses MongoDB or PostgreSQL as its datastore.
When building modern web apps, you often need more than just a UI—you need user accounts, data storage, file uploads, and real-time updates. Instead of building a custom backend, many teams reach for a Backend-as-a-Service (BaaS). The four main contenders—@supabase/supabase-js, aws-amplify, firebase, and parse-server—all promise to simplify this, but they solve the problem in very different ways. Let’s compare them head-to-head based on how you actually use them day-to-day.
All four support email/password and social logins, but their implementation details vary.
@supabase/supabase-js uses JWTs issued by GoTrue (an open-source identity service). You sign in and get session data immediately.
// Supabase
import { createClient } from '@supabase/supabase-js';
const supabase = createClient('https://...', '...');
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'secret'
});
// data.session contains access_token, user, etc.
aws-amplify wraps AWS Cognito. You configure it once, then use a unified Auth class.
// Amplify
import { Auth } from 'aws-amplify';
try {
const user = await Auth.signIn('user@example.com', 'secret');
// user object includes signInUserSession
} catch (error) {
console.log('Sign in failed', error);
}
firebase uses Firebase Authentication. After init, you call methods directly on the auth instance.
// Firebase
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
const auth = getAuth();
try {
const userCredential = await signInWithEmailAndPassword(auth, 'user@example.com', 'secret');
// userCredential.user has uid, email, etc.
} catch (error) {
console.error('Login failed:', error);
}
parse-server handles auth through its REST API. You typically use the Parse SDK’s logIn method.
// Parse
import Parse from 'parse';
try {
const user = await Parse.User.logIn('user@example.com', 'secret');
// user is the current logged-in Parse.User
} catch (error) {
console.error('Login failed:', error.message);
}
💡 Key difference: Supabase and Firebase return user/session objects directly. Amplify abstracts Cognito sessions behind its own interface. Parse relies on a global
Parse.User.current()pattern.
How you store and query data is where these SDKs diverge most.
@supabase/supabase-js exposes a fluent API over Postgres tables. You write queries that look like SQL.
// Supabase: relational queries
const { data, error } = await supabase
.from('posts')
.select('id, title, author!inner(name)')
.eq('published', true)
.order('created_at', { ascending: false });
aws-amplify typically uses GraphQL with AWS AppSync (though REST is possible). You define operations in .graphql files.
// Amplify: GraphQL query
import { API } from 'aws-amplify';
import { listPosts } from './graphql/queries';
const response = await API.graphql({
query: listPosts,
variables: { filter: { published: { eq: true } } }
});
// response.data.listPosts.items
firebase uses Firestore (NoSQL). You navigate collections and documents.
// Firebase: Firestore query
import { collection, query, where, orderBy, getDocs } from 'firebase/firestore';
const q = query(
collection(db, 'posts'),
where('published', '==', true),
orderBy('createdAt', 'desc')
);
const snapshot = await getDocs(q);
const posts = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
parse-server uses a simple object model. Queries are built with a chainable builder.
// Parse
const Post = Parse.Object.extend('Post');
const query = new Parse.Query(Post);
query.equalTo('published', true);
query.descending('createdAt');
const results = await query.find();
// results is an array of Parse.Object instances
💡 Key difference: Supabase gives you SQL power (joins, filters, RLS). Firebase forces denormalization but offers live sync. Amplify leans on GraphQL types. Parse uses a classic ORM-like approach.
Live data is critical for chat apps, dashboards, or collaborative tools.
@supabase/supabase-js uses PostgreSQL’s replication slots to push changes over WebSockets.
// Supabase: real-time subscription
const channel = supabase
.channel('posts-changes')
.on(
'postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'posts' },
(payload) => console.log('New post:', payload.new)
)
.subscribe();
aws-amplify uses AppSync subscriptions over MQTT/WebSockets.
// Amplify: GraphQL subscription
import { graphqlOperation } from 'aws-amplify';
import { onCreatePost } from './graphql/subscriptions';
const subscription = API.graphql(
graphqlOperation(onCreatePost)
).subscribe({
next: (data) => console.log('New post:', data.value.data.onCreatePost)
});
firebase has built-in real-time listeners for Firestore.
// Firebase: onSnapshot listener
import { collection, query, where, onSnapshot } from 'firebase/firestore';
const q = query(collection(db, 'posts'), where('published', '==', true));
const unsubscribe = onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach(change => {
if (change.type === 'added') {
console.log('New post:', change.doc.data());
}
});
});
parse-server does not include native real-time support. You must add it yourself via LiveQuery (a separate plugin) or use WebSockets manually.
// Parse: requires enabling LiveQuery server-side
const query = new Parse.Query(Post);
query.subscribe().then(subscription => {
subscription.on('create', (post) => {
console.log('New post:', post);
});
});
// Note: LiveQuery must be configured in parse-server options
⚠️ Warning: Parse’s real-time capability is optional and not enabled by default. The other three include it out of the box.
Your choice here affects long-term flexibility.
All support file uploads, but with different abstractions.
Supabase uses buckets (like S3) with signed URLs.
// Supabase
const { data, error } = await supabase.storage
.from('avatars')
.upload('user123.png', file);
Amplify uses S3 under the hood.
// Amplify
import { Storage } from 'aws-amplify';
await Storage.put('avatars/user123.png', file, {
contentType: 'image/png'
});
Firebase uses Cloud Storage.
// Firebase
import { ref, uploadBytes } from 'firebase/storage';
const storageRef = ref(storage, 'avatars/user123.png');
await uploadBytes(storageRef, file);
Parse stores files via its REST API, typically backed by GridFS (MongoDB) or local disk.
// Parse
const parseFile = new Parse.File('user123.png', file);
await parseFile.save();
// Returns URL like https://your-parse-server.com/files/appId/user123.png
As of 2024:
@supabase/supabase-js: Actively maintained, v2 is current.aws-amplify: Actively maintained by AWS.firebase: Actively maintained by Google.parse-server: Community-maintained. Not deprecated, but development pace is slower. Suitable for legacy migrations, not recommended for new projects unless you need full control and have DevOps resources.| Feature | @supabase/supabase-js | aws-amplify | firebase | parse-server |
|---|---|---|---|---|
| Database Type | Relational (PostgreSQL) | NoSQL (DynamoDB) / GraphQL | NoSQL (Firestore) | Document (MongoDB/PostgreSQL) |
| Real-Time | ✅ Built-in (Postgres RLS) | ✅ Via AppSync | ✅ Built-in | ⚠️ Optional (LiveQuery) |
| Offline Support | ❌ | ✅ (AppSync) | ✅ (Firestore) | ❌ |
| Auth Providers | Email, OAuth, Magic Links | Cognito (many providers) | Many (Google, Apple, etc.) | Email, OAuth (limited) |
| Self-Hostable | ✅ Yes | ❌ No | ❌ No | ✅ Yes |
| Vendor Lock-in | Low | High | High | None |
| Best For | SQL lovers, open-source fans | AWS shops, enterprise apps | Rapid prototyping, mobile | Legacy migrations, full control |
Don’t pick based on hype—pick based on your team’s skills, cloud strategy, and data model needs.
Choose @supabase/supabase-js if you prefer working with relational data, need full SQL capabilities, and want an open-source stack you can self-host or use via Supabase’s managed service. It’s ideal when your team values Postgres’ ACID compliance, row-level security, and real-time subscriptions over WebSockets. Avoid it if you’re locked into AWS or Google Cloud ecosystems and don’t want to manage another vendor.
Choose firebase if you need rapid prototyping with real-time NoSQL data, built-in auth providers, and seamless integration with Google Cloud. Its Firestore offering provides automatic live updates and offline persistence out of the box, making it great for collaborative or mobile-first apps. Avoid it if you require complex joins, transactions across collections, or strict relational integrity.
Choose aws-amplify if your application already runs on AWS or you plan to leverage services like Lambda, DynamoDB, S3, or Cognito. It excels in enterprise environments requiring fine-grained IAM policies, GraphQL APIs via AppSync, and offline data sync with conflict resolution. However, be prepared for steeper learning curves around AWS configuration and potential vendor lock-in.
Choose parse-server only if you need a self-hosted, open-source BaaS with legacy compatibility (e.g., migrating from the original Parse.com). It supports both MongoDB and PostgreSQL backends and offers a simple REST/GraphQL-like API. However, it lacks first-class real-time support compared to others and requires significant DevOps effort for scaling and monitoring—making it less suitable for new greenfield projects.
Guides · Reference Docs · TypeDoc
First of all, you need to install the library:
npm install @supabase/supabase-js
Then you're able to import the library and establish the connection with the database:
import { createClient } from '@supabase/supabase-js'
// Create a single supabase client for interacting with your database
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
You can use plain <script>s to import supabase-js from CDNs, like:
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
or even:
<script src="https://unpkg.com/@supabase/supabase-js@2"></script>
Then you can use it from a global supabase variable:
<script>
const { createClient } = supabase
const _supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
console.log('Supabase Instance: ', _supabase)
// ...
</script>
You can use <script type="module"> to import supabase-js from CDNs, like:
<script type="module">
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm'
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
console.log('Supabase Instance: ', supabase)
// ...
</script>
You can use supabase-js in the Deno runtime via JSR:
import { createClient } from 'jsr:@supabase/supabase-js@2'
fetch implementationsupabase-js uses the cross-fetch library to make HTTP requests, but an alternative fetch implementation can be provided as an option. This is most useful in environments where cross-fetch is not compatible, for instance Cloudflare Workers:
import { createClient } from '@supabase/supabase-js'
// Provide a custom `fetch` implementation as an option
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', {
global: {
fetch: (...args) => fetch(...args),
},
})
This section outlines the scope of support for various runtime environments in Supabase JavaScript client.
We only support Node.js versions that are in Active LTS or Maintenance status as defined by the official Node.js release schedule. This means we support versions that are currently receiving long-term support and critical bug fixes.
When a Node.js version reaches end-of-life and is no longer in Active LTS or Maintenance status, Supabase will drop it in a minor release, and this won't be considered a breaking change.
⚠️ Node.js 18 Deprecation Notice
Node.js 18 reached end-of-life on April 30, 2025. As announced in our deprecation notice, support for Node.js 18 was dropped in version
2.79.0.If you must use Node.js 18, please use version
2.78.0, which is the last version that supported Node.js 18.
We support Deno versions that are currently receiving active development and security updates. We follow the official Deno release schedule and only support versions from the stable and lts release channels.
When a Deno version reaches end-of-life and is no longer receiving security updates, Supabase will drop it in a minor release, and this won't be considered a breaking change.
All modern browsers are supported. We support browsers that provide native fetch API. For Realtime features, browsers must also support native WebSocket API.
We support Bun runtime environments. Bun provides native fetch support and is compatible with Node.js APIs. Since Bun does not follow a structured release schedule like Node.js or Deno, we support current stable versions of Bun and may drop support for older versions in minor releases without considering it a breaking change.
We support React Native environments with fetch polyfills provided by the framework. Since React Native does not follow a structured release schedule, we support current stable versions and may drop support for older versions in minor releases without considering it a breaking change.
We support Cloudflare Workers runtime environments. Cloudflare Workers provides native fetch support. Since Cloudflare Workers does not follow a structured release schedule, we support current stable versions and may drop support for older versions in minor releases without considering it a breaking change.
We welcome contributions! Please see our Contributing Guide for details on how to get started.
For major changes or if you're unsure about something, please open an issue first to discuss your proposed changes.
# From the monorepo root
npx nx build supabase-js
# Or with watch mode for development
npx nx build supabase-js --watch
There's a complete guide on how to set up your environment for running locally the supabase-js integration tests. Please refer to TESTING.md.