app.ts
Overview
[app.ts](/projects/291/68852) serves as the main entry point for the ShapeShift Bitcoin API server. It sets up an Express.js-based REST API alongside a WebSocket server that interfaces with a blockchain indexer (Blockbook) to provide real-time Bitcoin blockchain data and transaction information. The file orchestrates middleware, routing, WebSocket connection handling, and blockchain event processing, integrating various components such as service controllers, subscription registries, and metrics monitoring.
Key functionalities include:
Exposing REST endpoints for health checks, metrics, and Bitcoin blockchain data.
Serving interactive API documentation using Swagger UI.
Connecting to a Bitcoin indexer WebSocket endpoint to receive new block and transaction events.
Managing WebSocket client connections to stream real-time blockchain updates to subscribed clients.
Handling blockchain event processing through specialized handlers and a registry.
Collecting and exposing Prometheus metrics for observability.
Centralized logging with configurable verbosity.
This file acts as the gateway layer between client applications and underlying Bitcoin blockchain data sources, abstracting blockchain complexities and providing a consistent, developer-friendly API surface.
Detailed Explanations
Imports and Constants
Imports express, path utilities, WebSocket server, Swagger UI, logging, middleware, blockchain-related handlers, and internal controller and routes.
Reads environment variables for server port, indexer WebSocket URL, and API key.
Throws an error if
INDEXER_WS_URLis not set, as it's critical for blockchain data ingestion.Determines indexer type flags (
IS_LIQUIFY,IS_NOWNODES) based on the URL string for conditional logic.
Logger Initialization
const logger = new Logger({
namespace: ['unchained', 'coinstacks', 'bitcoin', 'api'],
level: process.env.LOG_LEVEL,
})
Creates a namespaced logger instance to categorize logs related to the Bitcoin API.
Log level is configurable via environment variables for dynamic verbosity control.
Prometheus Metrics
const prometheus = new Prometheus({ coinstack: 'bitcoin' })
Initializes a Prometheus metrics collector scoped to the Bitcoin coinstack.
Used throughout the app to track request counts, error rates, and WebSocket connections.
Express App Setup
const app = express()
app.use(...middleware.common(prometheus))
app.get('/health', async (_, res) => res.json({ status: 'up', network: 'bitcoin', connections: wsServer.clients.size }))
app.get('/metrics', async (_, res) => {
res.setHeader('Content-Type', prometheus.register.contentType)
res.send(await prometheus.register.metrics())
})
Creates an Express app instance.
Uses common middleware for logging, CORS, JSON parsing, and metrics instrumentation.
Defines
/healthendpoint returning service status and the number of active WebSocket clients.Defines
/metricsendpoint exposing Prometheus metrics in the required format.
Swagger UI Setup
const options = {
customCss: '.swagger-ui .topbar { display: none }',
customSiteTitle: 'ShapeShift Bitcoin API Docs',
customfavIcon: '/public/favi-blue.png',
swaggerUrl: '/swagger.json',
}
app.use('/public', express.static(join(__dirname, '../../../../../../common/api/public')))
app.use('/swagger.json', express.static(join(__dirname, './swagger.json')))
app.use('/docs', swaggerUi.serve, swaggerUi.setup(undefined, options))
Serves static assets for the Swagger UI documentation.
Configures Swagger UI with custom branding, favicon, and hides the default topbar.
Provides
/docsroute hosting interactive API docs.Serves Swagger specification JSON at
/swagger.json.
Route Registration and Error Handlers
RegisterRoutes(app)
// Redirect unmatched routes to API docs
app.get('/', async (_, res) => {
res.redirect('/docs')
})
app.use(middleware.errorHandler, middleware.notFoundHandler)
Registers all API routes from the
routesmodule.Redirects root route
/to API documentation.Sets up generic middleware for handling errors and 404 Not Found responses.
Blockchain Event Handlers
Block Handler
const blockHandler: BlockHandler<NewBlock, Array<BlockbookTx>> = async (block) => {
const txs = await service.handleBlock(block.hash)
return { txs }
}
Handles incoming new block notifications.
Invokes the controller service to fetch and process all transactions within the block.
Returns the processed transactions for further dispatch.
Transaction Handler
const transactionHandler: TransactionHandler<BlockbookTx, utxo.Tx> = async (blockbookTx) => {
const tx = service.handleTransaction(blockbookTx)
const addresses = getAddresses(blockbookTx)
return { addresses, tx }
}
Processes individual transactions received from the indexer.
Parses and enriches the transaction data via the controller.
Extracts all addresses involved in the transaction using Blockbook utilities.
Returns both the processed transaction and associated addresses.
Registry Setup
const registry = new Registry({ addressFormatter: formatAddress, blockHandler, transactionHandler })
Creates a
Registryinstance responsible for managing WebSocket client subscriptions by address.Uses custom address formatting logic to maintain consistent address representation.
Delegates blockchain event processing to the defined block and transaction handlers.
Registry orchestrates dispatching relevant blockchain events only to subscribed clients.
WebSocket Client to Blockchain Indexer
const wsUrl = INDEXER_API_KEY && IS_LIQUIFY ? `${INDEXER_WS_URL}/api=${INDEXER_API_KEY}` : INDEXER_WS_URL
const apiKey = INDEXER_API_KEY && IS_NOWNODES ? INDEXER_API_KEY : undefined
const blockbook = new WebsocketClient(
wsUrl,
{
blockHandler: registry.onBlock.bind(registry),
transactionHandler: registry.onTransaction.bind(registry),
apiKey,
},
{ resetInterval: 15 * 60 * 1000 } // 15 minutes
)
Constructs the WebSocket URL with API key if required by specific indexer providers (Liquify, Nownodes).
Instantiates a
WebsocketClientthat connects to the Bitcoin indexer.Passes event handlers bound to the registry for processing blocks and transactions.
Configured to reset the connection every 15 minutes to ensure stability.
HTTP and WebSocket Server Initialization
const server = app.listen(PORT, () => logger.info('Server started'))
const wsServer = new Server({ server })
wsServer.on('connection', (connection) => {
ConnectionHandler.start(connection, registry, blockbook, prometheus, logger)
})
Starts the Express HTTP server on the configured port.
Creates a WebSocket server bound to the same HTTP server.
On new WebSocket connection, delegates handling to
ConnectionHandler.start, passing the registry, blockchain client, metrics, and logger.This enables real-time blockchain event subscription and notification for connected clients.
Usage Examples
Starting the Server
Set environment variables:
export PORT=3000
export INDEXER_WS_URL=wss://bitcoin-indexer.example.com
export INDEXER_API_KEY=your_api_key_here
export LOG_LEVEL=info
Run the server:
node dist/app.js
Accessing API Docs and Health
Open
http://localhost:3000/docsin a browser to explore the interactive API documentation.Check service health at
http://localhost:3000/health.View Prometheus metrics at
http://localhost:3000/metrics.
Subscribing via WebSocket
Clients connect to the WebSocket server on the same port and subscribe to Bitcoin address updates. On connection:
const ws = new WebSocket('ws://localhost:3000')
ws.onopen = () => {
ws.send(JSON.stringify({ method: 'subscribe', params: { addresses: ['bitcoinAddress1'] } }))
}
ws.onmessage = (message) => {
console.log('Received blockchain event:', message.data)
}
Important Implementation Details and Algorithms
Registry Pattern: The
Registryclass manages active client subscriptions by Bitcoin address. It efficiently routes incoming blockchain events only to clients interested in particular addresses, minimizing unnecessary data flow.Event-Driven Architecture: The app uses asynchronous event handlers for new blocks and transactions. These handlers rely on the service controller to parse, enrich, and format blockchain data before forwarding.
Dynamic Indexer Integration: The code handles multiple blockchain indexer providers (Liquify, Nownodes) with conditional URL and API key injection, allowing seamless integration without code changes.
WebSocket Connection Resilience: The
WebsocketClientis configured with a periodic reset interval (15 minutes) to mitigate stale or dropped connections to the blockchain indexer.Middleware Composition: Common Express middleware is composed with Prometheus instrumentation to provide observability and robust error handling.
Swagger UI Customization: The embedded API documentation is branded and styled for a streamlined developer experience.
Interaction with Other Parts of the System
Controller Module (
controller.ts): Provides core business logic for processing blocks and transactions, formatting addresses, and handling chain-specific details.Routes Module (
routes.ts): Defines all REST API endpoints, which are registered with the Express app.Common API Library (
@shapeshiftoss/common-api): Supplies middleware, connection handlers, registries, and Prometheus integration used by this server.Blockchain Indexer (
@shapeshiftoss/blockbook): Supplies utilities for parsing transactions and addresses, and the WebSocket client for receiving blockchain event streams.Logger (
@shapeshiftoss/logger): Provides standardized logging across the application.Prometheus Monitoring: Collects and exposes metrics for monitoring usage, errors, and system health.
Clients (Wallets, dApps): Connect via REST and WebSocket APIs for querying Bitcoin blockchain data and receiving live updates.
Diagram: Class and Component Structure
classDiagram
class App {
+expressApp: Express
+wsServer: Server
+logger: Logger
+prometheus: Prometheus
+registry: Registry
+blockbookClient: WebsocketClient
+start(): void
}
class Registry {
+addressFormatter: Function
+blockHandler: Function
+transactionHandler: Function
+onBlock(block): void
+onTransaction(tx): void
}
class WebsocketClient {
+constructor(url: string, handlers: Object, options: Object)
+connect(): void
+reset(): void
}
class ConnectionHandler {
+start(connection, registry, blockbook, prometheus, logger): void
}
App --> Express
App --> Logger
App --> Prometheus
App --> Registry
App --> WebsocketClient
WebsocketClient --> Registry : uses handlers
wsServer "0..*" --> ConnectionHandler : manages connections
Summary
The [app.ts](/projects/291/68852) file is the core bootstrapper of the ShapeShift Bitcoin API server, combining REST and WebSocket interfaces to deliver rich blockchain data and events. It integrates a modular architecture leveraging controllers, registries, and indexer clients to provide scalable, real-time Bitcoin blockchain services. The file orchestrates middleware, routing, WebSocket connection management, and blockchain event processing with observability and error handling baked in. This design enables client applications to interact with Bitcoin blockchain data efficiently and reliably through a unified API.