Official Node.js SDK for SCS (Spyxpo Cloud Services) - a comprehensive Backend-as-a-Service platform.
npm install @spyxpo/scsThis package includes both the SDK and the CLI tool.
const SCS = require('@spyxpo/scs');
// Initialize from scs-info.json
const scs = SCS.initializeApp('./scs-info.json');const SCS = require('@spyxpo/scs');
const scs = new SCS({
apiKey: 'your-api-key',
projectId: 'your-project-id',
baseUrl: 'https://your-scs-instance.com'
});// Register a new user
const { token, user } = await scs.auth.register({
email: '[email protected]',
password: 'securepassword',
displayName: 'John Doe',
customData: { role: 'admin' }
});
// Login
const { token, user } = await scs.auth.login({
email: '[email protected]',
password: 'securepassword'
});
// Get current user
const user = await scs.auth.getCurrentUser();
// Update profile
await scs.auth.updateProfile({
displayName: 'Jane Doe',
customData: { preferences: { theme: 'dark' } }
});
// Change password
await scs.auth.changePassword({
currentPassword: 'oldpassword',
newPassword: 'newpassword'
});
// Logout
scs.auth.logout();
// Admin: List users
const { users, total } = await scs.auth.listUsers({ limit: 10 });
// Admin: Disable/Enable user
await scs.auth.disableUser('user-id');
await scs.auth.enableUser('user-id');SCS supports multiple OAuth providers for seamless social authentication. Each provider follows a similar pattern but requires different credentials obtained from their respective SDKs.
Authenticate users with their Google account. Requires the Google Sign-In SDK on the client.
// Sign in with Google
const { token, user } = await scs.auth.signInWithGoogle({
idToken: 'google-id-token', // Required: ID token from Google Sign-In
accessToken: 'google-access-token' // Optional: Access token for additional scopes
});
console.log('User ID:', user.uid);
console.log('Email:', user.email);
console.log('Display Name:', user.displayName);
console.log('Photo URL:', user.photoURL);
console.log('Provider:', user.providerId); // 'google'Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
idToken |
string | Yes | The ID token obtained from Google Sign-In SDK |
accessToken |
string | No | Access token for additional Google API scopes |
Returns: { token: string, user: User }
Example with Google Sign-In Web SDK:
// Client-side: Get tokens from Google
const auth2 = gapi.auth2.getAuthInstance();
const googleUser = await auth2.signIn();
const idToken = googleUser.getAuthResponse().id_token;
const accessToken = googleUser.getAuthResponse().access_token;
// Sign in to SCS
const { token, user } = await scs.auth.signInWithGoogle({ idToken, accessToken });Authenticate users with their Facebook account. Requires the Facebook SDK.
// Sign in with Facebook
const { token, user } = await scs.auth.signInWithFacebook({
accessToken: 'facebook-access-token' // Required: Access token from Facebook Login
});
console.log('User:', user.displayName);
console.log('Email:', user.email); // May be null if user didn't grant email permissionParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accessToken |
string | Yes | Access token from Facebook Login SDK |
Example with Facebook SDK:
// Client-side: Login with Facebook
FB.login(async (response) => {
if (response.authResponse) {
const accessToken = response.authResponse.accessToken;
// Sign in to SCS
const { token, user } = await scs.auth.signInWithFacebook({ accessToken });
console.log('Signed in as:', user.displayName);
}
}, { scope: 'email,public_profile' });Authenticate users with their Apple ID. Ideal for iOS apps and required for apps with social login on the App Store.
// Sign in with Apple
const { token, user } = await scs.auth.signInWithApple({
identityToken: 'apple-identity-token', // Required: Identity token from Sign in with Apple
authorizationCode: 'apple-auth-code', // Optional: Authorization code for server verification
fullName: 'John Doe' // Optional: User's name (only available on first sign-in)
});Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
identityToken |
string | Yes | JWT identity token from Sign in with Apple |
authorizationCode |
string | No | Authorization code for additional verification |
fullName |
string | No | User's full name (Apple only provides this on first sign-in) |
Important Notes:
- Apple only provides the user's name on the first sign-in. Store it immediately.
- Users can choose to hide their email (Apple provides a relay email).
- Required for apps using social login on iOS/macOS.
Example with Apple JS SDK:
// Client-side: Configure and sign in
AppleID.auth.init({
clientId: 'com.yourapp.client',
scope: 'name email',
redirectURI: 'https://yourapp.com/callback',
usePopup: true
});
const response = await AppleID.auth.signIn();
const { token, user } = await scs.auth.signInWithApple({
identityToken: response.authorization.id_token,
authorizationCode: response.authorization.code,
fullName: response.user ? `${response.user.name.firstName} ${response.user.name.lastName}` : null
});Authenticate users with their GitHub account. Popular for developer-focused applications.
// Sign in with GitHub
const { token, user } = await scs.auth.signInWithGitHub({
code: 'github-oauth-code', // Required: OAuth authorization code
redirectUri: 'https://yourapp.com/callback' // Optional: Must match OAuth app settings
});
console.log('GitHub username:', user.displayName);
console.log('Email:', user.email);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
code |
string | Yes | OAuth authorization code from GitHub OAuth flow |
redirectUri |
string | No | Redirect URI (must match your GitHub OAuth App settings) |
OAuth Flow Example:
// Step 1: Redirect user to GitHub authorization
const clientId = 'your-github-client-id';
const redirectUri = 'https://yourapp.com/auth/github/callback';
const scope = 'read:user user:email';
const authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}`;
window.location.href = authUrl;
// Step 2: Handle callback (on your callback page)
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
if (code) {
const { token, user } = await scs.auth.signInWithGitHub({ code, redirectUri });
console.log('Signed in:', user.displayName);
}Authenticate users with their Twitter/X account using OAuth 1.0a.
// Sign in with Twitter/X
const { token, user } = await scs.auth.signInWithTwitter({
oauthToken: 'twitter-oauth-token', // Required: OAuth token
oauthTokenSecret: 'twitter-oauth-secret' // Required: OAuth token secret
});Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
oauthToken |
string | Yes | OAuth token from Twitter authentication |
oauthTokenSecret |
string | Yes | OAuth token secret from Twitter authentication |
Note: Twitter uses OAuth 1.0a which requires a more complex flow. Consider using a library like passport-twitter or Twitter's official SDK.
Authenticate users with their Microsoft account (personal, work, or school accounts).
// Sign in with Microsoft
const { token, user } = await scs.auth.signInWithMicrosoft({
accessToken: 'microsoft-access-token', // Required: Access token from MSAL
idToken: 'microsoft-id-token' // Optional: ID token for additional claims
});Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accessToken |
string | Yes | Access token from Microsoft Authentication Library (MSAL) |
idToken |
string | No | ID token for additional user claims |
Example with MSAL.js:
import { PublicClientApplication } from '@azure/msal-browser';
const msalConfig = {
auth: {
clientId: 'your-client-id',
authority: 'https://login.microsoftonline.com/common',
redirectUri: 'https://yourapp.com'
}
};
const msalInstance = new PublicClientApplication(msalConfig);
await msalInstance.initialize();
// Login
const loginResponse = await msalInstance.loginPopup({
scopes: ['openid', 'profile', 'email']
});
// Sign in to SCS
const { token, user } = await scs.auth.signInWithMicrosoft({
accessToken: loginResponse.accessToken,
idToken: loginResponse.idToken
});Allow users to use your app without creating an account. Anonymous accounts can later be upgraded to permanent accounts by linking a provider.
// Sign in anonymously (creates a temporary account)
const { token, user } = await scs.auth.signInAnonymously({
customData: { referrer: 'landing-page', campaign: 'summer-sale' } // optional
});
console.log('Anonymous user ID:', user.uid);
console.log('Is anonymous:', user.isAnonymous); // trueParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
customData |
object | No | Custom data to store with the anonymous user for analytics |
Use Cases:
- Allow users to try your app before signing up
- Guest checkout in e-commerce
- Save user progress/preferences before account creation
- A/B testing with user tracking
Converting Anonymous to Permanent Account:
// User decides to create a permanent account
// Link their anonymous account to a provider
try {
await scs.auth.linkProvider('google', {
idToken: 'google-id-token'
});
console.log('Account upgraded! User data preserved.');
console.log('Is anonymous:', user.isAnonymous); // false
} catch (error) {
if (error.code === 'auth/credential-already-in-use') {
// This Google account is already linked to another user
console.log('This account is already registered. Please sign in instead.');
}
}Two-step authentication flow using SMS verification codes.
// Step 1: Send verification code to phone
const { verificationId } = await scs.auth.sendPhoneVerificationCode({
phoneNumber: '+1234567890', // Required: E.164 format
recaptchaToken: 'recaptcha-token' // Optional: For bot protection
});
console.log('Verification ID:', verificationId);
// Store this ID - you'll need it in step 2
// Step 2: User enters the code they received
const { token, user } = await scs.auth.signInWithPhoneNumber({
verificationId: verificationId, // The ID from step 1
code: '123456' // 6-digit code from SMS
});
console.log('Phone verified:', user.phoneNumber);sendPhoneVerificationCode Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
phoneNumber |
string | Yes | Phone number in E.164 format (e.g., +1234567890) |
recaptchaToken |
string | No | reCAPTCHA token for abuse prevention |
signInWithPhoneNumber Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
verificationId |
string | Yes | Verification ID from sendPhoneVerificationCode |
code |
string | Yes | 6-digit verification code from SMS |
Complete Flow Example:
async function signInWithPhone(phoneNumber) {
try {
// Step 1: Send code
const { verificationId } = await scs.auth.sendPhoneVerificationCode({
phoneNumber: phoneNumber
});
// Show UI for code entry
const code = await promptUserForCode(); // Your UI implementation
// Step 2: Verify code
const { token, user } = await scs.auth.signInWithPhoneNumber({
verificationId,
code
});
return user;
} catch (error) {
switch (error.code) {
case 'auth/invalid-phone-number':
throw new Error('Invalid phone number format. Use E.164 format (+1234567890)');
case 'auth/too-many-requests':
throw new Error('Too many attempts. Please try again later.');
case 'auth/invalid-verification-code':
throw new Error('Invalid verification code. Please try again.');
case 'auth/code-expired':
throw new Error('Code expired. Please request a new one.');
default:
throw error;
}
}
}Sign in using a JWT token generated by your own backend. Useful for migrating users from another system or integrating with custom authentication.
// Sign in with a custom token (generated by your backend)
const { token, user } = await scs.auth.signInWithCustomToken('your-custom-jwt-token');
console.log('Signed in user:', user.uid);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
token |
string | Yes | JWT token generated by your backend |
Backend Token Generation Example (Node.js):
const jwt = require('jsonwebtoken');
function createCustomToken(uid, claims = {}) {
const payload = {
uid: uid,
claims: claims,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1 hour
};
return jwt.sign(payload, process.env.SCS_SECRET_KEY, {
algorithm: 'HS256',
issuer: 'your-project-id'
});
}
// Generate token for a user
const customToken = createCustomToken('user-123', { role: 'admin' });
// Send this token to the client for sign-inUse Cases:
- Migrating users from another authentication system
- Server-side user creation with immediate client sign-in
- Integration with enterprise SSO systems
- Machine-to-machine authentication
Link multiple authentication providers to a single account. Users can sign in with any linked provider.
// Link a provider to current account
const updatedUser = await scs.auth.linkProvider('facebook', {
accessToken: 'facebook-access-token'
});
console.log('Linked providers:', updatedUser.providerData);
// [{ providerId: 'password' }, { providerId: 'google' }, { providerId: 'facebook' }]
// Unlink a provider from current account
const user = await scs.auth.unlinkProvider('facebook');
console.log('Remaining providers:', user.providerData);
// Get available sign-in methods for an email
const { methods } = await scs.auth.fetchSignInMethodsForEmail('[email protected]');
console.log('Available methods:', methods);
// ['password', 'google', 'facebook']linkProvider Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
provider |
string | Yes | Provider name: 'google', 'facebook', 'apple', 'github', 'twitter', 'microsoft' |
credentials |
object | Yes | Provider-specific credentials (tokens) |
Supported Providers and Credentials:
| Provider | Required Credentials |
|---|---|
google |
{ idToken, accessToken? } |
facebook |
{ accessToken } |
apple |
{ identityToken, authorizationCode?, fullName? } |
github |
{ code, redirectUri? } |
twitter |
{ oauthToken, oauthTokenSecret } |
microsoft |
{ accessToken, idToken? } |
Complete Account Linking Flow:
// Check if user can link a provider
async function canLinkProvider(email, provider) {
const { methods } = await scs.auth.fetchSignInMethodsForEmail(email);
if (methods.includes(provider)) {
throw new Error(`${provider} is already linked to this account`);
}
return true;
}
// Link Google to existing account
async function linkGoogleAccount(googleIdToken) {
try {
// Verify user is signed in
const currentUser = await scs.auth.getCurrentUser();
if (!currentUser) {
throw new Error('Must be signed in to link accounts');
}
// Link the provider
const updatedUser = await scs.auth.linkProvider('google', {
idToken: googleIdToken
});
console.log('Successfully linked Google account');
return updatedUser;
} catch (error) {
if (error.code === 'auth/credential-already-in-use') {
console.error('This Google account is already linked to another user');
} else if (error.code === 'auth/provider-already-linked') {
console.error('Google is already linked to this account');
}
throw error;
}
}Handle password recovery and email verification flows.
// Send password reset email
await scs.auth.sendPasswordResetEmail('[email protected]');
console.log('Password reset email sent');
// Confirm password reset (user clicks link in email, you extract the code)
await scs.auth.confirmPasswordReset({
code: 'reset-code-from-email', // Code from the reset link
newPassword: 'newSecurePassword123' // User's new password
});
console.log('Password successfully reset');
// Send email verification to current user
await scs.auth.sendEmailVerification();
console.log('Verification email sent');
// Verify email with code (user clicks link, you extract the code)
await scs.auth.verifyEmail('verification-code');
console.log('Email verified');Error Handling:
// Password Reset with error handling
async function resetPassword(email) {
try {
await scs.auth.sendPasswordResetEmail(email);
return { success: true, message: 'Reset email sent' };
} catch (error) {
switch (error.code) {
case 'auth/user-not-found':
// Don't reveal if user exists for security
return { success: true, message: 'If this email exists, a reset link was sent' };
case 'auth/too-many-requests':
return { success: false, message: 'Too many attempts. Please try later.' };
default:
throw error;
}
}
}
// Confirm password reset with validation
async function confirmReset(code, newPassword) {
// Validate password strength
if (newPassword.length < 8) {
throw new Error('Password must be at least 8 characters');
}
try {
await scs.auth.confirmPasswordReset({ code, newPassword });
return { success: true };
} catch (error) {
switch (error.code) {
case 'auth/expired-action-code':
return { success: false, message: 'Reset link expired. Please request a new one.' };
case 'auth/invalid-action-code':
return { success: false, message: 'Invalid reset link.' };
case 'auth/weak-password':
return { success: false, message: 'Password is too weak.' };
default:
throw error;
}
}
}Listen for authentication state changes and manage user sessions.
// Check if user is logged in
const isLoggedIn = scs.auth.isLoggedIn;
console.log('Is logged in:', isLoggedIn);
// Get current user (from cache)
const cachedUser = scs.auth.currentUser;
// Get current user (fresh from server)
const user = await scs.auth.getCurrentUser();
// Refresh user data
const refreshedUser = await scs.auth.reload();
// Get the current auth token
const token = scs.auth.token;
// Listen for auth state changes (if supported)
scs.auth.onAuthStateChanged((user) => {
if (user) {
console.log('User signed in:', user.uid);
} else {
console.log('User signed out');
}
});const SCS = require('@spyxpo/scs');
const scs = new SCS({
apiKey: 'your-api-key',
projectId: 'your-project-id',
baseUrl: 'https://your-scs-instance.com'
});
class AuthService {
// Email/Password registration
async register(email, password, displayName) {
const { token, user } = await scs.auth.register({
email,
password,
displayName
});
// Send verification email
await scs.auth.sendEmailVerification();
return user;
}
// Email/Password login
async login(email, password) {
return await scs.auth.login({ email, password });
}
// Social login (works for any provider)
async socialLogin(provider, credentials) {
const methods = {
google: () => scs.auth.signInWithGoogle(credentials),
facebook: () => scs.auth.signInWithFacebook(credentials),
apple: () => scs.auth.signInWithApple(credentials),
github: () => scs.auth.signInWithGitHub(credentials),
twitter: () => scs.auth.signInWithTwitter(credentials),
microsoft: () => scs.auth.signInWithMicrosoft(credentials)
};
if (!methods[provider]) {
throw new Error(`Unknown provider: ${provider}`);
}
return await methods[provider]();
}
// Guest mode
async continueAsGuest(customData = {}) {
return await scs.auth.signInAnonymously({ customData });
}
// Upgrade guest to permanent account
async upgradeGuestAccount(provider, credentials) {
const user = await scs.auth.getCurrentUser();
if (!user?.isAnonymous) {
throw new Error('Current user is not anonymous');
}
return await scs.auth.linkProvider(provider, credentials);
}
// Logout
logout() {
scs.auth.logout();
}
}
// Usage
const auth = new AuthService();
// Register new user
const user = await auth.register('[email protected]', 'password123', 'John Doe');
// Or sign in with Google
const googleUser = await auth.socialLogin('google', { idToken: 'xxx' });
// Or continue as guest and upgrade later
const guest = await auth.continueAsGuest({ source: 'homepage' });
// ... user decides to create account ...
const upgraded = await auth.upgradeGuestAccount('google', { idToken: 'xxx' });NoSQL document database with nested collections. SCS supports two powerful database options:
| Type | Name | Description | Best For |
|---|---|---|---|
eazi |
eaZI Database | Document-based NoSQL with Firestore-like collections, documents, and subcollections | Development, prototyping, small to medium apps |
reladb |
RelaDB | Production-grade NoSQL database with relational-style views | Production, scalability, advanced queries |
import SCS from 'scs-sdk';
// eaZI is the default database - no special configuration needed
const scs = new SCS({
projectId: 'your-project-id',
apiKey: 'your-api-key'
// databaseType: 'eazi' is implicit
});import SCS from 'scs-sdk';
// Use RelaDB for production
const scs = new SCS({
projectId: 'your-project-id',
apiKey: 'your-api-key',
databaseType: 'reladb' // Enable RelaDB
});- Document-based: Firestore-like collections and documents
- Subcollections: Nested data organization
- File-based storage: No external dependencies required
- Zero configuration: Works out of the box
- Query support: Filtering, ordering, and pagination
- Production-ready: Built for reliability and performance
- Scalable: Horizontal scaling and replication support
- Advanced queries: Aggregation pipelines, complex filters
- Indexing: Custom indexes for optimized performance
- Schema flexibility: Dynamic schema with validation support
- Relational-style views: Table view with columns and rows in the console
// Get a collection reference
const users = scs.database.collection('users');
// List all collections
const collections = await scs.database.listCollections();
// Create a collection
await scs.database.createCollection('newCollection');
// Delete a collection
await scs.database.deleteCollection('oldCollection');// Add document with auto-generated ID
const doc = await scs.database.collection('users').add({
name: 'John Doe',
email: '[email protected]',
age: 30,
tags: ['developer', 'nodejs'],
profile: {
bio: 'Software developer',
avatar: 'https://example.com/avatar.jpg'
}
});
console.log('Document ID:', doc.id);
// Set document with custom ID (creates or overwrites)
await scs.database.collection('users').doc('user-123').set({
name: 'Jane Doe',
email: '[email protected]'
});
// Get a single document
const user = await scs.database.collection('users').doc('user-123').get();
console.log(user.data);
// Update document (partial update)
await scs.database.collection('users').doc('user-123').update({
age: 31,
'profile.bio': 'Senior developer'
});
// Delete document
await scs.database.collection('users').doc('user-123').delete();// Simple query with single filter
const activeUsers = await scs.database.collection('users')
.where('status', '==', 'active')
.get();
// Multiple filters
const results = await scs.database.collection('users')
.where('age', '>=', 18)
.where('status', '==', 'active')
.get();
// Ordering and pagination
const posts = await scs.database.collection('posts')
.where('published', '==', true)
.orderBy('createdAt', 'desc')
.limit(10)
.skip(20)
.get();
// Using 'in' operator
const featured = await scs.database.collection('posts')
.where('category', 'in', ['tech', 'science', 'news'])
.get();
// Using 'contains' for array fields
const tagged = await scs.database.collection('posts')
.where('tags', 'contains', 'javascript')
.get();| Operator | Description | Example |
|---|---|---|
== |
Equal to | .where('status', '==', 'active') |
!= |
Not equal to | .where('status', '!=', 'deleted') |
> |
Greater than | .where('age', '>', 18) |
>= |
Greater than or equal | .where('age', '>=', 18) |
< |
Less than | .where('price', '<', 100) |
<= |
Less than or equal | .where('price', '<=', 50) |
in |
Value in array | .where('status', 'in', ['active', 'pending']) |
contains |
Array contains value | .where('tags', 'contains', 'featured') |
// Access a subcollection
const postsRef = scs.database
.collection('users')
.doc('userId')
.collection('posts');
// Add to subcollection
const post = await postsRef.add({
title: 'My First Post',
content: 'Hello World!',
createdAt: new Date()
});
// Query subcollection
const userPosts = await postsRef
.orderBy('createdAt', 'desc')
.limit(5)
.get();
// Nested subcollections (e.g., users/userId/posts/postId/comments)
const commentsRef = scs.database
.collection('users')
.doc('userId')
.collection('posts')
.doc('postId')
.collection('comments');
// List subcollections of a document
const subcollections = await scs.database
.collection('users')
.doc('userId')
.listCollections();// Upload a file from path
const file = await scs.storage.upload('./image.png', {
folder: 'images',
filename: 'profile.png'
});
// Upload from buffer
const buffer = fs.readFileSync('./document.pdf');
const file = await scs.storage.upload(buffer, {
folder: 'documents',
filename: 'report.pdf',
contentType: 'application/pdf'
});
// List files
const { files, total } = await scs.storage.list({
folder: 'images',
limit: 20
});
// Get file URL
const url = scs.storage.getUrl('file-id');
// Get file metadata
const metadata = await scs.storage.getMetadata('file-id');
// Download file
const buffer = await scs.storage.download('file-id');
// Download to file
await scs.storage.downloadToFile('file-id', './downloaded.png');
// Delete file
await scs.storage.delete('file-id');
// Create/delete folders
await scs.storage.createFolder('documents/reports');
await scs.storage.deleteFolder('documents/reports');
// File reference
const fileRef = scs.storage.ref('file-id');
console.log(fileRef.url);
await fileRef.delete();Real-time data synchronization via WebSocket.
// Connect to realtime database
await scs.realtime.connect();
// Get a reference
const chatRef = scs.realtime.ref('chat/room1');
// Set data
await chatRef.set({
name: 'General Chat',
created: new Date().toISOString()
});
// Update data (merge)
await chatRef.update({
lastMessage: 'Hello!'
});
// Push new child with auto-generated key
const messageRef = await chatRef.child('messages').push({
text: 'Hello everyone!',
sender: 'user123',
timestamp: Date.now()
});
// Get data
const data = await chatRef.get();
// Listen for real-time updates
const unsubscribe = await chatRef.on((data, event) => {
console.log('Data changed:', data, 'Event:', event);
});
// Stop listening
unsubscribe();
// Remove data
await chatRef.remove();
// Disconnect
scs.realtime.disconnect();Push notifications with topics.
// Register device token
await scs.messaging.registerToken({
token: 'device-token',
platform: 'web' // 'web', 'ios', or 'android'
});
// Create a topic
await scs.messaging.createTopic({
name: 'news',
description: 'Breaking news updates'
});
// Subscribe to topic
await scs.messaging.subscribeToTopic('news', 'device-token');
// Send to topic
await scs.messaging.sendToTopic('news', {
title: 'Breaking News',
body: 'Something important happened!',
data: { articleId: '123' }
});
// Send to specific token
await scs.messaging.sendToToken('device-token', {
title: 'Personal Update',
body: 'You have a new message',
data: { messageId: '456' }
});
// Topic reference
const newsTopic = scs.messaging.topic('news');
await newsTopic.send({ title: 'Update', body: 'New content available' });
// Unsubscribe from topic
await scs.messaging.unsubscribeFromTopic('news', 'device-token');
// List topics
const topics = await scs.messaging.listTopics();Dynamic configuration management.
// Fetch configuration
const config = await scs.remoteConfig.fetch();
// Get typed values
const welcomeMessage = await scs.remoteConfig.getString('welcome_message', 'Hello!');
const maxItems = await scs.remoteConfig.getNumber('max_items', 10);
const featureEnabled = await scs.remoteConfig.getBoolean('new_feature', false);
const settings = await scs.remoteConfig.getJson('app_settings', {});
// Force refresh from server
const freshConfig = await scs.remoteConfig.fetch({ forceRefresh: true });
// Set cache duration (in milliseconds)
scs.remoteConfig.setCacheDuration(10 * 60 * 1000); // 10 minutes
// Admin: Create parameter
await scs.remoteConfig.createParam({
key: 'welcome_message',
value: 'Welcome to our app!',
type: 'string',
description: 'Message shown on home screen'
});
// Admin: Update parameter
await scs.remoteConfig.updateParam('welcome_message', {
value: 'Welcome back!'
});
// Admin: Publish changes
await scs.remoteConfig.publish();
// Admin: Rollback to previous version
await scs.remoteConfig.rollback('version-id');// Create a function
await scs.functions.create({
name: 'hello',
code: `
module.exports = async (req, res) => {
const name = req.body.name || 'World';
res.json({ message: \`Hello, \${name}!\` });
};
`,
timeout: 30,
memoryLimit: 128,
public: true
});
// Invoke a function
const result = await scs.functions.invoke('hello', { name: 'John' });
// Use httpsCallable for cleaner syntax
const helloFn = scs.functions.httpsCallable('hello');
const response = await helloFn.call({ name: 'Jane' });
// Test a function (admin)
const testResult = await scs.functions.test('function-id', { test: true });
// Get function logs
const logs = await scs.functions.getLogs('function-id', { limit: 50 });
// Update function
await scs.functions.update('function-id', {
code: 'new code...',
timeout: 60
});
// Delete function
await scs.functions.delete('function-id');Chat, text completion, and image generation with local LLM models.
// Chat with AI
const response = await scs.ai.chat({
message: 'What is the capital of France?',
systemPrompt: 'You are a helpful geography assistant.'
});
console.log(response.content);
// Text completion
const completion = await scs.ai.complete({
prompt: 'Once upon a time'
});
console.log(completion.content);
// Generate image
const image = await scs.ai.generateImage({
prompt: 'A sunset over mountains'
});
console.log(image.imageUrl);
// List available models
const models = await scs.ai.listModels();
// Conversation management
const conversation = await scs.ai.createConversation({ title: 'Geography Chat' });
await scs.ai.getConversation(conversation.conversationId);
await scs.ai.deleteConversation(conversation.conversationId);Create and manage AI agents with custom instructions and tools.
// Create an agent
const agent = await scs.ai.createAgent({
name: 'Customer Support',
instructions: 'You are a helpful customer support assistant. Be polite and helpful.',
model: 'llama3.2',
temperature: 0.7
});
console.log('Created agent:', agent.agentId);
// List agents
const agents = await scs.ai.listAgents();
// Run the agent
let response = await scs.ai.runAgent(agent.agentId, {
input: 'How do I reset my password?'
});
console.log('Agent:', response.output);
console.log('Session:', response.sessionId);
// Continue the conversation in the same session
response = await scs.ai.runAgent(agent.agentId, {
input: 'Thanks! What about enabling 2FA?',
sessionId: response.sessionId
});
// List agent sessions
const sessions = await scs.ai.listAgentSessions(agent.agentId);
// Get full session history
const session = await scs.ai.getAgentSession(agent.agentId, response.sessionId);
session.messages.forEach(msg => {
console.log(`${msg.role}: ${msg.content}`);
});
// Update agent
await scs.ai.updateAgent(agent.agentId, {
instructions: 'Updated instructions here',
temperature: 0.5
});
// Define a tool for agents
const tool = await scs.ai.defineTool({
name: 'get_weather',
description: 'Get weather for a location',
parameters: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' }
}
}
});
// List tools
const tools = await scs.ai.listTools();
// Delete agent and sessions
await scs.ai.deleteAgentSession(agent.agentId, response.sessionId);
await scs.ai.deleteAgent(agent.agentId);The SCS CLI allows you to deploy your application from the command line.
# Initialize a new project
scs init
# Deploy all targets
scs deploy
# Deploy specific targets
scs deploy hosting
scs deploy functions
scs deploy rules
# Deploy multiple targets
scs deploy hosting functions
# Use --only or --except flags
scs deploy --only hosting,functions
scs deploy --except rules| Command | Description |
|---|---|
scs init |
Initialize a new SCS project |
scs deploy |
Deploy to SCS (all targets by default) |
scs list <target> |
List deployments for a target |
scs login |
Authenticate with SCS |
scs logout |
Log out from SCS |
scs help |
Show help information |
| Target | Description |
|---|---|
hosting |
Static website hosting |
app-hosting |
Dynamic app hosting (Node.js, Python, etc.) |
functions |
Cloud functions |
rules |
Security rules |
| Option | Description |
|---|---|
--config <path> |
Path to scs.json config file |
--project <id> |
Project ID to use |
--message <msg> |
Deployment message/description |
--only <targets> |
Only deploy specified targets (comma-separated) |
--except <targets> |
Deploy all except specified targets |
--verbose |
Show detailed error information |
The CLI reads from scs.json in your project root:
{
"sdk_config": {
"api_key": "YOUR_API_KEY",
"project_id": "YOUR_PROJECT_ID",
"base_url": "https://your-scs-instance.com"
},
"hosting": {
"public": "public"
},
"functions": {
"source": "functions",
"runtime": "nodejs18"
},
"appHosting": {
"source": ".",
"runtime": "nodejs18",
"buildCommand": "npm run build",
"startCommand": "npm start"
},
"database": {
"rules": "scs.rules.json"
}
}# Initialize and deploy
scs init
scs deploy
# Deploy only hosting with a message
scs deploy hosting --message "Updated homepage"
# Deploy functions from a custom directory
scs deploy functions --source ./my-functions
# List all hosting deployments
scs list hosting
# List deployed functions
scs list functionsThe SDK includes TypeScript type definitions. Just import and use:
import SCS from '@spyxpo/scs-sdk';
const scs = new SCS({
apiKey: 'your-api-key',
projectId: 'your-project-id',
baseUrl: 'https://your-scs-instance.com'
});
// Full type inference and autocompletion
const user = await scs.auth.getCurrentUser();
console.log(user.email); // TypeScript knows this is a stringAll methods throw errors with useful information:
try {
await scs.auth.login({ email: '[email protected]', password: 'wrong' });
} catch (error) {
console.error('Error:', error.message);
console.error('Status:', error.status); // HTTP status code
console.error('Response:', error.response); // Full response body
}The SDK can read from a scs-info.json file:
{
"sdk_config": {
"api_key": "pk_your_api_key",
"project_id": "your-project-id",
"base_url": "https://your-scs-instance.com"
},
"project_info": {
"project_id": "your-project-id",
"api_url": "https://your-scs-instance.com"
},
"client": {
"api_key": "pk_your_api_key"
}
}- Node.js >= 14.0.0
socket.io-client(for realtime features)form-data(for file uploads)
MIT