Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

Client API

The Client API is designed for use in client-side environments such as web or mobile apps. It powers user-facing auction interactions and supports both public reads and authenticated user actions.

🧭 Access Modes

Mode Use Case Auth Mechanism
Public Unauthenticated reads on auction objects No auth required
Authenticated (User) Placing bids, receiving user-scoped subscriptions JWT in Authorization header

🔑 Bidder Tokens (JWT)

Since Basta does not manage users for integrating businesses, businesses generate their own bidder tokens via the Management API.

These JWT tokens:

  • Contain a userId and ttl
  • Give user permissions to make bids
  • Are added to the Authorization header on Client API requests

Example Header

{
  "Authorization": "Bearer <BIDDER_JWT>"
}

📌 The userId embedded in the token is used to resolve bids and subscriptions in the user's context.

💡 Capabilities

Unauthenticated Queries

Retrieve auction listings, timing details, bid history, etc.

query GetAuctions {
  sales {
    id
    title
    startDate
    endDate
    items {
      id
      title
      currentBid
    }
  }
}

Authenticated Mutations

Place bids via BidOnItem using a bidder token:

mutation PlaceBid {
  bidOnItem(input: {
    saleId: "sale-123"
    itemId: "item-456"
    amount: 1000
  }) {
    bidId
    amount
    status
  }
}

User-Scoped Subscriptions (WebSockets)

Subscribe to auction events in real-time, filtered by userId in token:

subscription WatchAuction {
  auctionUpdates(saleId: "sale-123") {
    itemId
    currentBid
    bidder {
      id
      isMe
    }
  }
}

🔗 GraphQL Explorer

Explore the Client API: client.api.basta.app

Code Examples

JavaScript/TypeScript - Public Query

// No authentication needed for public queries
const response = await fetch('https://client.api.basta.app/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    query: `
      query GetAuctions {
        sales {
          id
          title
          startDate
          endDate
        }
      }
    `
  })
});

const { data } = await response.json();

JavaScript/TypeScript - Authenticated Mutation

// First, get a bidder token from your backend (which calls Management API)
const bidderToken = await fetchBidderTokenFromBackend(userId);

// Then use it to place a bid
const response = await fetch('https://client.api.basta.app/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${bidderToken}`
  },
  body: JSON.stringify({
    query: `
      mutation PlaceBid($input: BidInput!) {
        bidOnItem(input: $input) {
          bidId
          amount
          status
        }
      }
    `,
    variables: {
      input: {
        saleId: 'sale-123',
        itemId: 'item-456',
        amount: 1000
      }
    }
  })
});

const { data } = await response.json();

React Example with Apollo Client

import { ApolloClient, InMemoryCache, gql, useQuery, useMutation } from '@apollo/client';

// Configure Apollo Client
const client = new ApolloClient({
  uri: 'https://client.api.basta.app/graphql',
  cache: new InMemoryCache(),
  headers: {
    // Add authorization header if user is authenticated
    ...(bidderToken && { Authorization: `Bearer ${bidderToken}` })
  }
});

// Query component
function AuctionList() {
  const { loading, error, data } = useQuery(gql`
    query GetAuctions {
      sales {
        id
        title
        items {
          id
          title
          currentBid
        }
      }
    }
  `);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      {data.sales.map(sale => (
        <div key={sale.id}>
          <h2>{sale.title}</h2>
          {sale.items.map(item => (
            <div key={item.id}>
              {item.title} - Current bid: ${item.currentBid}
            </div>
          ))}
        </div>
      ))}
    </div>
  );
}

// Mutation component
function BidButton({ saleId, itemId, amount }) {
  const [placeBid, { loading }] = useMutation(gql`
    mutation PlaceBid($input: BidInput!) {
      bidOnItem(input: $input) {
        bidId
        amount
        status
      }
    }
  `);

  const handleBid = async () => {
    try {
      const result = await placeBid({
        variables: {
          input: { saleId, itemId, amount }
        }
      });
      console.log('Bid placed:', result.data.bidOnItem);
    } catch (error) {
      console.error('Bid failed:', error);
    }
  };

  return (
    <button onClick={handleBid} disabled={loading}>
      {loading ? 'Placing bid...' : `Bid $${amount}`}
    </button>
  );
}

Ruby Example

require 'net/http'
require 'json'

class BastaClientAPI
  API_URL = 'https://client.api.basta.app/graphql'

  def initialize(bidder_token = nil)
    @bidder_token = bidder_token
  end

  # Public query - no auth needed
  def get_auctions
    query = <<~GRAPHQL
      query GetAuctions {
        sales {
          id
          title
          startDate
          endDate
        }
      }
    GRAPHQL

    execute_query(query)
  end

  # Authenticated mutation - requires bidder token
  def place_bid(sale_id:, item_id:, amount:)
    raise 'Bidder token required' unless @bidder_token

    query = <<~GRAPHQL
      mutation PlaceBid($input: BidInput!) {
        bidOnItem(input: $input) {
          bidId
          amount
          status
        }
      }
    GRAPHQL

    variables = {
      input: {
        saleId: sale_id,
        itemId: item_id,
        amount: amount
      }
    }

    execute_query(query, variables, authenticated: true)
  end

  private

  def execute_query(query, variables = {}, authenticated: false)
    uri = URI(API_URL)
    request = Net::HTTP::Post.new(uri)
    request['Content-Type'] = 'application/json'
    request['Authorization'] = "Bearer #{@bidder_token}" if authenticated && @bidder_token

    request.body = JSON.generate({
      query: query,
      variables: variables
    })

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    JSON.parse(response.body)
  end
end

# Usage
# Public access
public_api = BastaClientAPI.new
auctions = public_api.get_auctions

# Authenticated access
bidder_token = get_token_from_your_backend(user_id)
authenticated_api = BastaClientAPI.new(bidder_token)
result = authenticated_api.place_bid(
  sale_id: 'sale-123',
  item_id: 'item-456',
  amount: 1000
)

Rate Limiting

The Client API implements rate limiting to ensure fair usage and protect the service.

Rate Limit Details

  • Limit: 400 requests per 10 seconds
  • Scope: Per IP address and endpoint combination
  • Response: HTTP 429 Too Many Requests when exceeded

Rate Limit Headers

When making requests, you can monitor your rate limit status through response headers (if available):

  • X-RateLimit-Limit: Maximum requests allowed in the time window
  • X-RateLimit-Remaining: Number of requests remaining
  • X-RateLimit-Reset: Time when the rate limit resets

Example Response When Rate Limited

HTTP/1.1 429 Too Many Requests
Content-Type: text/plain

rate limit exceeded

Best Practices

  1. Implement Retry Logic: Add exponential backoff when receiving 429 responses
  2. Cache Public Data: Cache auction listings and item details to reduce API calls
  3. Use Subscriptions: For real-time updates, use GraphQL subscriptions instead of polling
  4. Batch Queries: Combine multiple data requirements into a single GraphQL query

Example: Retry Logic

async function queryWithRetry(query: string, variables: any, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch('https://client.api.basta.app/graphql', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ query, variables })
      });

      if (response.status === 429) {
        // Exponential backoff: 1s, 2s, 4s
        const waitTime = Math.pow(2, i) * 1000;
        console.log(`Rate limited. Retrying in ${waitTime}ms...`);
        await new Promise(resolve => setTimeout(resolve, waitTime));
        continue;
      }

      return await response.json();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
}

✅ Summary

The Client API offers full auction interactivity while keeping your users' identity management under your control. Basta provides a secure and flexible token-based mechanism that fits well with existing auth stacks.

Key Points

  • No auth required for public auction browsing
  • Bidder tokens (JWT) enable authenticated actions
  • Your backend generates tokens via Management API
  • User context is preserved via userId in token
  • Real-time updates via GraphQL subscriptions
  • Rate limits enforce fair usage (400 requests per 10 seconds)

Related Documentation

Support

For questions about the Client API: