Swift no es solo para apps de iOS y macOS. Desde que Apple liberó el lenguaje como open source en 2015 y publicó soporte oficial para Linux, existe un ecosistema de backend Swift real. Vapor es el framework web más maduro de ese ecosistema: lleva activo desde 2016, y la versión 4 (lanzada en 2020 y mantenida activamente) es la base de muchos servicios en producción. En 2026, con Swift 6 y structured concurrency integrados, Vapor 4 es una opción seria para backend HTTP cuando ya tienes equipos con experiencia en Swift.
Por qué Swift en servidor
El argumento principal es compartir código entre cliente y servidor. Si tu app iOS usa modelos Swift con Codable, esos mismos structs pueden usarse en el backend sin ninguna duplicación. El tipado estático de Swift también elimina una clase entera de errores en tiempo de ejecución que son habituales en Node.js.
El rendimiento es comparable a Go para cargas de trabajo típicas de APIs REST, aunque sin el ecosistema de librerías de Go o la adopción masiva que tiene. Si buscas un lenguaje de servidor con años de madurez y un ecosistema enorme, Go o Rust son mejores opciones. Si tu equipo ya sabe Swift y quieres compartir lógica con el cliente, Vapor tiene sentido.
Instalación y primer proyecto
Vapor necesita Swift 5.9 o superior y funciona en macOS y Linux. La forma más sencilla de empezar es con la CLI de Vapor:
# macOS con Homebrew brew install vapor # Crear nuevo proyecto vapor new MiAPI --template=api cd MiAPI swift run
El servidor arranca en http://localhost:8080. La estructura del proyecto usa Swift Package Manager: no hay configuración externa, todo está en Package.swift.
Rutas y controladores
Las rutas en Vapor se definen en configure.swift o en controladores separados. La sintaxis es fluida y se lee bien:
import Vapor
func routes(_ app: Application) throws {
// Ruta simple
app.get("saludo") { req async in
"Hola desde Vapor"
}
// Ruta con parámetro
app.get("usuario", ":id") { req async throws -> Usuario in
guard let id = req.parameters.get("id", as: UUID.self) else {
throw Abort(.badRequest)
}
guard let usuario = try await Usuario.find(id, on: req.db) else {
throw Abort(.notFound)
}
return usuario
}
// Grupo de rutas con prefijo y middleware
let api = app.grouped("api", "v1")
.grouped(TokenAuthMiddleware())
api.post("articulos", use: ArticuloController().crear)
}
Fluent: ORM para Swift
Vapor incluye Fluent, un ORM que soporta PostgreSQL, MySQL, SQLite y MongoDB. Los modelos son structs o clases conformando el protocolo Model:
import Fluent
import Vapor
final class Articulo: Model, Content {
static let schema = "articulos"
@ID(key: .id)
var id: UUID?
@Field(key: "titulo")
var titulo: String
@Field(key: "contenido")
var contenido: String
@Timestamp(key: "created_at", on: .create)
var createdAt: Date?
init() {}
init(id: UUID? = nil, titulo: String, contenido: String) {
self.id = id
self.titulo = titulo
self.contenido = contenido
}
}
Las migraciones son código Swift tipado:
struct CreateArticulo: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("articulos")
.id()
.field("titulo", .string, .required)
.field("contenido", .string, .required)
.field("created_at", .datetime)
.create()
}
func revert(on database: Database) async throws {
try await database.schema("articulos").delete()
}
}
Autenticación con JWT
Vapor tiene soporte integrado para JWT a través del paquete vapor/jwt:
// Generar token
let payload = TokenPayload(
sub: .init(value: usuario.id!.uuidString),
exp: .init(value: Date().addingTimeInterval(3600))
)
let token = try req.jwt.sign(payload)
// Verificar token en middleware
struct TokenAuthMiddleware: AsyncMiddleware {
func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response {
let payload = try request.jwt.verify(as: TokenPayload.self)
request.auth.login(payload)
return try await next.respond(to: request)
}
}
Vapor y Swift 6
Vapor 4 es compatible con Swift 6 y su modo de concurrencia estricta. Las rutas son automáticamente @Sendable, los handlers son funciones async que el compilador verifica. Si vienes de frameworks como Express en Node.js o Gin en Go, el equivalente en Go con goroutines es diferente en filosofía pero similar en ergonomía de routing.
Despliegue
Vapor se despliega como cualquier binario nativo. La forma más común es Docker:
# Dockerfile de ejemplo FROM swift:5.10-jammy as builder WORKDIR /app COPY . . RUN swift build -c release FROM ubuntu:jammy WORKDIR /app COPY --from=builder /app/.build/release/App . EXPOSE 8080 CMD ["./App", "serve", "--hostname", "0.0.0.0"]
El binario resultante es pequeño (10-30 MB típicamente) y arranca en milisegundos. No necesita runtime de Node, JVM ni intérprete de Python.
Imagen: Pexels / Digital Buggu
