Alerts: Spaceport's Event System
Alerts are Spaceport's powerful event-driven hook system that allows your application code to respond to events
throughout the request lifecycle, database operations, and custom application events. By annotating static methods
with @Alert, you can hook into dozens of built-in events or create your own custom event flows.
Think of Alerts as Spaceport's central nervous system—they connect your application logic to every significant event that occurs, from HTTP requests hitting your server to documents being saved in the database. This declarative approach keeps your code organized and makes it easy to see exactly what happens when events fire.
# Core Concepts
The Alerts system is built on a few key concepts that work together to provide a flexible, performant event system.
## Alert Annotations
The @Alert annotation marks a static method as an event handler. When an event with a matching name is invoked
anywhere in Spaceport, your method will be called automatically.
import spaceport.computer.alerts.Alert
import spaceport.computer.alerts.results.HttpResult
class MyRouter {
@Alert('on /api/users hit')
static _handleUsers(HttpResult r) {
// Writing a Groovy Map automatically serializes to JSON
r.writeToClient([ 'users': [ 'alice', 'bob' ]])
}
}
Key Requirements:
- The method must be static
- The method must accept exactly one parameter (the Result context object)
- The annotation value is the event string to listen for
## Event Strings
Event strings identify which event an alert should respond to. Spaceport uses a consistent naming convention for built-in events, but you can invoke custom events with any string you choose.
Some Built-in Event Patterns:
on initialize- Application startup (source modules loaded)on deinitialize- Before hot reload (debug mode only)on [route] hit- Any HTTP request to a routeon [route] [METHOD]- HTTP request with specific method (POST, PUT, DELETE, etc.)on page hit- Any HTTP page request (fires for every request)on document created- A new document is createdon document saved- A document is saved to the databaseon socket connect- WebSocket connection establishedon socket data- WebSocket message received
See Built-in Events Reference for the complete list.
## The Result Object
Every alert handler receives a Result object (or a specialized subclass) that contains:
- Context data about the event (request details, document info, etc.)
- Control flags like
cancelledto stop further processing - Match data from regex-based event strings (if applicable)
The Result object is both an input (providing context) and an output (allowing you to signal and mutate outcomes).
## Alert Priority
When multiple alerts listen to the same event, they execute in priority order (highest first). This allows you to control the sequence of execution for middleware-style patterns.
@Alert(value = 'on page hit', priority = 100)
static _securityCheck(HttpResult r) {
// Runs first - high priority
if (!r.client.isAuthenticated()) {
r.setRedirectUrl('/login')
r.cancelled = true // Stop other alerts
}
}
@Alert('on page hit') // Default priority = 0
static _logRequest(HttpResult r) {
// Runs after security check, if not cancelled
println "Request to ${ r.context.target }."
}
# Basic Usage
Let's start with some common patterns for using alerts in your application. While alerts can be used for a wide variety of purposes, these examples cover some typical use cases that you'll certainly encounter.
## Application Lifecycle
Hook into startup and shutdown events to initialize resources, transform assets, or perform cleanup.
import spaceport.Spaceport
import spaceport.computer.alerts.Alert
import spaceport.computer.alerts.results.Result
class AppLifecycle {
static boolean running
@Alert('on initialize')
static _startup(Result r) {
println "Application starting up..."
// Spaceport's API to ensure required databases exist
if (!Spaceport.main_memory_core.containsDatabase('users')) {
Spaceport.main_memory_core.createDatabase('users')
}
// Modify global state
running = true
// Initialize your own application systems
// (could be connection pools, caches, scheduled tasks, etc.)
SomeExternalSystem.initialize()
}
@Alert('on deinitialized')
static _shutdown(Result r) {
println "Application shutting down for hot reload..."
// 'on deinitialize' is called before hot reload in debug mode,
// 'on deinitialized' is called after cleanup.
running = false
// You could imagine the need to
SomeExternalSystem.shutdown()
}
}
## Handling HTTP Routes
The most common use of alerts is routing HTTP requests. Use the on [route] hit pattern for any request, or
on [route] [METHOD] for specific HTTP methods. Note the case sensitivity of the method name—always use uppercase
here (GET, POST, DELETE, etc).
import spaceport.computer.alerts.Alert
import spaceport.computer.alerts.results.HttpResult
class ProductRouter {
@Alert('on /system/ping hit')
static _ping(HttpResult r) {
r.writeToClient('pong!')
}
// Handle GET /products
@Alert('on /products GET')
static _listProducts(HttpResult r) {
// Your application logic to fetch products
// (could be from Documents, a View, or any other data source)
def products = fetchAllProducts()
// Spaceport's HttpResult offers a way to write the response to the client
r.writeToClient(products)
}
// Handle POST /products
@Alert('on /products POST')
static _createProduct(HttpResult r) {
// Extract data from the request using Spaceport's context
// Since this is a POST, it's likely form data or JSON payload
// If it was a GET, it could be query parameters
def name = r.context.data.name
def price = r.context.data.getNumber('price') // Complete with data helpers
// Your application logic to create the product
// (could save to a Document, call an external API, etc.)
def product = createProduct(name, price)
// Use Spaceport's HttpResult to set a specific status and write a JSON response
r.setStatus(201)
r.writeToClient(['id': product._id])
}
// Handle DELETE /products/123
@Alert('~on /products/(.*) DELETE')
static _deleteProduct(HttpResult r) {
def productId = r.matches[0] // Captured from regex by Spaceport
// (Your application logic to delete the product)
if (deleteProduct(productId))
r.setStatus(204) // No content
else
r.setStatus(404) // Not found
}
}
## Global Request Middleware
Use on page hit to run code for every HTTP request, perfect for logging, authentication, or adding common headers. Use
the context available to determine the request details with a finer grain.
class Middleware {
@Alert(value = 'on page hit', priority = 50)
static _cors(HttpResult r) {
// Use addResponseHeader to append headers, or setResponseHeader to overwrite
r.setResponseHeader('Access-Control-Allow-Origin', '*')
r.setResponseHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE')
}
@Alert('on page hit')
static _logging(HttpResult r) {
def start = System.currentTimeMillis()
def method = r.context.method // GET, POST, etc.
// This could also use Spaceport's built-in logging (Command.log), or your own system
println "${ start.time() }: Processed ${ method } request to ${ r.context.target } for ${ r.client.userID ?: 'anonymous' }"
}
}
## Document Lifecycle Hooks
React to database events to implement audit logs, cache invalidation, or derived data updates.
import spaceport.computer.alerts.Alert
import spaceport.computer.alerts.results.Result
import spaceport.computer.memory.physical.Document
class AuditLog {
@Alert('on document created')
static _logCreation(Result r) {
// Spaceport provides the document and context
def doc = r.context.doc
// Your logging implementation (could write to a log, send to a service, etc.)
println "Document created: ${doc._id} in ${r.context.database}"
}
@Alert('on document saved')
static _invalidateCache(Result r) {
def doc = r.context.doc
// Your caching system (could be a Map in the Spaceport Store,
// or something external like Redis, Memcached, Cloudflare, etc.)
YourCacheSystem.remove(doc._id)
}
}
# Regex-Based Event Matching
Alert strings that start with ~ are treated as regular expressions, allowing you to capture dynamic route parameters
or create flexible event patterns.
## Capturing Route Parameters
The most common use of regex alerts is capturing URL path segments.
class ArticleRouter {
// Match: /articles/123, /articles/my-post-slug, etc.
@Alert('~on /articles/(.*) hit')
static _viewArticle(HttpResult r) {
// Spaceport captures the route parameter via regex
def articleId = r.matches[0]
// Your application logic to fetch the article
// (this could be a custom Document class method, a database query, etc.)
def article = Article.fetchById(articleId)
if (!article) {
// Use Spaceport's HttpResult to send a 404
r.setStatus(404)
r.writeToClient("Article not found")
return
}
// Render the article using Launchpad now that the
// middleware and data fetching is done
new Launchpad().assemble(['article.ghtml']).launch(r)
}
// Match: /users/alice/posts/123
@Alert('~on /users/(.)/posts/(.) hit')
static _viewUserPost(HttpResult r) {
// Spaceport captures multiple groups
def username = r.matches[0]
def postId = r.matches[1]
// Your application logic here
// ...
}
}
Important Notes:
- Captured groups are available in
r.matchesas a list of strings - The regex is anchored (must match the entire event string)
- Spaces in the pattern are automatically treated as
\s(whitespace)
## Pattern Matching for Custom Events
You can also use regex for your own custom event hierarchies.
class NotificationHandler {
// Match any notification type
@Alert('~on notification\\.(.*)')
static _handleNotification(Result r) {
def notificationType = r.matches[0] // 'email', 'sms', 'push', etc.
println "Handling ${notificationType} notification"
// ... dispatch to specific handler
}
}
// Elsewhere in your code:
Alerts.invoke('on notification.email', [ message: 'Hello!' ])
Alerts.invoke('on notification.sms', [ message: 'Alert!' ])
# Result Types and Context Objects
Spaceport provides a few specialized Result classes for different event types. These classes extend the base Result and
add helper methods specific to their context.
## HttpResult (HTTP Requests)
Used for all HTTP-related alerts. Provides methods to manipulate the response.
@Alert('on /download hit')
static _downloadFile(HttpResult r) {
// Access request data from Spaceport's context
def filename = r.context.data.file
def userAgent = r.context.headers.'User-Agent'
// Use Spaceport's HttpResult API to set response properties
r.setContentType('application/pdf; charset=UTF-8')
r.setResponseHeader('Cache-Control', 'max-age=3600')
// Add cookies using Spaceport's cookie methods
r.addSecureResponseCookie('last-download', filename)
// Your application logic to locate the file
def file = new File("/downloads/${filename}")
// Use Spaceport's convenience methods to send the file
r.markAsAttachment(filename)
r.writeToClient(file) // Automatically handles file streaming
}
Available Context Properties:
| Property | Type | Description |
|---|---|---|
request |
HttpServletRequest |
Raw servlet request object |
response |
HttpServletResponse |
Raw servlet response object |
method |
String |
HTTP method (GET, POST, etc.) |
target |
String |
Request path (e.g., /api/users) |
time |
Long |
Request timestamp (epoch milliseconds) |
cookies |
Map |
Request cookies as a map |
headers |
Map |
Request headers as a map |
data |
Cargo |
Query parameters (GET) or form data (POST) |
dock |
Cargo |
Session-specific storage for this client |
client |
Client |
Authenticated client object (if logged in) |
Common Helper Methods:
| Method | Description |
|---|---|
setStatus(Integer) |
Set HTTP status code (200, 404, etc.) |
setContentType(String) |
Set Content-Type header |
addResponseHeader(name, value) |
Add a response header |
setResponseHeader(name, value) |
Set/replace a response header |
addResponseCookie(name, value) |
Add a cookie (7-day expiry, root path) |
addSecureResponseCookie(name, value) |
Add a secure, httpOnly cookie |
setRedirectUrl(String) |
Redirect to a URL (302) |
writeToClient(String) |
Write string response |
writeToClient(Map) |
Write JSON response (auto-sets content type) |
writeToClient(File) |
Write file response (auto-detects content type) |
markAsAttachment(String) |
Set Content-Disposition for download |
See the HttpResult API Reference for the complete list.
## SocketResult (WebSocket Events)
Used for WebSocket-related alerts. Provides methods to send data back through the socket.
@Alert('on socket data')
static _handleMessage(SocketResult r) {
// Spaceport provides the parsed message data
def message = r.context.data.message
def sessionId = r.context.session.remote.inetSocketAddress
// Get the authenticated client (if any) via Spaceport
def client = r.client
// Your application logic to process the message
def response = YourMessageProcessor.handle(message, client)
// Use Spaceport's SocketResult to send response through the WebSocket
r.writeToRemote(['response': 'Message received', 'echo': message])
// Or close the connection using Spaceport's API
if (message == 'goodbye') {
r.close('Client requested disconnect')
}
}
Available Context Properties:
| Property | Type | Description |
|---|---|---|
request |
UpgradeRequest |
Original HTTP upgrade request |
session |
Session |
WebSocket session object |
handler |
SocketHandler |
Handler instance managing this socket |
handlerId |
String |
ID of the handler (for routing) |
time |
Long |
Event timestamp |
headers |
Map |
Request headers from upgrade |
cookies |
Map |
Cookies from upgrade request |
data |
Map |
Parsed JSON data from the client |
dock |
Cargo |
Session-specific storage |
client |
Client |
Authenticated client object |
Helper Methods:
| Method | Description |
|---|---|
getHandler() |
Get the SocketHandler instance |
getClient() |
Get the Client associated with this socket |
writeToRemote(String) |
Send a string message |
writeToRemote(Map) |
Send a JSON message |
close() |
Close the socket connection |
close(String reason) |
Close with a reason message |
## Base Result (Custom Events)
For custom events or simpler built-in events, you'll receive the base Result class. This has fewer helper methods
but provides the core functionality.
@Alert('on user registered')
static _sendWelcomeEmail(Result r) {
def user = r.context.user
def email = r.context.email
// Send email logic...
// You can cancel further processing if needed
if (!emailSent) {
r.cancelled = true
}
}
Base Result Properties:
| Property | Type | Description |
|---|---|---|
context |
Object/Map |
Event-specific context data |
called |
boolean |
Set to true when this alert fires |
cancelled |
boolean |
Set to true to stop further alert processing |
matches |
List |
Captured groups from regex event strings |
# Advanced Patterns
## Creating Custom Events
You can invoke your own custom events anywhere in your application to create decoupled, event-driven architectures. This pattern allows different parts of your system to react to domain events without tight coupling.
The Flow: 1. Your core business logic performs an operation and invokes a custom event 2. Spaceport broadcasts this event to all registered alert handlers 3. Multiple independent handlers can react to the same event 4. Each handler implements its own specific concern (email, inventory, analytics, etc.)
This creates a flexible plugin-like architecture where you can add new behaviors without modifying existing code.
import spaceport.computer.Alerts
class OrderProcessor {
static void processOrder(Order order) {
// Your main business logic
order.status = 'processed'
order.save()
// Invoke a custom event - Spaceport notifies all listeners
Alerts.invoke('on order processed', [
order: order,
timestamp: System.currentTimeMillis()
])
}
}
// Multiple handlers can react independently to the same event
class EmailNotifier {
@Alert('on order processed')
static _sendConfirmation(Result r) {
def order = r.context.order
// Your email service integration
YourEmailService.send(order.customerEmail, "Order #${order.id} confirmed")
}
}
class InventoryManager {
@Alert('on order processed')
static _updateInventory(Result r) {
def order = r.context.order
// Your inventory management logic
order.items.each { item ->
YourInventorySystem.decrementStock(item.productId, item.quantity)
}
}
}
class AnalyticsTracker {
@Alert('on order processed')
static _trackSale(Result r) {
def order = r.context.order
// Your analytics service integration
YourAnalytics.trackEvent('sale', order.total)
}
}
## Using resultType for Custom Result Classes
For complex custom events, you can create your own Result subclass with domain-specific helper methods. This pattern lets you encapsulate common operations and make your alert handlers more readable.
The Flow:
- Create a custom Result class with helpful methods for your domain
- When invoking the event, specify your custom class as the
resultType - Spaceport instantiates your custom class and passes it to alert handlers
- Alert handlers can use your custom methods for cleaner, more expressive code
import spaceport.computer.alerts.results.Result
// Custom Result class with domain-specific helper methods
class OrderResult extends Result {
OrderResult(Object context) {
super(context)
}
// Helper to get the order with proper type
Order getOrder() {
return context.order as Order
}
// Domain-specific operation that multiple handlers might need
void markAsFailed(String reason) {
getOrder().status = 'failed'
getOrder().failureReason = reason
getOrder().save()
this.cancelled = true // Stop further alert processing
}
}
// Invoking with a custom result type
def context = [
order: myOrder,
customer: customer,
resultType: OrderResult // Tell Spaceport to use your custom class
]
def result = Alerts.invoke('on order submitted', context)
Then in your alert handlers, you get the custom type with its helper methods:
@Alert('on order submitted')
static _validateOrder(OrderResult r) { // Type is OrderResult, not Result
// Use your custom helper methods
if (r.order.total > r.customer.creditLimit) {
r.markAsFailed('Exceeds credit limit')
// No need to manually set cancelled, markAsFailed handles it
}
}
## Cancelling Alert Chains
Set cancelled = true on the Result to prevent subsequent alerts from executing. This is useful for authentication,
authorization, or validation logic.
class AuthMiddleware {
@Alert(value = 'on page hit', priority = 100)
static _requireAuth(HttpResult r) {
// Skip auth for public routes
if (r.context.target.startsWith('/public/')) return
if (!r.client.isAuthenticated()) {
r.setRedirectUrl('/public/login')
r.cancelled = true // Stop processing this request
}
}
}
class ProtectedRoutes {
// This will only run if auth check passes
@Alert('on /admin/dashboard hit')
static _showDashboard(HttpResult r) {
// Only authenticated users reach here
new Launchpad().assemble(['admin-dashboard.ghtml']).launch(r)
}
}
## Multi-Stage Processing Pipelines
Use priorities and shared context to build processing pipelines. This pattern demonstrates how multiple alerts can collaborate on the same request, each handling a specific stage of processing.
The Flow:
1. High-priority alert validates the input (priority 30)
2. If validation passes, mid-priority alert processes the data (priority 20)
3. Low-priority alert saves the result (priority 10)
4. Each stage can access results from previous stages via r.context
5. Any stage can set r.cancelled = true to abort the pipeline
This approach keeps each stage focused and testable, while maintaining a clear sequence of operations.
class ImageUploadPipeline {
@Alert(value = 'on /api/upload POST', priority = 30)
static _validateUpload(HttpResult r) {
def file = r.context.data.file
// Your validation logic
if (!YourImageValidator.isValidType(file)) {
r.setStatus(400)
r.writeToClient(['error': 'Invalid file type'])
r.cancelled = true // Stop the pipeline
return
}
// Store validation result for next stage to use
r.context.validated = true
}
@Alert(value = 'on /api/upload POST', priority = 20)
static _processImage(HttpResult r) {
// Only proceed if validation passed
if (!r.context.validated) return
def file = r.context.data.file
// Your image processing logic (resize, optimize, watermark, etc.)
def processed = YourImageProcessor.resizeAndOptimize(file)
// Store processed image for final stage
r.context.processedImage = processed
}
@Alert(value = 'on /api/upload POST', priority = 10)
static _saveImage(HttpResult r) {
// Only proceed if processing completed
if (!r.context.processedImage) return
// Your storage logic (S3, local filesystem, CDN, etc.)
def url = YourStorageSystem.save(r.context.processedImage)
// Send response to client using Spaceport's HttpResult
r.writeToClient(['url': url])
}
}
# Built-in Events Reference
## HTTP Events
| Event String | Result Type | Context | Description |
|---|---|---|---|
on page hit |
HttpResult |
HTTP request context | Fires for every HTTP request |
on / hit |
HttpResult |
HTTP request context | GET request to root path |
on [route] hit |
HttpResult |
HTTP request context | GET request to specific route |
on [route] [METHOD] |
HttpResult |
HTTP request context | Specific HTTP method to route |
Examples:
- on /api/users hit - GET /api/users
- on /api/users POST - POST /api/users
- ~on /articles/(.) hit - GET /articles/ with capture
- ~on /users/(.)/posts/(.) DELETE - DELETE with multiple captures
## WebSocket Events
| Event String | Result Type | Context | Description |
|---|---|---|---|
on socket connect |
SocketResult |
Socket lifecycle context | WebSocket connection opened |
on socket data |
SocketResult |
Socket message context | Data received from client |
on socket [handler-id] |
SocketResult |
Socket message context | Routed to specific handler |
on socket closed |
SocketResult |
Socket lifecycle context | WebSocket connection closed |
## Document Events
| Event String | Result Type | Context Properties | Description |
|---|---|---|---|
on document created |
Result |
id, database, doc, classType |
New document created |
on document save |
Result |
doc |
Before document save (can modify) |
on document saved |
Result |
doc |
After document saved successfully |
on document modified |
Result |
type, action, document, operation |
Document modified in database |
on document remove |
Result |
type, document |
Before document deleted |
on document removed |
Result |
type, document, operation |
After document deleted |
on document conflict |
Result |
Document conflict details | Save conflict detected |
## Application Lifecycle Events
| Event String | Result Type | Context | Description |
|---|---|---|---|
on initialize |
Result |
Empty map | Application startup complete |
on initialized |
Result |
Empty map | All initialization complete |
on deinitialize |
Result |
Empty map | Before hot reload (debug mode) |
on deinitialized |
Result |
Empty map | After cleanup before reload |
## Authentication Events
| Event String | Result Type | Context Properties | Description |
|---|---|---|---|
on client auth |
Result |
user_id, client |
Client authenticated successfully |
on client auth failed |
Result |
user_id, exists |
Authentication attempt failed |
# Performance Considerations
## Alert Registration Performance
- Alerts are registered once at startup (or during hot reload)
- Registration is sorted by priority, so invocation is fast
- Use WeakReferences internally to allow garbage collection
## Invocation Performance
- String matching is very fast (direct equality check)
- Regex matching has overhead—use sparingly for high-traffic events
- Consider priority carefully: high-priority alerts run first for every match
## Best Practices
1. Use specific event strings when possible instead of broad catches with on page hit
2. Minimize regex complexity for frequently-invoked events
3. Return early in alert handlers when conditions aren't met
4. Use priority to short-circuit unnecessary processing with r.cancelled = true
5. Avoid heavy computation in high-frequency alerts (like on page hit)
# Common Patterns and Recipes
## Basic Authentication Guard
class AuthGuard {
@Alert(value = 'on page hit', priority = 100)
static _checkAuth(HttpResult r) {
def publicPaths = ['/login', '/register', '/public', '/assets']
// Skip auth for public paths
if (publicPaths.any { r.context.target.startsWith(it) }) {
return
}
// Require authentication for everything else
if (!r.client.hasPermission('authenticated')) {
r.setRedirectUrl('/login')
r.cancelled = true
}
}
}
## Request Logging
class RequestLogger {
@Alert('on page hit')
static _logRequest(HttpResult r) {
def method = r.context.method
def path = r.context.target
def ip = r.context.request.remoteAddr
def user = r.client.userID
println "[${new Date()}] ${method} ${path} | ${user} | ${ip}"
}
}
## Automatic Cache Invalidation
This pattern shows how to automatically clear caches when documents change, keeping your cached data fresh. The Alert system makes this seamless—whenever a document is saved, your cache logic runs without any coupling to the save logic.
class CacheInvalidator {
@Alert('on document saved')
static _invalidate(Result r) {
// Spaceport provides the document that was saved
def doc = r.context.doc
// Your caching implementation (could be a Map, Redis, Memcached, etc.)
// Clear the specific document cache
YourCacheSystem.remove("doc:${doc._id}")
// Clear any aggregate caches that might include this document
if (doc.type == 'article') {
YourCacheSystem.remove('article:list')
YourCacheSystem.remove('article:recent')
}
}
}
Note: Spaceport includes a Document caching system that is covered in the Document Documentation, but often other external caches are used in real-world applications. Imagine integrating with Redis, Memcached, or even in-memory caches specific to your application logic. This pattern demonstrates how Alerts can help maintain cache coherency effortlessly.
## Error Page Handling
class ErrorHandler {
@Alert(value = 'on page hit', priority = -100) // Very low priority
static _handle404(HttpResult r) {
// Only handle if no other handler responded
if (!r.called) {
r.setStatus(404)
new Launchpad().assemble(['errors/404.ghtml']).launch(r)
}
}
}
## Document Audit Trail
class AuditTrail {
@Alert('on document modified')
static _logChange(Result r) {
def auditEntry = Document.getNew('audit-log')
auditEntry.type = 'audit'
auditEntry.fields.documentId = r.context.document._id
auditEntry.fields.documentType = r.context.type
auditEntry.fields.action = r.context.action
auditEntry.fields.timestamp = System.currentTimeMillis()
auditEntry.save()
}
}
# Troubleshooting
## Alert Not Firing
Symptoms: Your annotated method isn't being called.
Checklist:
- Is the method
static? - Does it have exactly one parameter?
- Is the class in a directory scanned by the Source Loader?
- Is the event string exactly correct? (check spelling and spaces)
- For HTTP routes, is another alert setting
r.cancelled = truefirst? - Check the console for compilation errors
## Wrong Result Type
Symptoms: ClassCastException or missing methods on result object.
Solution: Match your parameter type to the event type:
- HTTP events →
HttpResult - WebSocket events →
SocketResult - Document events →
Result - Custom events →
Result(or your own subclass)
## Regex Not Matching
Symptoms: Regex alert handler never fires.
Checklist:
- Did you prefix the event string with
~? - Remember spaces become
\sautomatically - Test your regex with concrete examples
- Check if
r.matchesis populated correctly
## Priority Confusion
Symptoms: Alerts firing in unexpected order.
Remember:
- Higher numbers = higher priority (run first)
- Default priority is
0 - Negative priorities run last
- Same priority = undefined order
# Summary
The Alerts system is the backbone of event-driven programming in Spaceport. By understanding how to:
- Use
@Alertannotations to hook into events - Work with different Result types and their context data
- Leverage priorities and cancellation for control flow
- Create custom events for your own domain logic
- Apply regex patterns for dynamic routing
...you can build sophisticated, maintainable applications that respond elegantly to every significant event in your system. While Spaceport has some built-in Alerts for common scenarios, the true power lies in your ability to define and react to the events that matter most to your application's unique needs.
# See Also
- Source Modules: Where to place your alert-annotated classes
- HTTP Result Reference: Complete API for HTTP responses
- Documents: Understanding document lifecycle events
- Routing Patterns: Deep dive into HTTP routing with alerts
- Launchpad: Rendering responses in your alert handlers
SPACEPORT DOCS