Hello World—From
Spaceport
Spaceport is a comprehensive full-stack web application framework engineered to make building applications fast, dynamic, and scalable. Designed for rapid iteration, it features an opinionated yet flexible technology stack built upon mature and powerful components.
Spaceport uniquely enhances server-client interaction with built-in capabilities like server actions, server reactivity, and server elements—directly embeddable within your HTML. These features seamlessly integrate with robust backend systems developed using powerful Groovy modules offering a cohesive front to back development experience.
Have a listen to The Spaceport Precast—the case for Spaceport for a high-level explanation of what the Spaceport stack looks like.
A durable stack
Spaceport is built on a robust foundation of open-source components— Groovy, Jetty, and CouchDB,
ensuring long-term support and transparency for your operations, and technology that won't be phased out in 6 months.
Groovy
Dynamic, powerful, and seamless with Java—Groovy is the backbone of Spaceport's server-side logic, enabling rapid development and iteration.
Learn more aboutJetty
A high-performance web server and servlet container, Jetty powers Spaceport's HTTP and WebSocket communications, ensuring robust and scalable web applications.
CouchDB
A flexible NoSQL database with built-in replication, CouchDB provides reliable data storage and backup for Spaceport applications.
Learn more aboutJump right in
Spaceport is designed for developers who want to get up and going quickly. If you prefer to hit the ground running, check out these starter kits.
Port Echo
A minimal starter kit to get your Spaceport application up and running quickly.
Get Port EchoPort Mercury
A more feature-complete starter kit with user authentication, database integration, and example modules.
Get Port MercurySpaceport also has comprehensive documentation available with a multitude of resources for developers that are just getting started.
A full-stack of features
Spaceport gives you the tools and workflow to rapidly develop, with hot reloadable Groovy source modules, a dynamic templating system, unified state management, web-focused class enhancements, and other boiler-plate busting features.
Quick to Start, Easy to Scale
Spaceport requires a very minimal scaffold, with as little as a single source directory and file.
# Start Spaceport with the default scaffold
java -jar spaceport.jar --start --no-manifest
📁 [SPACEPORT_ROOT]
📄 spaceport.jar
📁 modules/
📄 App.groovy
Configuration sits in one, unified manifest file, able to grow with your application.
# Start Spaceport with the a custom manifest file
java -jar spaceport.jar --start config.spaceport
# config.spaceport
host:
address: '127.0.0.1'
port: 10002
static assets:
paths:
/assets/* : 'assets/'
/export/ : '../export/'
Event-Driven Architecture
The Alert-driven Event System connects backend logic, defining endpoints, business logic, and data flows in one cohesive codebase.
// Application Lifecycle Events
@Alert('on initialized')
static _init(Result r) {
println 'Server started.'
}
// HTTP Endpoint Events
@Alert('on /api/status hit')
static _status(HttpResult r) {
r.writeToClient([
'status': 'ok',
'time': System.currentTimeMillis()
])
}
// Database Document Events
@Alert('on document saved')
static _audit(Result r) {
def doc = r.context.document
println "Document ${ doc._id } saved at ${ new Date() }"
}
Define advanced routing and custom pipelines with advanced, yet intuitive features such as regular expression matching and alert priorities.
@Alert(value = '~on /u/(.+)/messages/(\d+) hit', priority = 100)
static _userMessage(HttpResult r) {
def userID = r.matches[0]
def messageID = r.matches[1]
// Grab a message from the database, and check ownership
def message = MessageDocument.grab(messageID)
if (message.owner != userID) {
r.setStatus(403)
r.writeToClient([ 'error': 'Access denied' ])
r.cancelled = true // Stop further processing
} else {
r.context.message = message
}
}
@Alert('on /u/(.+)/messages/(\d+) hit')
static _serveUserMessage(HttpResult r) {
// Will only fire if the user is the owner (from previous alert)
r.writeToClient(r.context.message.text)
}
Server-Side Templating
Launchpad templates give you HTML-first rendering with embedded Groovy code for dynamic content generation with all of the backend power of Spaceport at your fingertips.
<div class="stats">
<div>Active Users: ${ dock.stats.users.get() }</div>
<div>Messages: ${ dock.stats.messages.get() }</div>
</div>
<%if (dock.stats.users.get() > 1000) {%>
<div class="important">High Traffic Alert</div>
<%}%>
Unified Experience
Enjoy a unified development experience where server and client code work seamlessly together. The system is built to blend the simplicity of a Multi-Page Application (MPA) with the fluid, app-like experience of a Single-Page Application (SPA).
// Clicks on the server, updates in the browser
<button on-click=${ _{ counter.inc() }}>
Likes: ${{ counter.get() }}
</button>
Persistent Data
Documents provide an ORM-like interface, backed by CouchDB. Spaceport gives you generic Document types, or you can extend them with your own custom classes, including schemas, methods, and more.
class ArticleDocument extends Document {
// Use Document's built-in get and save methods
static createDraft(String title) {
getNew('article_database').tap {
it.title = title
meta = new Meta()
save()
}
}
// Define nested classes for structured data schemas
class Meta {
String collection = 'articles'
String slug = title.slugify()
}
// Define the properties to be stored in the database
def customProperties = ['title', 'published', 'meta']
String title
Meta meta
boolean published = false // Provide defaults
// Provide custom methods for your business logic
void publish() {
this.published = true
this.save()
}
}
State Management
Cargo is a universal data container that works seamlessly across templates, modules, and database storage. Use it for counters, toggles, sets, and complex nested data—or mirror it from a Document for automatic database persistence.
// Use Cargo as a local data store
def counter = new Cargo()
counter.inc(5)
// Or, persist it in a static memory store
def cart = Cargo.fromStore('user-cart')
cart.items.addToSet(productId)
cart.total.inc(price)
cart.toggle('expressShipping')
// For maximum durability, use it with a Document
def doc = Document.get(userId, 'carts')
def persistentCart = Cargo.fromDocument(doc)
persistentCart.address = [ street: '123 Main St', city: 'Metropolis' ]
// Changes auto-save to the database
Sign up for the newsletter
Stay updated with the latest Spaceport news, releases, and tutorials by subscribing to our newsletter.