Basta's Client API supports GraphQL subscriptions over WebSockets to deliver near real-time updates to connected clients.
These updates enhance the responsiveness and interactivity of auction experiences — especially near the end of a sale when timing and feedback are critical.
✅ Subscriptions support both unauthenticated and authenticated access via bidder tokens.
Near real-time updates for connected clients are achieved with GraphQL subscriptions. These updates enhance the auction experience and play a crucial part near the end of an auction.
- Endpoint:
wss://client.api.basta.app/query - Protocol: GraphQL over WebSocket (
graphql-ws) - Ping/Pong: Automatic keep-alive every 10 seconds
To authenticate, send a bidder token in the initPayload when establishing the WebSocket connection:
{
"type": "connection_init",
"payload": {
"token": "<BIDDER_JWT_TOKEN>"
}
}If no token is provided, the connection is treated as unauthenticated.
If you're using the graphql-ws package you can use the connectionParams to provide the bidder token like so:
connectionParams: () => {
return {
token: bidderToken,
};
},For integrators building advanced clients, here's how auth is handled on the server:
- The
tokenfield in the WebSocketconnection_initpayload is validated via JWT - Valid claims are injected into the request context
- Bids and subscriptions are scoped to the authenticated
userId
📌 Primary subscription for real-time updates on both sales and items.
subscription SaleActivity($saleId: ID!, $itemIdFilter: ItemIdsFilter) {
saleActivity(saleId: $saleId, itemIdFilter: $itemIdFilter) {
__typename
... on Sale {
id
status
}
... on Item {
id
status
currentBid
userBids {
id
amount
maxAmount
bidderIdentifier
}
bids {
amount
maxAmount
bidDate
bidderIdentifier
}
}
}
}Use Cases:
- Listen for all changes on sale object and item objects for a given
saleId - Track sale status changes (e.g. "OPEN" → "CLOSING")
- Update UI countdowns and prices dynamically
Auth: Optional, but required to get user scoped properties such as userBids
import { createClient } from 'graphql-ws';
// Create WebSocket client
const wsClient = createClient({
url: 'wss://client.api.basta.app/query',
connectionParams: () => {
return {
token: bidderToken, // Optional: include for authenticated subscriptions
};
},
});
// Subscribe to sale activity
const unsubscribe = wsClient.subscribe(
{
query: `
subscription SaleActivity($saleId: ID!) {
saleActivity(saleId: $saleId) {
__typename
... on Sale {
id
status
}
... on Item {
id
status
currentBid
userBids {
id
amount
maxAmount
}
}
}
}
`,
variables: {
saleId: 'sale-123',
},
},
{
next: (data) => {
console.log('Received update:', data);
},
error: (error) => {
console.error('Subscription error:', error);
},
complete: () => {
console.log('Subscription completed');
},
}
);
// Clean up when done
// unsubscribe();import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { getMainDefinition } from '@apollo/client/utilities';
import { createClient } from 'graphql-ws';
import { useSubscription, gql } from '@apollo/client';
// Create HTTP link for queries and mutations
const httpLink = new HttpLink({
uri: 'https://client.api.basta.app/graphql',
});
// Create WebSocket link for subscriptions
const wsLink = new GraphQLWsLink(
createClient({
url: 'wss://client.api.basta.app/query',
connectionParams: () => ({
token: getBidderToken(), // Your function to get the token
}),
})
);
// Split based on operation type
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink
);
// Create Apollo Client
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
// Use in component
const SALE_ACTIVITY_SUBSCRIPTION = gql`
subscription SaleActivity($saleId: ID!) {
saleActivity(saleId: $saleId) {
__typename
... on Sale {
id
status
}
... on Item {
id
status
currentBid
userBids {
id
amount
maxAmount
}
}
}
}
`;
function AuctionMonitor({ saleId }) {
const { data, loading, error } = useSubscription(
SALE_ACTIVITY_SUBSCRIPTION,
{
variables: { saleId },
}
);
if (loading) return <div>Connecting...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h2>Real-time Auction Updates</h2>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}require 'graphql/client'
require 'graphql/client/http'
# Configure HTTP adapter for schema introspection
HTTP = GraphQL::Client::HTTP.new('https://client.api.basta.app/graphql')
# Load schema
Schema = GraphQL::Client.load_schema(HTTP)
# Create client
Client = GraphQL::Client.new(
schema: Schema,
execute: HTTP
)
# Define subscription
SaleActivitySubscription = Client.parse <<-GRAPHQL
subscription SaleActivity($saleId: ID!) {
saleActivity(saleId: $saleId) {
__typename
... on Sale {
id
status
}
... on Item {
id
status
currentBid
}
}
}
GRAPHQL
# Note: For WebSocket subscriptions in Ruby, you'll need to use
# a WebSocket client library like faye-websocket or action_cable_client
# and implement the graphql-ws protocol manually.import asyncio
from gql import gql, Client
from gql.transport.websockets import WebsocketsTransport
# Create WebSocket transport
transport = WebsocketsTransport(
url='wss://client.api.basta.app/query',
init_payload={'token': bidder_token} # Optional
)
# Create client
async def subscribe_to_auction():
async with Client(
transport=transport,
fetch_schema_from_transport=True,
) as session:
# Define subscription
subscription = gql("""
subscription SaleActivity($saleId: ID!) {
saleActivity(saleId: $saleId) {
__typename
... on Sale {
id
status
}
... on Item {
id
status
currentBid
}
}
}
""")
# Subscribe and handle updates
async for result in session.subscribe(
subscription,
variable_values={'saleId': 'sale-123'}
):
print(f"Received update: {result}")
# Run subscription
asyncio.run(subscribe_to_auction())- Handle Reconnection: Implement automatic reconnection logic for network interruptions
- Cleanup: Always unsubscribe when components unmount or when subscriptions are no longer needed
- Error Handling: Implement proper error handling for connection failures
- Token Refresh: If using authenticated subscriptions, handle token expiration and refresh
- Selective Subscriptions: Use
itemIdFilterto subscribe only to specific items when needed
- Verify the WebSocket endpoint URL is correct
- Check that your bidder token is valid (if using authenticated mode)
- Ensure your firewall allows WebSocket connections
- Verify you're subscribed to the correct
saleId - Check that the sale is in an active state
- Confirm authentication if trying to access user-scoped data
- Consider your network conditions
- Check if you're subscribed to too many items simultaneously
- Use
itemIdFilterto reduce the volume of updates
For questions about subscriptions:
- Email: hi@basta.app
- Documentation: docs.basta.ai