The BAGS Restream SDK provides a powerful, full-fledged, and resilient WebSocket client for connecting to the BAGS real-time data stream. It is designed to give developers a simple yet highly configurable way to subscribe to a wide range of on-chain Solana DeFi events, including token swaps, price updates, new token launches, and maker actions. This guide provides a general overview of the SDK’s core concepts, features, and common usage patterns.

Prerequisites

Before starting, make sure you have:

Key Features

The ReStream client is built to handle the complexities of real-time data streaming so you can focus on your application’s logic.
  • Automatic Reconnection: The client automatically handles dropped connections with a configurable exponential backoff strategy, ensuring your application remains connected and resilient.
  • Simple Subscription Management: An intuitive API allows you to subscribe and unsubscribe from data channels, with built-in support for wildcard subscriptions.
  • Built-in Protobuf Decoding: Raw binary messages are automatically decoded into structured, type-safe TypeScript objects, removing the need for manual parsing.
  • Type-Safe Handlers: Leverage TypeScript generics to ensure your event handlers receive data with the correct types, improving code quality and reducing runtime errors.
  • Resilient and Configurable: The client is highly configurable, allowing you to adjust connection timeouts, ping intervals, subscription limits, and reconnection behavior.

Getting Started: A Basic Subscription Script

Here is a complete script that demonstrates the fundamental workflow: connecting to the service, subscribing to BAGS launches, handling incoming data, and disconnecting gracefully.
stream-launches.ts
import { RestreamClient, RestreamLaunchpadLaunchSubscriptionHandler, LaunchpadLaunchEvent } from "@bagsfm/bags-sdk";

// 1. Create a new Restream client instance
const client = new RestreamClient();

async function main() {
	try {
		// 2. Connect to the Restream service
		console.log("🔌 Connecting to Bags Restream...");
		await client.connect();
		console.log("✅ Connected!");

		// 3. Define a handler for incoming launch events
		const launchHandler: RestreamLaunchpadLaunchSubscriptionHandler = (
			launchData: LaunchpadLaunchEvent,
			meta: { channel: string; topic: string; subject: string },
		) => {
			console.log(
				`New token launch: ${launchData.mint}`,
			);
		};

		const unsubscribe = client.subscribeTopic("launchpad_launch", "BAGS", launchHandler);
		console.log("👂 Listening for all BAGS launch events. Press Ctrl+C to exit.");

		// 5. Handle graceful shutdown
		process.on("SIGINT", async () => {
			console.log("\n🛑 Unsubscribing and disconnecting...");
			unsubscribe();
			await client.disconnect();
			process.exit(0);
		});
	} catch (error) {
		console.error("🚨 An error occurred:", error);
		process.exit(1);
	}
}

// Run the main function
main();

Subscription Methods

The SDK offers several ways to subscribe to channels, from generic to highly specific convenience methods.

Generic Methods

These methods provide maximum flexibility for subscribing to any channel.
  • subscribeTopic<T>(topic, subject, handler): The most common method. You provide the topic and subject separately.
    // Subscribe to token launches for a specific launchpad
    client.subscribeTopic(
    	'launchpad_launch',
    	'BAGS',
    	(ev) => { console.log('Launch:', ev); }
    );
    
  • subscribe<T>(channel, handler): Use this when you have the full channel string.
    // Subscribe to all BAGS token launches
    client.subscribe('launchpad_launch:BAGS', (ev) => {
    	console.log('Launch:', ev);
    });
    

Convenience Methods

For common use cases, the SDK provides helper methods that build the channel string for you.
  • subscribeBagsLaunches(handler): Subscribes to new token launches on Bags.
streram-launches-convenient.tsx
import { RestreamClient, RestreamLaunchpadLaunchSubscriptionHandler, LaunchpadLaunchEvent } from "@bagsfm/bags-sdk";

// 1. Create a new Restream client instance
const client = new RestreamClient();

async function main() {
	try {
		// 2. Connect to the Restream service
		console.log("🔌 Connecting to Bags Restream...");
		await client.connect();
		console.log("✅ Connected!");

		// 3. Define a handler for incoming launch events
		const launchHandler: RestreamLaunchpadLaunchSubscriptionHandler = (
			launchData: LaunchpadLaunchEvent,
			meta: { channel: string; topic: string; subject: string },
		) => {
			console.log(
				`New token launch: ${launchData.mint}`,
			);
		};

		const unsubscribe = client.subscribeBagsLaunches(launchHandler);
		console.log("👂 Listening for all BAGS launch events. Press Ctrl+C to exit.");

		// 5. Handle graceful shutdown
		process.on("SIGINT", async () => {
			console.log("\n🛑 Unsubscribing and disconnecting...");
			unsubscribe();
			await client.disconnect();
			process.exit(0);
		});
	} catch (error) {
		console.error("🚨 An error occurred:", error);
		process.exit(1);
	}
}

// Run the main function
main();

Handling Client Lifecycle Events

To build a robust application, you should monitor the client’s connection state. The RestreamClient is an EventEmitter and provides several key events.
// Log when the connection is first established
client.on("open", () => console.log("Connection opened!"));

// Log errors
client.on("error", (err) => console.error("🔥 Client Error:", err));

// Log when the connection closes and a reconnect is scheduled
client.on("close", ({ code, reason }) =>
	console.log(`🚪 Connection Closed: ${code} - ${reason}`),
);

// Monitor reconnection attempts
client.on("reconnecting", ({ attempt, delayMs }) =>
	console.log(`⏳ Reconnecting... (Attempt ${attempt}, delay ${delayMs}ms)`),
);

// Confirm when the connection is restored
client.on("reconnected", ({ attempts }) =>
	console.log(`🎉 Reconnected after ${attempts} attempts!`),
);

Advanced: Debugging Events

Some events are emitted by the ReStream client for observability or custom logic:
// Log when there is a handler error
client.on("handler_error", (error) => console.error("handler error", error));

// Log when there is a socket error
client.on("socket_error", (error) => console.error("socket error", error));

// Could be server notices, errors, etc.
client.on("control", (msg) => console.log("notice:", msg))

// Reconnection errors
client.on("reconnect_error", (error) => console.log("reconnect error:", error))

// For every other error...
client.on("client_error", (ev) => console.log("some client error:", ev))
ReStream client doesn’t log anything by design, so make sure to subscribe to error events for observability, you can then tie that with your own logger.

Advanced: Custom Decoders

If Bags introduces a new topic without a built-in decoder in the SDK, or if you need to override existing decoding logic, you can register your own decoder function. The function receives a raw Buffer and should return the parsed data.
client.registerDecoder("new_experimental_topic", (payload: Buffer) => {
	// Your custom decoding logic here
	const decodedData = JSON.parse(payload.toString());
	return decodedData;
});