This is an Angular application that visualizes and explores data from an xRegistry-compliant API. It provides a user-friendly interface to browse registry groups, resources, versions, and detailed documents.
The application is model-driven, using the metadata provided by the xRegistry API's model endpoint and dynamically adapts its UI based on the resource schemas and types.
You can point the application to one of multiple xRegistry endpoints. The application will consolidate and display data from the selected registries in a unified interface, grouping metadata by model and groups.
The repository builds a Docker image with a production-ready Angular application served via a Node.js + Express server, including an API proxy for cross-origin requests.
- Features
- Prerequisites
- Quick Start
- Development Guide
- Docker Deployment
- Testing
- Architecture
- Contributing
- Browse Registry Data: Explore group types, groups, resources, and versions
- Detailed Views: View resource documents, schemas, and metadata
- Search & Filter: Quick search and advanced filtering capabilities
- Smart Display Modes: Automatic card/list view based on content density
- Sticky Page Reloads: Maintains your location across page refreshes (see below)
- API Proxy: Built-in proxy for secure cross-origin API requests
- Health Monitoring: Health check endpoints for production monitoring
The application preserves your navigation state during page refreshes, making development and bookmarking easier.
Use Cases:
- Bookmarking deep links to specific resources
- Development workflow with hot reloads
- Sharing URLs that point to specific registry items
How It Works:
- Current route is stored in browser session storage
- On page reload, app redirects to root and then auto-navigates to previous location
- Route storage is cleared on explicit home navigation to prevent redirect loops
Example:
Navigate to: /schemagroups/Contoso.ERP/schemas/Contoso.ERP.CancellationData
Reload page β automatically returns to the same resource detail view
Before you begin, ensure you have the following installed:
- Node.js: v20.19.3 or higher (LTS recommended)
- npm: v10.0.0 or higher (comes with Node.js)
- Angular CLI: v19.0.0 or higher
- Docker: v20.10.0 or higher (optional, for containerized deployment)
- Docker Compose: v2.0.0 or higher (optional, for containerized deployment)
Check your versions:
node --version # Should show v20.x.x or higher
npm --version # Should show v10.x.x or higher
ng version # Should show Angular CLI 19.x.x
docker --version # Optional: for Docker deploymentInstall Angular CLI globally (if not already installed):
npm install -g @angular/cli@19git clone https://github.com/xregistry/viewer.git
cd viewernpm installThis will install all required packages listed in package.json, including:
- Angular 19 framework and dependencies
- FluentUI Web Components for UI
- RxJS for reactive programming
- Development tools (TypeScript, Jest, etc.)
npm start
# or
ng serveThe application will be available at: http://localhost:4200/
The development server includes:
- Hot Module Replacement (HMR): Changes automatically reflected in browser
- Source Maps: For easier debugging
- Live Reload: Browser refreshes on file changes
xregistry-viewer/
βββ src/
β βββ app/
β β βββ components/ # UI components
β β βββ services/ # Business logic and API calls
β β βββ models/ # TypeScript interfaces and types
β β βββ utils/ # Helper functions
β β βββ app.routes.ts # Application routing
β βββ assets/ # Static files (images, fonts, etc.)
β βββ environments/ # Environment-specific configuration
β βββ styles/ # Global SCSS styles
β βββ index.html # Main HTML entry point
βββ docs/ # Documentation
βββ public/ # Public assets (favicon, config.json)
βββ server.js # Production Express server
βββ Dockerfile # Docker build configuration
βββ docker-compose.yml # Docker Compose orchestration
- Angular 19: Frontend framework with standalone components
- TypeScript 5.7: Strongly-typed JavaScript superset
- FluentUI Web Components: UI component library
- RxJS 7: Reactive programming library
- SCSS: CSS preprocessor for styling
- Jest: Testing framework
- Express.js 4: Production server (Docker deployment)
-
Create a Feature Branch
git checkout -b feature/my-new-feature
-
Make Changes
- Edit files in
src/app/ - Follow Angular style guide
- Use TypeScript strict mode
- Edit files in
-
Test Your Changes
npm test # Run unit tests npm run test:watch # Watch mode for TDD
-
Build for Production
npm run build-prod # Create production build -
Commit and Push
git add . git commit -m "feat: add new feature" git push origin feature/my-new-feature
The application needs to be configured with your xRegistry API endpoint.
- Click the Settings icon (βοΈ) in the application header
- Navigate to API Configuration
- Enter your API base URL (e.g.,
https://myregistry.example.com) - Click Save Changes
Configuration is persisted in browser local storage and survives page reloads.
For production deployments, configure the API via public/config.json:
{
"apiBaseUrl": "https://your-xregistry-api.com",
"registryEndpoint": "https://your-xregistry-api.com/api/v1"
}The application loads this configuration on startup. This approach is ideal for:
- Docker deployments
- CI/CD pipelines
- Environment-specific configurations
- Configuration without rebuilding the application
For build-time configuration, edit environment files:
Development (src/environments/environment.ts):
export const environment = {
production: false,
apiBaseUrl: 'https://dev.myregistry.example.com'
};Production (src/environments/environment.prod.ts):
export const environment = {
production: true,
apiBaseUrl: 'https://myregistry.example.com'
};Note: Environment files are baked into the build, requiring a rebuild to change configuration.
The built-in proxy allows the application to make cross-origin API requests securely. Configure proxy targets in public/config.json:
{
"proxyPrefixes": [
"https://myregistry.example.com",
"https://api.example.com"
]
}The application will route requests through /proxy?target=<url> for these prefixes.
npm run build
# or
ng buildOutput: dist/xregistry-viewer/ (non-optimized, with source maps)
npm run build-prod
# or
ng build --configuration productionOutput: dist/xregistry-viewer/ (optimized, minified, tree-shaken)
Production build features:
- Ahead-of-Time (AOT) Compilation: Pre-compiles templates for faster rendering
- Tree Shaking: Removes unused code
- Minification: Reduces file sizes
- Code Splitting: Lazy-loaded routes for optimal performance
- Server-Side Rendering (SSR) Support: Pre-renders pages for SEO and performance
- Compression: Gzip/Brotli ready
Build artifacts location:
dist/xregistry-viewer/
βββ browser/ # Client-side application
β βββ index.html
β βββ main.*.js
β βββ polyfills.*.js
β βββ styles.*.css
βββ server/ # SSR server (if enabled)
Pre-built Docker images are automatically published to GitHub Container Registry (ghcr.io) on every push to main and for tagged releases. Images are signed with Sigstore cosign for supply chain security.
Pull the latest image:
docker pull ghcr.io/xregistry/viewer:latestThe application uses a unified architecture with a single Node.js + Express server that handles:
- Static file serving (Angular application)
- API proxy (for cross-origin requests)
- Health monitoring
- Configuration loading
- Gzip compression
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Container (Port 4000) β
β β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β Express.js Server (Node 22) β β
β β β β
β β β’ Static Files (/dist/xregistry-viewer/) β β
β β β’ API Proxy (/proxy) β β
β β β’ Health Check (/health) β β
β β β’ Config (/config.json) β β
β β β’ Compression (gzip) β β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Alpine Linux (minimal base image) β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
This is the easiest way to run the application in Docker using the pre-built image.
Step 1: Create a docker-compose.yml
version: '3.8'
services:
xregistry-viewer:
image: ghcr.io/xregistry/viewer:latest
ports:
- "4000:4000"
volumes:
- ./public/config.json:/app/dist/xregistry-viewer/config.json
environment:
- NODE_ENV=production
restart: unless-stoppedStep 2: Start the service
docker-compose up -dThis will:
- Pull the latest pre-built Docker image from GitHub Container Registry
- Start the container in detached mode
- Expose port 4000 on your host machine
- Mount
public/config.jsonas a volume (for easy configuration updates)
Step 3: Verify it's running
# Check container status
docker-compose ps
# View logs
docker-compose logs -f
# Test health endpoint
curl http://localhost:4000/healthStep 4: Access the application
- Application: http://localhost:4000
- Health Check: http://localhost:4000/health
- Config Endpoint: http://localhost:4000/config.json
- Proxy Endpoint: http://localhost:4000/proxy?target=https://api.example.com
Step 5: Stop the service
docker-compose downPull and run the latest image directly:
# Pull the latest image
docker pull ghcr.io/xregistry/viewer:latest
# Run the container
docker run -d \
-p 4000:4000 \
--name xregistry-viewer \
-v $(pwd)/public/config.json:/app/dist/xregistry-viewer/config.json \
ghcr.io/xregistry/viewer:latestAvailable image tags:
latest- Latest build from main branchv1.2.3- Specific version (semantic versioning)<sha>- Specific commit SHA
Images are:
- β Automatically built on every push to main
- β Signed with Sigstore cosign (supply chain security)
- β Published to GitHub Container Registry (ghcr.io)
- β Multi-platform (linux/amd64, linux/arm64)
For local development or custom builds:
Using build scripts:
PowerShell (Windows):
.\build-docker.ps1Bash (Linux/Mac):
chmod +x build-docker.sh
./build-docker.shManual build:
# Build the image
docker build -t xregistry-viewer:latest .
# Run the container
docker run -d \
-p 4000:4000 \
--name xregistry-viewer \
-v $(pwd)/public/config.json:/app/dist/xregistry-viewer/config.json \
xregistry-viewer:latest
# Verify
docker ps
docker logs xregistry-viewerMulti-Stage Build:
The Dockerfile uses a multi-stage build for optimal image size and security:
- Builder Stage: Uses Node 22 to install dependencies and build the application
- Production Stage: Uses Node 22 Alpine (minimal) to run only the production server
Image Size: ~250MB (Alpine Linux + Node 22 + Application)
Base Image: node:22-alpine (minimal, secure, regularly updated)
Mount your config.json file to customize the application without rebuilding:
docker run -d \
-p 4000:4000 \
-v /path/to/your/config.json:/app/dist/xregistry-viewer/config.json \
xregistry-viewer:latestExample config.json:
{
"apiBaseUrl": "https://myregistry.example.com",
"registryEndpoint": "https://myregistry.example.com/api/v1",
"proxyPrefixes": [
"https://myregistry.example.com",
"https://api.example.com"
]
}Customize docker-compose.yml for your environment:
version: '3.8'
services:
xregistry-viewer:
build: .
ports:
- "4000:4000" # Change port mapping if needed
volumes:
- ./public/config.json:/app/dist/xregistry-viewer/config.json
environment:
- NODE_ENV=production
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:4000/health"]
interval: 30s
timeout: 10s
retries: 3For production deployments, see the comprehensive guide:
Covers:
- Security best practices
- SSL/TLS configuration
- Reverse proxy setup (nginx, Traefik)
- Container orchestration (Kubernetes, Docker Swarm)
- Monitoring and logging
- Backup and recovery
- Performance tuning
Run unit tests with Jest:
npm test
# or
ng testWatch mode (recommended for development):
npm run test:watchGenerate coverage report:
npm run test:coverageCoverage report will be generated in coverage/ directory. Open coverage/index.html in a browser to view detailed coverage.
Test Structure:
src/app/
βββ components/
β βββ my-component/
β βββ my-component.component.ts
β βββ my-component.component.spec.ts β Test file
βββ services/
βββ my-service.service.ts
βββ my-service.service.spec.ts β Test file
Writing Tests:
import { TestBed } from '@angular/core/testing';
import { MyService } from './my-service.service';
describe('MyService', () => {
let service: MyService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MyService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should return correct value', () => {
const result = service.getValue();
expect(result).toBe('expected value');
});
});Test the Docker image before deployment:
# Build and start the container
docker-compose up -d
# Run smoke tests against the container
curl http://localhost:4000/health
curl http://localhost:4000/
# Check logs for errors
docker-compose logs
# Stop the container
docker-compose downThe application follows Angular best practices with a component-based architecture:
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Angular Application β
β β
β βββββββββββββββ ββββββββββββββββ β
β β Components ββββ Services β β
β β β β β β
β β - Header β β - Registry β β
β β - Groups β β - Document β β
β β - Resources β β - Config β β
β β - Search β β β β
β βββββββββββββββ ββββββββββββββββ β
β β β β
β β β β
β ββββββββ΄βββββββββββββββββ΄ββββββ β
β β Routing (Routes) β β
β ββββββββββββββββββββββββββββββββ β
β β β
β β β
β ββββββββ΄βββββββββββββββββββββββββ β
β β Models & Interfaces β β
β βββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββ
β xRegistry API β
β (via proxy) β
ββββββββββββββββββββββββ
1. Service Layer Pattern
- Services handle business logic and API calls
- Components only handle presentation logic
- Dependency injection for loose coupling
2. Reactive Programming (RxJS)
- Observables for asynchronous operations
- Operators for data transformation
- Subscription management to prevent memory leaks
3. Component Composition
- Small, focused components
- Parent-child communication via
@Input()and@Output() - Smart components (containers) vs. Dumb components (presentational)
4. Lazy Loading
- Routes loaded on-demand
- Reduces initial bundle size
- Improves performance
User Action β Component β Service β API (via Proxy) β Response
β β
Template Update β Observable β RxJS Transform ββ
- Component state for UI-specific state
- Service state for shared state
- SessionStorage for navigation state (sticky reloads)
- LocalStorage for user preferences (API configuration)
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes (follow coding standards)
- Add tests for new functionality
- Run tests and linting (
npm test,npm run lint) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to your fork (
git push origin feature/amazing-feature) - Open a Pull Request
Follow Conventional Commits:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Examples:
feat(search): add fuzzy search functionality
fix(api): resolve CORS issue with proxy
docs(readme): update Docker deployment instructions
test(services): add unit tests for registry service- Follow Angular Style Guide
- Use TypeScript strict mode
- Write meaningful variable and function names
- Add JSDoc comments for public APIs
- Keep functions small and focused (< 50 lines)
- Use async/await instead of callbacks
- Title: Clear and descriptive
- Description: What, why, and how
- Tests: Include tests for new features
- Documentation: Update docs if needed
- Screenshots: For UI changes
- Breaking Changes: Clearly marked
This project is licensed under the MIT License - see the LICENSE file for details.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: docs/
- Angular team for the amazing framework
- xRegistry specification authors
- All contributors to this project
Built with β€οΈ using Angular 19