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

@delta-base/auth

v0.2.7

Published

DeltaBase Auth Service

Readme

Auth Service

A clean architecture implementation for user authentication and authorization built with event sourcing and CQRS patterns.

Architecture Overview

This service follows Clean Architecture principles with clear separation of concerns across three main layers:

graph TD
    A[HTTP Request] --> B[Route Layer]
    B --> C[Handler Layer]
    C --> D[Domain Layer]
    
    subgraph "Route Layer - HTTP/Infrastructure Concerns"
        B1[Input Validation]
        B2[Authentication Check<br/>is user logged in?]
        B3[Response Formatting]
        B4[HTTP Status Codes]
        B5[Error Mapping]
    end
    
    subgraph "Handler Layer - Application Concerns"
        C1[Authorization Check<br/>can user perform action?]
        C2[Permission Validation]
        C3[Cross-cutting Concerns]
        C4[Orchestration]
    end
    
    subgraph "Domain Layer - Business Logic"
        D1[Business Rules]
        D2[Domain Validation]
        D3[State Changes]
        D4[Event Generation]
    end
    
    B --> B1
    B1 --> B2
    B2 --> C1
    C1 --> C2
    C2 --> C4
    C4 --> D1
    D1 --> D2
    D2 --> D3
    D3 --> D4
    
    style B fill:#e1f5fe
    style C fill:#f3e5f5
    style D fill:#e8f5e8

Quick Setup

Prerequisites

  1. Start your Deltabase platform (required):

    # From project root
    pnpm --filter @delta-base/core dev
  2. Install dependencies:

    # From auth package directory
    pnpm install

Environment Setup

Your environment variables are configured in .dev.vars:

DELTABASE_URL=http://localhost:8787      # Platform API URL
DELTABASE_API_KEY=dev-key               # API key for platform access
DELTABASE_EVENT_STORE_ID=auth-service   # Event store name

Initial Setup (Idempotent)

Run the setup script to create your event store and subscriptions:

# For development (uses local platform)
pnpm run setup:dev

# For production (uses environment variables)
pnpm run setup

The setup script will:

  1. ✅ Create the auth-service event store (idempotent)
  2. ✅ Setup projection subscriptions for user events (idempotent)
  3. ✅ Validate the configuration
  4. ✅ Display the event store ID for your .dev.vars

Example output:

🚀 Starting Auth Service setup
📍 Target: http://localhost:8787
🏪 Setting up event store: auth-service
✅ Event store ready: es_2vbr3EKPNnmBfPJpUi0bl
🔗 Setting up projection subscriptions
✅ Users projection subscription created: auth-users-projection
🔍 Validating setup
✅ Event store accessible (0 streams found)
✅ Event bus accessible (1 subscriptions found)
✨ Auth Service setup completed successfully!

🎉 Setup complete! Next steps:
1. Update your .dev.vars with the event store ID:
   DELTABASE_EVENT_STORE_ID=es_2vbr3EKPNnmBfPJpUi0bl
2. Run your auth service to start processing events
3. Check the deltabase studio for real-time monitoring

Development Workflow

# 1. Run setup (first time or when adding new projections)
pnpm run setup:dev

# 2. Start development server
pnpm run dev

# 3. Your service is now running with:
#    - Event sourcing configured
#    - Projections subscribed and processing
#    - Real-time monitoring available

Production Deployment

  1. Set environment variables in your production environment:

    DELTABASE_URL=https://your-platform.example.com
    DELTABASE_API_KEY=your-production-api-key
    DELTABASE_EVENT_STORE_NAME=auth-service-prod
  2. Run setup in production:

    pnpm run setup
  3. Deploy your service with the returned event store ID.

Layer Responsibilities

| Layer | Authentication | Authorization | Business Rules | HTTP Concerns | |-------|---------------|---------------|----------------|---------------| | Route | ✅ Basic (JWT valid?) | ❌ | ❌ | ✅ | | Handler | ❌ | ✅ Permissions | ❌ | ❌ | | Domain | ❌ | ❌ | ✅ Business Logic | ❌ |

🌐 Route Layer

Purpose: Handle HTTP transport concerns and external interface

Responsibilities:

  • Input validation and sanitization
  • Basic authentication checks (is user logged in?)
  • Response formatting and HTTP status codes
  • Error message mapping for clients
  • Rate limiting and request throttling

Example:

export async function createUserRoute(
  eventStore: EventStore,
  actor: Actor,
  request: CreateUserRouteRequest
): Promise<CreateUserRouteResponse | CreateUserRouteError> {
  // 1. Input validation
  if (!request.email || !request.firstName || !request.lastName) {
    return { success: false, message: 'Missing required fields' };
  }

  // 2. Basic authentication check
  if (!actor || actor.type === 'public') {
    return { success: false, message: 'Authentication required' };
  }

  // 3. Call handler (authorization happens there)
  const result = await createUser(eventStore, actor, request);

  // 4. Format response
  return {
    success: true,
    message: 'User created successfully',
    data: { id: result.id, user: result.user }
  };
}

🛡️ Handler Layer

Purpose: Application orchestration and cross-cutting concerns

Responsibilities:

  • Permission-based authorization (can user perform this action?)
  • Role-based access control
  • Cross-cutting concerns (logging, metrics, caching)
  • Orchestrating calls to domain and infrastructure services

Example:

export async function createUser(
  eventStore: EventStore,
  actor: Actor,
  request: CreateUserRequest
): Promise<CreateUserResult> {
  // Authorization check at application level
  requireUserCreationPermission(actor);
  
  const streamId = Users.generateStreamId(request.email);
  
  const command: Users.CreateUser = {
    type: 'user.create',
    data: request,
    metadata: { actor },
  };

  const result = await handleCommandWithDecider(
    eventStore,
    streamId,
    command,
    Users.decider,
  );

  return {
    id: streamId,
    user: result.newState,
    nextExpectedStreamVersion: result.appendResult.nextExpectedStreamVersion,
  };
}

🏛️ Domain Layer

Purpose: Pure business logic and domain rules

Responsibilities:

  • Business rule validation
  • State transitions and invariants
  • Event generation
  • Domain-specific constraints

Example:

export const createUserHandler = (command: CreateUser, state: UserEntity): UserCreated => {
  // Business rule: User must not already exist
  if (state.id && state.id !== '') {
    throw new IllegalStateError(`user with email ${command.data.email} already exists`);
  }

  // Generate domain event
  return {
    type: 'user.created',
    data: {
      id: generateStreamId(command.data.email),
      firstName: command.data.firstName,
      lastName: command.data.lastName,
      email: command.data.email
    },
    metadata: { actor: command.metadata.actor }
  };
};

Authorization Patterns

Simple Permission Check

function requireUserCreationPermission(actor: Actor): void {
  switch (actor.type) {
    case 'user':
      if (!actor.properties.userID || !actor.properties.organizationID) {
        throw new Error('User actor must have userID and organizationID');
      }
      break;
    
    case 'system':
      if (!actor.properties.organizationID) {
        throw new Error('System actor must have organizationID');
      }
      break;
    
    case 'public':
      throw new Error('Public actors cannot create users');
    
    default:
      throw new Error('Invalid actor type');
  }
}

Advanced Permission Service

export interface PermissionService {
  hasPermission(actor: Actor, permission: string, resource?: string): Promise<boolean>;
}

async function requireUserCreationPermissionAsync(
  actor: Actor,
  permissionService: PermissionService
): Promise<void> {
  const hasPermission = await permissionService.hasPermission(
    actor,
    'users:create',
    'organization'
  );
  
  if (!hasPermission) {
    throw new Error('Permission denied: users:create required');
  }
}

Project Structure

packages/auth/
├── src/
│   ├── core/
│   │   ├── actor.ts           # Actor types and utilities
│   │   ├── context.ts         # Context management
│   │   └── users/
│   │       ├── users.ts       # User domain logic
│   │       └── users.test.ts  # Domain tests
│   ├── functions/
│   │   └── users/
│   │       └── create-user/
│   │           ├── handler.ts # Application service
│   │           └── route.ts   # HTTP route handler
│   ├── shared/               # Shared utilities
│   ├── index.ts             # Public API
│   └── subject.ts           # Subject management
├── package.json
├── tsconfig.json
└── README.md

Key Design Decisions

1. Functional over Object-Oriented

  • Why: Simpler, more testable, better composability
  • Pattern: Pure functions with dependency injection via parameters

2. No Messages in Business Layer

  • Why: Messages are presentation concerns, not business concerns
  • Pattern: Handler returns data, route formats messages

3. Authorization in Handler Layer

  • Why: Application-level concern, reusable across transports
  • Pattern: Permission checks before domain operations

4. Event Sourcing with CQRS

  • Why: Audit trail, temporal queries, scalability
  • Pattern: Commands → Events → Projections

Usage Examples

Basic User Creation

import { createUser } from '@/functions/users/create-user/handler';

const result = await createUser(eventStore, actor, {
  email: '[email protected]',
  firstName: 'John',
  lastName: 'Doe'
});

With Permission Service

import { createUserWithPermissions } from '@/functions/users/create-user/handler';

const result = await createUserWithPermissions(
  eventStore,
  permissionService,
  actor,
  request
);

HTTP Route

import { createUserRoute } from '@/functions/users/create-user/route';

app.post('/users', async (req, res) => {
  const result = await createUserRoute(eventStore, actor, req.body);
  
  if (result.success) {
    res.status(201).json(result);
  } else {
    res.status(400).json(result);
  }
});

Testing Strategy

Unit Tests

  • Domain: Test business rules in isolation
  • Handlers: Test authorization and orchestration
  • Routes: Test HTTP concerns and error handling

Integration Tests

  • Event Store: Test event persistence and replay
  • Full Flow: Test complete request/response cycles

Example Test

describe('createUser', () => {
  it('should reject public actors', async () => {
    const actor: Actor = { type: 'public', properties: {} };
    
    await expect(createUser(eventStore, actor, request))
      .rejects.toThrow('Public actors cannot create users');
  });
});

Best Practices

  1. Keep handlers thin: Orchestrate, don't implement business logic
  2. Pure domain functions: No side effects in domain layer
  3. Explicit dependencies: Pass dependencies as parameters
  4. Type safety: Use TypeScript strictly for better DX
  5. Test each layer: Unit test business logic, integration test flows
  6. Error boundaries: Handle errors at appropriate layers
  7. Documentation: Document business rules and authorization policies

Contributing

  1. Follow the layer responsibilities outlined above
  2. Write tests for new functionality
  3. Keep functions pure and composable
  4. Use meaningful error messages
  5. Document authorization requirements