Vapor Drawing Backend - Project Overview
A collaborative note-taking backend built with Vapor (Swift) that supports real-time collaboration, authentication, and drawing strokes persistence.
Tech Stack
- Framework: Vapor 4.115.0
- Database: MongoDB with Fluent ORM
- Authentication: JWT (JSON Web Tokens)
- Real-time: WebSockets
- Language: Swift 6.0
Features Implemented
1. Authentication System
JWT-based authentication with user registration and login functionality.
// User model with email and password
final class User: Model, Content, Authenticatable {
static let schema = "users"
@ID(key: .id)
var id: UUID?
@Field(key: "email")
var email: String
@Field(key: "password_hash")
var passwordHash: String
}
Endpoints:
-
POST /api/v1/auth/register- Register new user -
POST /api/v1/auth/login- Login user -
GET /api/v1/auth/me- Get current user (protected)
2. JWT Middleware
Custom authentication middleware that verifies JWT tokens and protects routes.
struct AuthMiddleware: AsyncMiddleware {
func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response {
let payload: UserToken = try await request.jwt.verify(as: UserToken.self)
guard let user = try await User.find(payload.userID, on: request.db) else {
throw Abort(.unauthorized, reason: "User not found")
}
request.auth.login(user)
return try await next.respond(to: request)
}
}
3. MongoDB with Fluent ORM
Database configuration using FluentMongoDriver with automatic migrations.
// Configure MongoDB
try app.databases.use(
.mongo(
connectionString: Environment.get("MONGODB_URI") ?? "mongodb://localhost:27017"
),
as: .mongo
)
// Run migrations
app.migrations.add(User.Migration())
app.migrations.add(NotesModel.Migration())
4. Notes Management
Full CRUD operations for notes with drawing strokes support.
final class NotesModel: Model, Content {
@Field(key: "title")
var title: String
@Field(key: "strokes")
var strokes: [DrawingStroke]
@Parent(key: "user_id")
var user: User
}
struct DrawingStroke: Codable {
let points: [DrawingPoint]
let color: DrawingColor
let width: Double
let timestamp: Date
}
Endpoints:
-
POST /api/v1/notes- Create note -
GET /api/v1/notes- Get all notes -
GET /api/v1/notes/get/:id- Get single note -
PUT /api/v1/notes/:id- Update note -
DELETE /api/v1/notes/:id- Delete note
5. Real-time WebSocket Collaboration
WebSocket manager for real-time collaborative editing with note session management.
final class WebSocketManager {
private var connections: [UUID: WebSocket] = [:]
private var noteCollaborators: [UUID: Set<UUID>] = [:]
func joinNoteSession(noteID: UUID, userID: UUID)
func leaveNoteSession(noteID: UUID, userID: UUID)
func broadcastToNote(noteID: UUID, message: String, excludeUserID: UUID?)
}
WebSocket Features:
- Join/leave note sessions
- Real-time stroke updates
- Broadcast to all collaborators (excluding sender)
- Personal messaging
Endpoint: WS /api/v1/auth/handleInvite (protected)
6. Note Sharing
Share notes via JWT tokens with external users.
// Share token endpoint
GET /api/v1/notes/shared/:shareToken
// Verifies JWT token and returns note
let payload = try await req.jwt.verify(shareToken, as: ShareTokenPayload.self)
Project Structure
Sources/NoterPlayBackend/
├── Controllers/
│ ├── AuthenticationController.swift
│ ├── NotesController.swift
│ └── InviteController.swift
├── Middleware/
│ └── AuthMiddleware.swift
├── Models/
│ ├── User.swift
│ ├── NotesModel.swift
│ ├── UserToken.swift
│ ├── ShareTokenPayload.swift
│ └── InviteModel.swift
├── WSManager/
│ └── WebSocketManager.swift
├── configure.swift
└── routes.swift
Getting Started
# Install dependencies
swift package resolve
# Run the server
swift run
# With Docker
docker-compose up
A lot to more to Fix it Up exploring just a small prototype.
Built with ❤️ using Vapor and Swift
Top comments (0)