npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@struktos/adapter-express

v0.1.0

Published

Express.js adapter for Struktos - Enterprise-grade context propagation and request lifecycle management

Downloads

3

Readme

@struktos/adapter-express

Express.js adapter for Struktos - Enterprise-grade context propagation and request lifecycle management

npm version License: MIT

🎯 What is this?

@struktos/adapter-express seamlessly integrates @struktos/core with Express.js applications, providing:

  • Automatic Context Propagation - Go-style context flows through your entire request lifecycle
  • Request Cancellation - Gracefully handle client disconnects and stop wasting resources
  • TraceID Generation - Built-in distributed tracing support
  • Zero Configuration - Works out of the box with sensible defaults
  • Full TypeScript Support - Type-safe APIs with excellent IDE support

📦 Installation

npm install @struktos/core @struktos/adapter-express express

🚀 Quick Start

import express from 'express';
import { createStruktosMiddleware, RequestContext } from '@struktos/adapter-express';

const app = express();

// Step 1: Add Struktos middleware
app.use(createStruktosMiddleware({
  generateTraceId: () => crypto.randomUUID(),
  extractUserId: (req) => req.user?.id
}));

// Step 2: Access context anywhere in your handlers
app.get('/api/users/:id', async (req, res) => {
  const ctx = RequestContext.current();
  const traceId = ctx?.get('traceId');
  
  console.log(`Processing request ${traceId}`);
  
  // Context automatically propagates through all async calls!
  const user = await fetchUserFromDB(req.params.id);
  
  res.json({ user, traceId });
});

app.listen(3000);

That's it! Context now flows automatically through your entire application.

✨ Features

Automatic Context Propagation

Stop passing context objects through every function:

// ❌ Before: Manual context passing
async function processOrder(ctx, orderId) {
  const user = await getUser(ctx, userId);
  const items = await getItems(ctx, orderId);
  // ...
}

// ✅ After: Automatic propagation
async function processOrder(orderId) {
  const ctx = RequestContext.current();
  const traceId = ctx?.get('traceId');
  
  // Context flows automatically!
  const user = await getUser(userId);
  const items = await getItems(orderId);
  // ...
}

Request Cancellation

Handle client disconnects gracefully:

app.get('/api/long-task', async (req, res) => {
  const ctx = RequestContext.current();
  
  // Register cleanup handler
  ctx?.onCancel(() => {
    console.log('Client disconnected - cleaning up');
    // Close DB connections, stop processing, etc.
  });
  
  // Check cancellation periodically in long-running tasks
  for (let i = 0; i < 1000; i++) {
    if (ctx?.isCancelled()) {
      return res.status(499).json({ message: 'Request cancelled' });
    }
    
    await processChunk(i);
  }
  
  res.json({ status: 'completed' });
});

TraceID & Request Tracking

Built-in support for distributed tracing:

app.use(createStruktosMiddleware({
  generateTraceId: () => `trace-${Date.now()}-${randomBytes(4).toString('hex')}`,
  onCancel: (req, traceId) => {
    logger.info(`Request cancelled`, { traceId, url: req.url });
  }
}));

app.get('/api/data', async (req, res) => {
  const ctx = RequestContext.current();
  const traceId = ctx?.get('traceId');
  
  // Use traceId for logging, distributed tracing, etc.
  logger.info('Fetching data', { traceId });
  
  res.json({ traceId });
});

📚 API Reference

createStruktosMiddleware(options)

Creates the main Struktos middleware.

Options

interface StruktosMiddlewareOptions {
  // Function to generate trace ID (default: crypto.randomUUID())
  generateTraceId?: () => string;
  
  // Function to generate request ID (default: crypto.randomUUID())
  generateRequestId?: () => string;
  
  // Extract user ID from request
  extractUserId?: (req: Request) => string | undefined;
  
  // Add custom context data
  additionalContext?: (req: Request) => Record<string, any>;
  
  // Error handler for context initialization
  onError?: (error: Error, req: Request, res: Response) => void;
  
  // Callback when request is cancelled
  onCancel?: (req: Request, traceId?: string) => void;
  
  // Enable cancellation handling (default: true)
  enableCancellation?: boolean;
  
  // Custom context extractor
  contextExtractor?: (req: Request) => Partial<StruktosContextData>;
}

Example

app.use(createStruktosMiddleware({
  generateTraceId: () => uuidv4(),
  extractUserId: (req) => req.session?.userId,
  additionalContext: (req) => ({
    userAgent: req.headers['user-agent'],
    ip: req.ip
  }),
  onCancel: (req, traceId) => {
    metrics.increment('requests.cancelled', { traceId });
  }
}));

createContextLoggerMiddleware()

Middleware to log context information for debugging:

if (process.env.DEBUG === 'true') {
  app.use(createContextLoggerMiddleware());
}

createCancellationCheckMiddleware()

Middleware to check if request has been cancelled:

// Add before expensive operations
app.use('/api/expensive', createCancellationCheckMiddleware());

Accessing Context

import { RequestContext } from '@struktos/adapter-express';

// Get current context
const ctx = RequestContext.current();

// Get values
const traceId = ctx?.get('traceId');
const userId = ctx?.get('userId');

// Set values
ctx?.set('customData', { foo: 'bar' });

// Check cancellation
if (ctx?.isCancelled()) {
  // Handle cancellation
}

// Register cleanup
ctx?.onCancel(() => {
  // Cleanup code
});

StruktosRequest

Enhanced request object with context helpers:

import { StruktosRequest } from '@struktos/adapter-express';

app.get('/api/data', (req: StruktosRequest, res) => {
  // Direct access to context metadata
  console.log(req.traceId);
  console.log(req.requestId);
  console.log(req.requestTimestamp);
});

🏗️ Architecture

HTTP Request
    ↓
Express App
    ↓
createStruktosMiddleware()
    ↓
RequestContext.run({ traceId, userId, ... })
    ↓
[Your Route Handlers]
    ↓
[Service Layer] ← Context automatically available
    ↓
[Data Layer] ← Context automatically available
    ↓
[External APIs] ← Context automatically available

📊 Examples

Basic Usage

import express from 'express';
import { createStruktosMiddleware, RequestContext } from '@struktos/adapter-express';

const app = express();

app.use(createStruktosMiddleware());

app.get('/api/hello', (req, res) => {
  const ctx = RequestContext.current();
  res.json({
    message: 'Hello!',
    traceId: ctx?.get('traceId')
  });
});

app.listen(3000);

With Authentication

app.use(createStruktosMiddleware({
  extractUserId: (req) => {
    // Extract from JWT
    const token = req.headers.authorization?.split(' ')[1];
    if (token) {
      const decoded = jwt.verify(token, SECRET);
      return decoded.userId;
    }
  }
}));

app.get('/api/profile', async (req, res) => {
  const ctx = RequestContext.current();
  const userId = ctx?.get('userId');
  
  if (!userId) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  
  const profile = await getUserProfile(userId);
  res.json(profile);
});

Long-Running Tasks

app.get('/api/process-batch', async (req, res) => {
  const ctx = RequestContext.current();
  const items = await getItemsToProcess();
  
  for (let i = 0; i < items.length; i++) {
    // Check if client disconnected
    if (ctx?.isCancelled()) {
      return res.status(499).json({
        message: 'Processing cancelled',
        processed: i,
        total: items.length
      });
    }
    
    await processItem(items[i]);
  }
  
  res.json({ status: 'completed', total: items.length });
});

Error Handling

app.use(createStruktosMiddleware({
  onError: (error, req, res) => {
    logger.error('Context initialization failed', { error, url: req.url });
    res.status(500).json({
      error: 'Internal Server Error',
      message: 'Failed to initialize request context'
    });
  }
}));

🔗 Integration with Logging

import winston from 'winston';

const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [new winston.transports.Console()]
});

// Create logger wrapper that adds traceId
function log(level: string, message: string, meta?: any) {
  const ctx = RequestContext.current();
  const traceId = ctx?.get('traceId');
  
  logger.log(level, message, { ...meta, traceId });
}

app.get('/api/data', async (req, res) => {
  log('info', 'Fetching data');
  const data = await fetchData();
  log('info', 'Data fetched successfully', { count: data.length });
  res.json(data);
});

🧪 Testing

import request from 'supertest';
import { createStruktosMiddleware } from '@struktos/adapter-express';

describe('API Tests', () => {
  let app;
  
  beforeEach(() => {
    app = express();
    app.use(createStruktosMiddleware());
    // ... setup routes
  });
  
  it('should propagate context', async () => {
    const response = await request(app)
      .get('/api/hello')
      .expect(200);
      
    expect(response.body).toHaveProperty('traceId');
  });
});

🤝 Related Packages

  • @struktos/core - Core context propagation engine
  • @struktos/adapter-fastify (coming soon) - Fastify adapter
  • @struktos/auth (coming soon) - Authentication system

📄 License

MIT © Struktos.js Team

🔗 Links


Built with ❤️ for enterprise Node.js development