A server-side link shortening service powered by Linkat
Code Architecture#
This document describes the modular architecture of the AT Protocol Link Shortener.
Directory Structure#
src/
├── lib/
│ ├── constants.ts # Application-wide constants
│ ├── index.ts # Main library exports
│ ├── services/
│ │ ├── atproto/ # AT Protocol services
│ │ │ ├── agent-factory.ts # Agent creation utilities
│ │ │ ├── agent-manager.ts # Agent caching and fallback
│ │ │ ├── identity-resolver.ts # Slingshot DID resolution
│ │ │ └── index.ts # Module exports
│ │ ├── cache/ # Caching utilities
│ │ │ └── index.ts # Generic TTL cache
│ │ ├── linkat/ # Linkat service
│ │ │ ├── fetcher.ts # Raw board fetching
│ │ │ ├── generator.ts # Shortcode generation
│ │ │ └── index.ts # Main service with caching
│ │ ├── agent.ts # Backwards compatibility
│ │ ├── linkat.ts # Backwards compatibility
│ │ └── types.ts # Shared type definitions
│ └── utils/
│ └── encoding.ts # URL encoding utilities
└── routes/
├── +layout.svelte # Global layout with dark mode
├── +page.server.ts # Homepage server logic
├── +page.svelte # Homepage UI
├── [shortcode]/
│ └── +server.ts # Redirect handler
├── api/
│ └── links/
│ └── +server.ts # Links API endpoint
└── favicon/
└── favicon.ico/
└── +server.ts # Favicon handler
Module Responsibilities#
Constants (lib/constants.ts)#
Central location for all configuration values:
- Cache settings (TTL, prefixes)
- Shortcode configuration (length, base62 chars)
- AT Protocol endpoints (Slingshot, public API)
- HTTP status codes
Benefits:
- Single source of truth
- Easy to modify configuration
- Type-safe constants
AT Protocol Services (lib/services/atproto/)#
agent-factory.ts#
- Creates AtpAgent instances
- Handles fetch function injection for server-side contexts
- Wraps fetch to ensure proper headers
identity-resolver.ts#
- Resolves DIDs to PDS endpoints via Slingshot
- Error handling and logging
- Uses constants for endpoint configuration
agent-manager.ts#
- Manages agent lifecycle and caching
- Provides fallback logic (PDS → public API)
- Exports helper functions for common operations
Benefits:
- Separation of concerns
- Easy to test individual components
- Reusable across different contexts
Cache Service (lib/services/cache/)#
Generic TTL-based cache implementation:
- Set/get/delete operations
- Automatic expiration
- Cache pruning utility
- Type-safe generic interface
Benefits:
- Reusable for any cached data
- Not tied to specific use case
- Clean API with proper TypeScript support
Linkat Service (lib/services/linkat/)#
fetcher.ts#
- Raw Linkat board data fetching
- AT Protocol record retrieval
- Data validation
generator.ts#
- Shortcode generation from URLs
- Collision handling
- Link search functionality
index.ts#
- Main service interface
- Combines fetching + generation
- Implements caching layer
Benefits:
- Pure functions in fetcher and generator (easy to test)
- Side effects isolated to main service
- Clear data flow
Utilities (lib/utils/)#
encoding.ts#
- URL to shortcode encoding
- Base62 conversion
- Validation helpers
Benefits:
- Stateless utility functions
- Reusable across application
- Easy to unit test
Backwards Compatibility#
The old agent.ts and linkat.ts files remain as re-export wrappers:
- Existing imports continue to work
- No breaking changes to route handlers
- Smooth migration path
Design Principles#
1. Single Responsibility#
Each module has one clear purpose:
agent-factory: Create agentsidentity-resolver: Resolve DIDscache: Cache data
2. Dependency Injection#
- Fetch functions can be injected
- Agents are created with custom configs
- Makes testing easier
3. Separation of Concerns#
- Pure logic in utilities
- Side effects in services
- UI in routes
4. Type Safety#
- Explicit TypeScript types
- Shared type definitions
- Constants with
as const
5. Testability#
- Pure functions are easy to test
- Services use dependency injection
- Clear interfaces
Import Patterns#
Using the main library export:#
import { getShortLinks, encodeUrl, CACHE } from '$lib';
Using specific modules:#
import { getPublicAgent } from '$lib/services/atproto';
import { Cache } from '$lib/services/cache';
Using backwards-compatible imports:#
import { createAgentForDID } from '$lib/services/agent';
import { findShortLink } from '$lib/services/linkat';
Future Improvements#
Easy to Add:#
- Database caching - Replace in-memory cache
- Custom shortcodes - Extend generator
- Analytics - Add tracking module
- Rate limiting - Add middleware
- Multiple DID support - Extend agent manager
Testing Strategy:#
- Unit tests for utils and pure functions
- Integration tests for services
- E2E tests for routes
Configuration#
All configuration is centralized in constants.ts:
- Change cache TTL in one place
- Update API endpoints easily
- Modify shortcode length globally
Error Handling#
Consistent error handling pattern:
- Log errors with context
- Throw with descriptive messages
- Fallback to sensible defaults
- Surface errors to users when appropriate