This article introduces how to install the Vapor command-line tool on mac 10.5.4 and ubuntu 18.04, create Vapor projects, debug projects simply, and how to deploy to a production environment. The runtime environment mentioned in the article includes: Vapor 4, Swift 5.2, Catalina 10.5.4, Ubuntu 18.04.
I recently set up a new cloud server (Linux system) and used Hexo to create a new blog website. Considering adding some simple interactive features, I need to add logic processing capabilities on the server side. Having mainly used Swift for the past six months, I decided to try Server Side Swift. Without paying much attention to choosing from various frameworks, after watching a few videos introducing Vapor, I felt good about it and started to attempt installation and configuration.
The official documentation of Vapor really has some problems, and many experiences and tutorials online are a bit outdated (there have been significant version changes recently). After two days of struggling, I finally managed to figure out the main points and initially set up both development and production environments.
What is Vapor
Vapor is a web framework written in Swift, cross-platform (mac, Linux), allowing users to efficiently complete various network services using Swift and its rich third-party libraries.
import Vapor
let app = try Application()
let router = try app.make(Router.self)
router.get("hello") { req in
return "Hello, world."
}
try app.run()
The above code can complete a basic network service. Visiting http://localhost:8080/hello
returns hello, world.
You can develop on mac or Linux platforms, and also deploy projects developed with the Vapor framework to mac or Linux platforms.
What is Vaper Command Line Tool
The Vaper command-line tool functions include:
- Creating Vaper projects based on templates
- Configuring, compiling, and running projects
- Other features that work with the operating system
However, it is not essential. If users are already familiar with the development and configuration of Vapor, they can do without this command line tool in both development and running environments. But for beginners like me, it undoubtedly is a helpful tool.
Resources Needed for Developing Vaper Projects
- mac or Linux, I have successfully completed configurations under Catalina 10.5.4 and Ubuntu 18.04
- Swift language environment, currently Vapor 4 can run under Swift 5.2
- Web server, I am currently using Nginx (it can be not configured if only for development and testing)
Installing Swift
Mac
On mac platforms, install Xcode and Xcode Command Line Tools. Vapor 4 provides very friendly support for Xcode, allowing you to use all kinds of capabilities of Xcode like other Swift projects (such as breakpoint debugging).
Ubuntu 18.04
sudo apt-get install clang
sudo apt-get install libcurl3 libpython2.7 libpython2.7-dev
# Find the required file from swift.org
wget https://swift.org/builds/swift-5.2.3-release/ubuntu1804/swift-5.2.3-RELEASE/swift-5.2.3-RELEASE-ubuntu18.04.tar.gz
tar xzvf swift-5.2.3-RELEASE-ubuntu18.04.tar.gz
sudo mv swift-5.2.3-RELEASE-ubuntu18.04 /usr/share/swift
echo "export PATH=/usr/share/swift/usr/bin:$PATH" >> ~/.bashrc
source ~/.bashrc
I have also used Docker to install Swift 5.2, but the Image is quite large, requiring about 1.2 Gb of space.
# Method to install Swift with Docker.
docker pull swift
Installing Vapor Command Line Tool
MacOS
brew tap vapor/tap
brew install vapor
# I currently installed vapor-beta
# brew install vapor-beta
# Test vapor
vapor
Ubuntu 1804
Installing the Vapor toolbox on Ubuntu is slightly cumbersome, mainly because the current Vapor Toolbox source files have some issues and need some modifications to compile properly.
First, ensure that Swift is successfully installed
cd ~
git clone https://github.com/vapor/toolbox.git
cd toolbox
Create a LinuxMain.swift file in the Test directory
import XCTest
@testable import AppTests
XCTMain([testCase(AppTests.allTests)])
This is a file needed by Swift SPM. I just wrote the simplest one that can complete the compilation. I’m not sure why the official git source does not include this file.
Modify the
Source/VaporToolbox/exec.swift file
Around line 36 (current version), find
let spawned = posix_spawnp(&pid, argv[0], &fileActions, nil, argv + [nil], envp + [nil])
Change it to
guard let _argv0 = argv[0] else {
fatalError("unwrap error")
}
let spawned = posix_spawnp(&pid, _argv, &fileActions, nil, argv + [nil], envp + [nil])
Again, I’m not sure why there is such an error in the code.
After completing the above modifications
cd ~/toolbox
swift build -c release --disable-sandbox
sudo mv .build/release/vapor /usr/local/bin
The above steps worked on my local Ubuntu, but on my Tencent cloud server, there was a missing library during compilation, which after being added allowed normal compilation.
sudo apt-get install libcurl4 -y
Thus, the installation of Vapor Toolbox is complete.
The directory of Toolbox contains a Dockerfile, which can directly create a Docker Image of toolbox, and the system will automatically download the Docker Image of Swift. However, using this method on Ubuntu (entrypoint configured), the vapor Image had no name, only a container id, but it can be run through the id. I currently do not recommend this method.
Using Vapor Command Line Tool
Creating a Project
#vapor new <projectname> [--template]
vapor new hello
Create a Vapor project named ‘hello’ using the default template. This process essentially clones a template from GitHub and assists with basic configuration. For those already familiar, one can start a project directly by cloning a template from GitHub without using the tool.
On macOS, templates can be directly compiled and run. However, on Linux, the git source still lacks the LinuxMain.swift file. As mentioned above, adding this file to the project allows compilation.
After cloning, the system prompts as follows:
Would you like to use Fluent? y
Database type selection #I chose sqlite
The system automatically creates corresponding code in the template based on your selection. (Fluent is an ORM written in Swift.)
Compiling the Project
cd ~/hello
vapor build
On ubuntu, vapor new executes normally, but vapor build throws an error, so I use swift build to compile the project. In fact, build and run directly invoke the swift command.
Running the Project
vapor run
#The system display indicates that the project has started successfully. Accessible at http://127.0.0.1:8080
Environment(name: "development", arguments: [".build/x86_64-apple-macosx/debug/Run"])
[ NOTICE ] Server starting on http://127.0.0.1:8080
Add runtime states after the run command, which is particularly important for deployment.
vapor run --env prod
# test prod dev correspond to different states, mainly related to whether to display operational logs
For macOS, use
vapor xcode
to directly open Xcode, where you can edit, compile, debug, and run under Xcode.
Even without installing Vapor Toolbox, you can create an Xcode project using:
swift package generate-xcodeproj
Following these steps, we can start writing and running our own Vapor projects on both macOS and Ubuntu.
Brief Analysis of the Template Project
In this section, we quickly experience the convenience of Vapor through a simple analysis of the template code.
I used the default project template, enabled Fluent, and chose sqlite as the database.
The project source files are located in the Sources directory.
main.swift
serves as the entry point of the program, creating the Vapor service.
configure.swift
contains autogenerated code for database usage since we chose sqlite. The following code completes the creation of the database:
app.migrations.add(CreateTodo())
To fully run this template project, execute the following command in the terminal:
vapor run migrate
This completes the creation of tables in the db.sqlite file in the project root directory. Without this step, accessing localhost:8080/todos results in an error:
[ ERROR ] error: no such table: todos
Xcode users can also directly add —auto-migrate in the Scheme’s Arguments to accomplish the above.
Additionally, it’s best to set the Scheme — Run — Working Directory in Xcode to the project’s root directory. This ensures that whether using the command line or Xcode, the same Sqlite file is used.
//app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
Uncommenting this line enables Vapor to support static files. Static files placed in the Public directory of the project root can be accessed at 127.0.0.1:8080/index.html. If using in conjunction with other WebServers, it might be better to use something like Nginx for static file support.
routes.swift
, as the core of the project, sets up the web routing logic:
import Fluent
import Vapor
func routes(_ app: Application) throws {
/*
Access 127.0.0.1:8080/ Returns: "It works!"
*/
app.get { req in
return "It works!"
}
// localhost:8080/hello Returns: "Hello, world!"
app.get("hello") { req -> String in
return "Hello, world!"
}
let todoController = TodoController()
/*
The following operations were tested using Postman
post 127.0.0.1:8080/todos with body content: {"title":"Dongpo's Elbow"} to add a record
get
127.0.0.1:8080/todos to display the added records
del 127.0.0.1:8080/todos/B508471F-FF5F-422C-B384-C300FD7B49D9 to delete a record. The id is obtained from the displayed record
*/
app.get("todos", use: todoController.index)
app.post("todos", use: todoController.create)
app.delete("todos", ":todoID", use: todoController.delete)
}
Further application details are not expanded upon here. However, even from the template example alone, we can feel the convenience and efficiency of Vapor.
Using with Nginx
By editing the Nginx configuration file, our Vapor project can now be deployed to the public.
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri @proxy;
}
location @proxy {
proxy_pass http://127.0.0.1:8080;
proxy_pass_header Server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Server;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
Now, you can access your project via http://yourdomainoraddress/todos
.
I use Vapor to complement my blog, so it still needs to work in conjunction with my existing pages. Hence, I’ve adopted the following configuration.
Even with Vapor’s static page support, if I configure my Vapor project at the root (I’ve discontinued Vapor’s response to root), I still need to explicitly enter http://mydomain/index.html
to access the index page. That’s why I moved it under /api/.
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /api {
root html;
index index.html index.htm;
try_files $uri @proxy;
}
location @proxy {
proxy_pass http://127.0.0.1:8080;
proxy_pass_header Server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Server;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
After such configuration, you need to modify the code in routes.swift to access it normally.
app.get("api","hello") { req -> String in
return "Hello, world!"
}
If anyone knows how to set it up so that / is forwarded directly to Vapor and still use http://mydomain to access the original pages, please let me know.
Deployment
Modifying the Running Port
Vapor 4 has significant differences in specifying the running port compared to before.
Make the following changes in main.swift:
app.http.server.configuration.hostname = "127.0.0.1" //Address to respond to 0.0.0.0
app.http.server.configuration.port = 8000 //The port you wish to set
try configure(app)
Currently, I haven’t found how to set the running port from the command line (the method before Vapor 3 seems no longer supported). If anyone knows, please share.
Manual Deployment
Since I’m also new to Vapor, for debugging convenience, I develop under Xcode on my machine. Using GitHub as an intermediary, I commit local modifications to the repository. Manually fetch and execute on the server side. If executed in the terminal, the current terminal will be locked by the task.
Docker Deployment
Additionally, the Vapor template itself has already generated a Dockerfile. You can also directly create a Docker Image from the completed project. This method allows the project to be deployed on any Docker-supported platform (mac, Linux, windows, etc.). However, it’s usually suitable for deployment only after development is completed. For more details, refer to the official documentation.
Supervisor
Vapor Toolbox already provides support for Supervisor, allowing easy management of services through Supervisor.
Installing Supervisor on Ubuntu:
sudo apt-get update
sudo apt-get install supervisor
We need to create a Supervisor configuration file for each project. Create /etc/supervisor/conf.d/hello.conf:
[program:hello]
command=/home/parallels/hello/.build/release/Run serve --env production
directory=/home/parallels/hello
user=parallels
stdout_logfile=/var/log/supervisor/%(program_name)-stdout.log
stderr_logfile=/var/log/supervisor/%(program_name)-stderr.log
The file name is your project name followed by .conf, with the directory pointing to the root of your project and the username set correctly.
command=/home/parallels/hello/.build/release/Run serve --env production
Ensure that the project is compiled into a release version. If vapor build doesn’t work, you can use the following command:
cd
~/hello
swift build -c release
Managing the project through Supervisor:
supervisorctl reread
supervisorctl add hello
supervisorctl start hello
You can also specify the running port through Supervisor’s configuration.
Add to /etc/supervisor/conf.d/hello.conf:
environment=PORT=8123
Modify main.swift:
let port = Environment.get("PORT") ?? ""
app.http.server.configuration.port = Int(port) ?? 8080
Conclusion
I hope this article helps you get started with Vapor 4. At the same time, I also hope to see Swift perform on more platforms.
Swift has shown more official support for Windows.