Getting Started
Installation and Setup
Prerequisites
Before diving into Hypermodern development, ensure you have:
- Dart SDK 3.0.0 or later: Download from dart.dev
- A code editor: VS Code with the Dart extension is recommended
- Basic Dart knowledge: Familiarity with Dart syntax and concepts
Installing the Hypermodern CLI
The Hypermodern CLI is your primary tool for creating projects, generating code, and managing development workflows.
Install it globally:
dart pub global activate hypermodern_cli
Verify the installation:
hypermodern --version
You should see output similar to:
Hypermodern CLI v1.0.0
Setting Up Your Development Environment
For the best development experience, configure your editor:
VS Code Extensions:
- Dart (official)
- Flutter (if you plan to build Flutter clients)
- JSON (for schema editing)
Editor Settings: Enable format on save and automatic imports for a smoother workflow.
Your First Hypermodern Project
Let's create a simple user management API to demonstrate Hypermodern's capabilities.
Creating the Project
hypermodern create user_api
cd user_api
This creates a new project with the following structure:
user_api/
├── schemas/
│ └── api.json # API schema definitions
├── lib/
│ ├── generated/src/ # Auto-generated code
│ ├── models/ # Custom model extensions
│ └── main.dart # Server entry point
├── client_example/
│ └── main.dart # Example client usage
├── pubspec.yaml # Dependencies
└── README.md # Project documentation
Understanding the Project Structure
schemas/: Contains JSON schema files that define your API contract. This is where you'll spend most of your time defining models and endpoints.
lib/generated/: Auto-generated code based on your schemas. Never edit these files directly - they're regenerated whenever schemas change.
lib/models/: Custom extensions and business logic for your generated models.
client_example/: Example client code showing how to consume your API.
Examining the Default Schema
Open schemas/api.json to see the default schema:
{
"models": {
"user": {
"id": "int64",
"name": "string",
"email": "string",
"created_at": "datetime"
}
},
"endpoints": {
"get_user": {
"method": "GET",
"path": "/users/{id}",
"request": {
"id": "int64"
},
"response": "@user",
"transports": ["http", "websocket", "tcp"]
},
"create_user": {
"method": "POST",
"path": "/users",
"request": {
"name": "string",
"email": "string"
},
"response": "@user",
"transports": ["http", "websocket", "tcp"]
}
}
}
This schema defines:
- A
usermodel with id, name, email, and creation timestamp - Two endpoints:
get_userandcreate_user - Support for all three transport protocols
Generating Code
Generate the type-safe client and server code:
hypermodern generate
This creates several files in lib/generated/:
models.dart: User model with serializationclient.dart: Type-safe client libraryserver.dart: Server endpoint stubs
Implementing Server Logic
Open lib/main.dart and implement the endpoint handlers:
import 'package:hypermodern_server/hypermodern_server.dart';
import 'generated/models.dart';
import 'generated/server.dart';
// In-memory storage for demo
final Map<int, User> users = {};
int nextId = 1;
void main() async {
final server = HypermodernServer();
// Register endpoint handlers
server.registerEndpoint<GetUserRequest, User>(
'get_user',
(request) async {
final user = users[request.id];
if (user == null) {
throw NotFoundException('User not found');
}
return user;
},
);
server.registerEndpoint<CreateUserRequest, User>(
'create_user',
(request) async {
final user = User(
id: nextId++,
name: request.name,
email: request.email,
createdAt: DateTime.now(),
);
users[user.id] = user;
return user;
},
);
// Start all protocol servers
await server.listen(
httpPort: 8080,
wsPort: 8082,
tcpPort: 8081,
);
print('🚀 Server running on:');
print(' HTTP: http://localhost:8080');
print(' WebSocket: ws://localhost:8082');
print(' TCP: localhost:8081');
}
Running the Development Server
Start the server with hot reload:
hypermodern serve
The server will start and display:
🚀 Server running on:
HTTP: http://localhost:8080
WebSocket: ws://localhost:8082
TCP: localhost:8081
👀 Watching for changes...
Basic Client-Server Communication
Testing with HTTP
You can test the HTTP endpoint using curl:
# Create a user
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
# Get the user
curl http://localhost:8080/users/1
Using the Generated Client
Create a simple client in client_example/main.dart:
import 'package:hypermodern/hypermodern.dart';
import '../lib/generated/models.dart';
import '../lib/generated/client.dart';
void main() async {
// Create client (automatically selects WebSocket)
final client = HypermodernClient('ws://localhost:8082');
try {
// Connect to server
await client.connect();
print('✅ Connected to server');
// Create a user
final createRequest = CreateUserRequest(
name: 'Bob',
email: 'bob@example.com',
);
final newUser = await client.request<User>('create_user', createRequest);
print('👤 Created user: ${newUser.name} (ID: ${newUser.id})');
// Get the user back
final getRequest = GetUserRequest(id: newUser.id);
final fetchedUser = await client.request<User>('get_user', getRequest);
print('📥 Fetched user: ${fetchedUser.name}');
} catch (e) {
print('❌ Error: $e');
} finally {
await client.disconnect();
print('👋 Disconnected');
}
}
Run the client:
cd client_example
dart run main.dart
You should see:
✅ Connected to server
👤 Created user: Bob (ID: 1)
📥 Fetched user: Bob
👋 Disconnected
Protocol Flexibility
The beauty of Hypermodern is that you can switch protocols without changing your client code:
// Use HTTP instead
final httpClient = HypermodernClient('http://localhost:8080');
// Use TCP for maximum performance
final tcpClient = HypermodernClient('tcp://localhost:8081');
// All use the same API!
final user = await client.request<User>('get_user', GetUserRequest(id: 1));
Understanding the Generated Code
Type-Safe Models
The generated User class includes:
class User {
final int id;
final String name;
final String email;
final DateTime createdAt;
User({
required this.id,
required this.name,
required this.email,
required this.createdAt,
});
// Serialization methods
Map<String, dynamic> toJson() => { /* ... */ };
factory User.fromJson(Map<String, dynamic> json) => { /* ... */ };
// Binary serialization
Uint8List toBinary() => { /* ... */ };
factory User.fromBinary(Uint8List data) => { /* ... */ };
}
Request/Response Types
For each endpoint, request and response types are generated:
class GetUserRequest {
final int id;
GetUserRequest({required this.id});
// Serialization methods...
}
class CreateUserRequest {
final String name;
final String email;
CreateUserRequest({required this.name, required this.email});
// Serialization methods...
}
Client Methods
The generated client provides type-safe methods:
class ApiClient {
Future<User> getUser(GetUserRequest request) async {
return await _client.request<User>('get_user', request);
}
Future<User> createUser(CreateUserRequest request) async {
return await _client.request<User>('create_user', request);
}
}
Development Workflow
Schema-First Development
- Define your API in
schemas/api.json - Generate code with
hypermodern generate - Implement handlers in your server code
- Test with clients using the generated client library
Hot Reload
When using hypermodern serve, changes to your schema or server code trigger automatic regeneration and server restart. This makes iteration fast and seamless.
Adding New Endpoints
To add a new endpoint, update your schema:
{
"endpoints": {
"list_users": {
"method": "GET",
"path": "/users",
"request": {},
"response": {
"users": "[@user]"
},
"transports": ["http", "websocket", "tcp"]
}
}
}
Run hypermodern generate and implement the handler:
server.registerEndpoint<ListUsersRequest, ListUsersResponse>(
'list_users',
(request) async {
return ListUsersResponse(users: users.values.toList());
},
);
What's Next
You now have a working Hypermodern application with multi-protocol support and type-safe communication. In the next chapter, we'll dive deeper into the core concepts that make this possible, including the transport protocols, unified routing system, and binary serialization.
You'll learn how Hypermodern achieves protocol transparency and why this approach is so powerful for modern application development.