@owlmeans/route
v0.1.1
Published
Cross-environment routing library for OwlMeans applications that provides structures for URLs, URIs, aliases, permissions, and validations.
Downloads
445
Readme
@owlmeans/route
Cross-environment routing library for OwlMeans applications that provides structures for URLs, URIs, aliases, permissions, and validations.
Overview
The @owlmeans/route module is a core component of the OwlMeans Common library suite that provides routing functionality as part of the OwlMeans Common Module approach (described in @owlmeans/context). This module is not self-sufficient and is designed to work within the OwlMeans Common Module ecosystem.
Understanding "Modules" in OwlMeans Common Libraries
The term "module" in the context of OwlMeans Common libraries refers not to a programmatic module but to a URL unit in the system. The OwlMeans Common libraries are intended for fullstack microservices and microclients development, where modules serve as a central registry for URL structures.
Modules provide several key capabilities:
- URL Declaration and Nesting - Define hierarchical URL structures that can be shared across services
- Context-Aware Transformation - Transform module definitions into routes based on frontend or backend context
- Handler Attachment - On the backend, attach handlers to routes; on the frontend, specify which components to render
- URL Generation - Generate final URLs for navigation or API calls on both backend and frontend
- Centralized Registration - Provide a single place where all possible routes are registered, allowing micro-applications and micro-services to seamlessly address different parts of the system
This approach enables consistent URL management across distributed systems while maintaining flexibility for different deployment contexts.
Key Features
- Routes - Cross-environment structures consisting of URLs, URIs, aliases, permissions, and validations (POJO)
- Route Models - Wrapper objects that add behavior to route POJOs with resolution capabilities
- Service Routes - Routes that define service endpoints and configurations
- Resolution Logic - Context-aware route resolution with parent-child relationships
- Helper Functions - Utilities for creating, manipulating, and working with routes
Core Concepts
Route
A route is a cross-environment structure that defines how to access a particular endpoint or resource. It includes:
- Alias - Unique identifier for the route
- Path - URL path pattern
- Parent - Optional parent route for hierarchical structures
- Method - HTTP method (GET, POST, PUT, PATCH)
- Protocol - Communication protocol (HTTP, WebSocket)
- Security - Whether the route requires secure connections
Route Model
A route model wraps a route POJO with additional behavior, particularly the ability to resolve the route within a given context.
Service Route
A service route is an object that describes the base URL of a service (host, port, and other connection details). It differs from a regular route object, which describes a specific URL within a service.
Installation
npm install @owlmeans/routeAPI Reference
Types
CommonRoute
Main route interface that extends BasicRoute with additional properties.
interface CommonRoute extends BasicRoute {
alias: string // Unique identifier for the route
path: string // URL path pattern
parent?: string // Optional parent route alias
default?: boolean // Whether this is a default route
method?: RouteMethod // HTTP method
protocol?: RouteProtocols // Communication protocol
secure?: boolean // Whether secure connection is required
}CommonServiceRoute
Service route interface that describes the base URL of a service.
interface CommonServiceRoute extends BasicRoute {
home?: string // Home path for the service
service: string // Service identifier
default?: boolean // Whether this is a default service
}Note: A service route describes the base URL of a service (such as host and port), while a regular route describes a specific URL within that service.
BasicRoute
Base route interface with common properties.
interface BasicRoute {
type: AppType // Application type (Backend/Frontend)
service?: string // Service identifier
host?: string // Host address
port?: number // Port number
base?: string // Base path
resolved: boolean // Whether route is resolved
}CommonRouteModel
Route model that wraps a route with resolution capabilities.
interface CommonRouteModel {
route: CommonRoute // The wrapped route
resolve: <C extends BasicConfig, T extends BasicContext<C>>(context: T) => Promise<CommonRoute>
}RouteOptions
Partial CommonRoute options for route creation.
interface RouteOptions extends Partial<CommonRoute> {
}Constants
RouteMethod
Enumeration of supported HTTP methods.
enum RouteMethod {
GET = 'get',
POST = 'post',
PATCH = 'patch',
PUT = 'put'
}RouteProtocols
Enumeration of supported communication protocols.
enum RouteProtocols {
WEB = 'http',
SOCKET = 'ws'
}SEP
Path separator constant.
const SEP = '/'PARAM
Parameter prefix constant.
const PARAM = ':'Core Functions
route(alias, path, opts?)
Creates a route model from the given parameters.
const route: CreateRouteSignature<CommonRouteModel> = (alias, path, opts?) => CommonRouteModelParameters:
alias: string- Unique identifier for the routepath: string- URL path patternopts?: RouteOptions | string- Route options or parent route alias
Returns: CommonRouteModel - Route model with resolve capability
Example:
const userRoute = route('user', '/api/users/:id', {
method: RouteMethod.GET,
protocol: RouteProtocols.WEB
})createRoute(alias, path, opts?)
Creates a route POJO without wrapping it in a model.
const createRoute: CreateRouteSignature<CommonRoute> = (alias, path, opts?) => CommonRouteParameters:
alias: string- Unique identifier for the routepath: string- URL path patternopts?: RouteOptions | string- Route options or parent route alias
Returns: CommonRoute - Route POJO
Example:
const apiRoute = createRoute('api', '/api', {
type: AppType.Backend,
secure: true
})makeRouteModel(route)
Creates a route model from a route POJO.
const makeRouteModel = (route: CommonRoute): CommonRouteModelParameters:
route: CommonRoute- Route POJO to wrap
Returns: CommonRouteModel - Route model with resolve capability
Helper Functions
normalizePath(path)
Normalizes a URL path by removing leading/trailing slashes and trimming whitespace.
const normalizePath = (path: string): stringParameters:
path: string- Path to normalize
Returns: string - Normalized path
Example:
normalizePath('/api/users/') // Returns 'api/users'
normalizePath(' /api/ ') // Returns 'api'rtype(type, opts?)
Creates route options with a specific application type.
const rtype = (type: AppType, opts?: RouteOptions | string): Partial<RouteOptions>Parameters:
type: AppType- Application type (Backend/Frontend)opts?: RouteOptions | string- Additional options or parent route alias
Returns: Partial<RouteOptions> - Route options with type set
backend(opts?, method?)
Creates route options configured for backend routes.
const backend = (opts?: RouteOptions | string | null, method?: RouteOptions | RouteMethod): Partial<RouteOptions>Parameters:
opts?: RouteOptions | string | null- Route options or parent route aliasmethod?: RouteOptions | RouteMethod- HTTP method or additional options
Returns: Partial<RouteOptions> - Backend route options
Example:
const backendRoute = route('api', '/api', backend(null, RouteMethod.GET))frontend(opts?, def?)
Creates route options configured for frontend routes.
const frontend = (opts?: RouteOptions | string | null, def?: RouteOptions | boolean): Partial<RouteOptions>Parameters:
opts?: RouteOptions | string | null- Route options or parent route aliasdef?: RouteOptions | boolean- Default flag or additional options
Returns: Partial<RouteOptions> - Frontend route options
Example:
const frontendRoute = route('home', '/', frontend(null, true))service(service, opts?)
Creates route options with a specific service identifier.
const service = (service: string, opts?: Partial<RouteOptions>): Partial<RouteOptions>Parameters:
service: string- Service identifieropts?: Partial<RouteOptions>- Additional route options
Returns: Partial<RouteOptions> - Route options with service set
socket(opts?, secondary?)
Creates route options configured for WebSocket routes.
const socket = (opts?: RouteOptions | string | null, secondary?: RouteOptions): Partial<RouteOptions>Parameters:
opts?: RouteOptions | string | null- Route options or parent route aliassecondary?: RouteOptions- Additional options
Returns: Partial<RouteOptions> - WebSocket route options
Example:
const socketRoute = route('ws', '/socket', socket())Utility Functions
resolve(route)
Creates a route resolution function that resolves the route within a given context.
const resolve = <C extends Config, T extends BasicContext<C>>(route: CommonRoute) => (context: T) => Promise<CommonRoute>Parameters:
route: CommonRoute- Route to resolve
Returns: (context: T) => Promise<CommonRoute> - Resolution function
Features:
- Resolves parent routes recursively
- Handles service route resolution
- Manages path concatenation
- Applies security settings
- Detects circular dependencies
getParentRoute(context, route)
Retrieves and resolves the parent route for a given route.
const getParentRoute = async <C extends Config, T extends BasicContext<C>>(context: T, route: CommonRoute): Promise<CommonRoute | null>Parameters:
context: T- Application contextroute: CommonRoute- Route to find parent for
Returns: Promise<CommonRoute | null> - Parent route or null if no parent
overrideParams(route, overrides, filter?)
Overrides route parameters with provided values.
const overrideParams = (route: CommonRoute, overrides?: Partial<CommonRoute>, filter?: string[]) => voidParameters:
route: CommonRoute- Route to modifyoverrides?: Partial<CommonRoute>- Parameters to overridefilter?: string[]- Optional filter for which parameters to override
prependBase(route)
Prepends the base path to a route's path.
const prependBase = (route: CommonRoute) => stringParameters:
route: CommonRoute- Route to process
Returns: string - Path with base prepended
isServiceRoute(obj)
Type guard to check if an object is a service route.
const isServiceRoute = (obj?: Object): obj is CommonServiceRouteParameters:
obj?: Object- Object to check
Returns: boolean - True if object is a service route
isServiceRouteResolved(route)
Type guard to check if a service route is resolved.
const isServiceRouteResolved = (route: CommonServiceRoute): route is ResolvedServiceRouteParameters:
route: CommonServiceRoute- Service route to check
Returns: boolean - True if service route is resolved
Usage Examples
Basic Route Creation
import { route, RouteMethod, RouteProtocols } from '@owlmeans/route'
// Create a simple API route
const userRoute = route('user', '/api/users/:id', {
method: RouteMethod.GET,
protocol: RouteProtocols.WEB
})
// Create a nested route with parent
const userProfileRoute = route('userProfile', '/profile', 'user')Backend Route Configuration
import { route, backend, RouteMethod } from '@owlmeans/route'
// Create a backend API route
const apiRoute = route('api', '/api', backend())
// Create a POST endpoint
const createUserRoute = route('createUser', '/users', backend('api', RouteMethod.POST))Frontend Route Configuration
import { route, frontend } from '@owlmeans/route'
// Create a default frontend route
const homeRoute = route('home', '/', frontend(null, true))
// Create a nested frontend route
const aboutRoute = route('about', '/about', frontend())WebSocket Route Configuration
import { route, socket } from '@owlmeans/route'
// Create a WebSocket route
const chatRoute = route('chat', '/chat', socket())Route Resolution
import { route, backend } from '@owlmeans/route'
// Create a route that needs resolution
const apiRoute = route('api', '/api', backend())
// Resolve the route with context
const resolvedRoute = await apiRoute.resolve(context)
console.log(resolvedRoute.host) // Resolved host
console.log(resolvedRoute.port) // Resolved portService Route Usage
import { route, service, backend } from '@owlmeans/route'
// Create a route with service configuration
const userServiceRoute = route('userService', '/users', {
...backend(),
...service('user-service')
})Client-Side Usage
For client-side applications, consider using the @owlmeans/client-route package which extends this module with additional client-specific functionality:
import { route as baseRoute } from '@owlmeans/route'
import { route as clientRoute } from '@owlmeans/client-route'
// Create a base route
const userRoute = baseRoute('user', '/users/:id')
// Wrap with client functionality
const clientUserRoute = clientRoute(userRoute)
// Access client-specific features
console.log(clientUserRoute.route.partialPath) // '/users/:id'The @owlmeans/client-route package provides:
- Partial Path Preservation - Maintains original path patterns for client-side processing
- Parameter Extraction - Utilities for extracting route parameters
- Promise-based Resolution - Enhanced resolution tracking for client applications
See the @owlmeans/client-route documentation for complete client-side routing capabilities.
Integration with OwlMeans Context
The route module is part of the OwlMeans Common Module approach and integrates seamlessly with the OwlMeans context system (see @owlmeans/context for more details on the Common Module approach). This module is not self-sufficient and requires the context system to function properly.
Working with Modules and Routes
In the OwlMeans ecosystem, modules define URL structures that can be transformed into routes based on the deployment context. Here's how they work together:
import { route, backend } from '@owlmeans/route'
import { BasicContext } from '@owlmeans/context'
// Define a module with route structure
const apiModule = {
_module: true,
route: route('api', '/api', backend()),
// Nested routes within the module
users: route('users', '/users', 'api'),
posts: route('posts', '/posts', 'api'),
// Resolution logic
resolve: async (context) => {
await apiModule.route.resolve(context)
await apiModule.users.resolve(context)
await apiModule.posts.resolve(context)
}
}
// Use the module in different contexts
const resolvedRoute = await apiModule.route.resolve(context)
// Generate URLs for navigation or API calls
const userApiUrl = await apiModule.users.resolve(backendContext) // Backend API URL
const userPageUrl = await apiModule.users.resolve(frontendContext) // Frontend page URLModule-Based Architecture Benefits
- Centralized Route Definition - All routes are defined in modules and shared across services
- Context-Aware Resolution - Same route definitions work for both frontend and backend
- Consistent URL Generation - URLs are generated consistently across the entire system
- Service Integration - Modules can span multiple microservices while maintaining coherent routing
Error Handling
The module provides comprehensive error handling:
- SyntaxError - Thrown for configuration errors, missing services, or circular dependencies
- Type Guards - Used to ensure type safety when working with service routes
- Validation - Route parameters are validated during resolution
Best Practices
- Use Aliases - Always provide meaningful aliases for routes
- Hierarchical Structure - Organize routes in a parent-child hierarchy when appropriate
- Service Configuration - Configure services properly in the context for route resolution
- Security - Set appropriate security flags for routes
- Type Safety - Use TypeScript interfaces and type guards for better development experience
Related Modules
- @owlmeans/context - Provides the context system for route resolution
- @owlmeans/client-route - Client-side route implementations and extensions with additional functionality like partial path preservation and parameter extraction
- @owlmeans/server-route - Server-side route implementations
