Introduction: Why Swift is Suitable for REST API?
\n\nWhen it comes to server-side development, Swift is not the first language that comes to mind. However, with the emergence of the Vapor framework (and its alternative, Kitura), Swift has become a full-fledged tool for creating REST APIs. Why use Swift on the server? First, it's a single language for iOS, macOS, and the backend — you can reuse data models. Second, Swift is fast, safe, and has excellent support for asynchrony via async/await.
\n\nIn this guide, we will create a simple REST API for managing a task list (Todo list) in Swift using the Vapor framework. You will learn how to configure a server, create routes, work with JSON, and test the API. The entire process will take about 30 minutes.
\n\nStep 1: Installing and Setting Up the Project
\n\nBefore writing code, you need to install Vapor Toolbox — a command-line utility for creating and managing Vapor projects.
\n\nInstalling Vapor Toolbox
\nOpen the terminal and run:
\nbrew install vapor\nIf you don't have Homebrew, install it from brew.sh.
\n\nCreating a New Project
\nLet's create a new project named TodoAPI:
vapor new TodoAPI -n\ncd TodoAPI\nThe -n flag means "no example" — we will get a clean template.
Starting the Server
\nOpen the project in Xcode:
\nopen Package.swift\nSelect the Run scheme and press Cmd+R. After a few seconds, the server will start at http://127.0.0.1:8080. Open your browser — you should see a welcome message from Vapor.
Step 2: Creating the Data Model
\n\nOur API will manage tasks. Each task has an ID, a title, and a completion status (completed/not completed).
\n\nLet's create the Todo model. In Vapor, models are regular structures that conform to the Content protocol (for JSON serialization) and Codable.
Create the file Sources/App/Models/Todo.swift:
\nimport Vapor\n\nstruct Todo: Content, Codable {\n var id: UUID?\n var title: String\n var isCompleted: Bool\n \n init(id: UUID? = nil, title: String, isCompleted: Bool = false) {\n self.id = id\n self.title = title\n self.isCompleted = isCompleted\n }\n}\n\nNote: id is an optional UUID. We will generate it on the server when creating a task.
Step 3: Creating the Store and Controller
\n\nFor simplicity, we will store tasks in memory (in an array). In a real project, you would use a database (Fluent ORM), but for a 30-minute demo, this is overkill.
\n\nIn-Memory Store
\nCreate the file Sources/App/Controllers/TodoStore.swift:
\nimport Vapor\n\nfinal class TodoStore {\n private var todos: [Todo] = []\n private let lock = NSLock()\n \n func getAll() -> [Todo] {\n lock.lock()\n defer { lock.unlock() }\n return todos\n }\n \n func get(id: UUID) -> Todo? {\n lock.lock()\n defer { lock.unlock() }\n return todos.first { $0.id == id }\n }\n \n func create(_ todo: inout Todo) -> Todo {\n lock.lock()\n defer { lock.unlock() }\n todo.id = UUID()\n todos.append(todo)\n return todo\n }\n \n func update(id: UUID, with updatedTodo: Todo) -> Todo? {\n lock.lock()\n defer { lock.unlock() }\n guard let index = todos.firstIndex(where: { $0.id == id }) else { return nil }\n var mutableTodo = updatedTodo\n mutableTodo.id = id\n todos[index] = mutableTodo\n return mutableTodo\n }\n \n func delete(id: UUID) ->