├── .dockerignore ├── .gitignore ├── .vscode └── launch.json ├── Dockerfile ├── LICENSE ├── Package.resolved ├── Package.swift ├── Public └── .gitkeep ├── README.md ├── Sources ├── App │ ├── Controllers │ │ └── .gitkeep │ ├── configure.swift │ └── routes.swift └── Run │ └── main.swift ├── Tests └── AppTests │ └── AppTests.swift └── docker-compose.yml /.dockerignore: -------------------------------------------------------------------------------- 1 | .build/ 2 | .swiftpm/ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Packages 2 | .build 3 | xcuserdata 4 | *.xcodeproj 5 | DerivedData/ 6 | .DS_Store 7 | db.sqlite 8 | .swiftpm 9 | .env 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "type": "lldb", 5 | "request": "launch", 6 | "name": "Debug Run", 7 | "program": "${workspaceFolder:HelloVapor}/.build/debug/Run", 8 | "args": [], 9 | "cwd": "${workspaceFolder:HelloVapor}", 10 | "preLaunchTask": "swift: Build Debug Run" 11 | }, 12 | { 13 | "type": "lldb", 14 | "request": "launch", 15 | "name": "Release Run", 16 | "program": "${workspaceFolder:HelloVapor}/.build/release/Run", 17 | "args": [], 18 | "cwd": "${workspaceFolder:HelloVapor}", 19 | "preLaunchTask": "swift: Build Release Run" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # ================================ 2 | # Build image 3 | # ================================ 4 | FROM swift:5.7-jammy as build 5 | 6 | # Install OS updates and, if needed, sqlite3 7 | RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ 8 | && apt-get -q update \ 9 | && apt-get -q dist-upgrade -y\ 10 | && rm -rf /var/lib/apt/lists/* 11 | 12 | # Set up a build area 13 | WORKDIR /build 14 | 15 | # First just resolve dependencies. 16 | # This creates a cached layer that can be reused 17 | # as long as your Package.swift/Package.resolved 18 | # files do not change. 19 | COPY ./Package.* ./ 20 | RUN swift package resolve 21 | 22 | # Copy entire repo into container 23 | COPY . . 24 | 25 | # Build everything, with optimizations 26 | RUN swift build -c release --static-swift-stdlib 27 | 28 | # Switch to the staging area 29 | WORKDIR /staging 30 | 31 | # Copy main executable to staging area 32 | RUN cp "$(swift build --package-path /build -c release --show-bin-path)/Run" ./ 33 | 34 | # Copy resources bundled by SPM to staging area 35 | RUN find -L "$(swift build --package-path /build -c release --show-bin-path)/" -regex '.*\.resources$' -exec cp -Ra {} ./ \; 36 | 37 | # Copy any resources from the public directory and views directory if the directories exist 38 | # Ensure that by default, neither the directory nor any of its contents are writable. 39 | RUN [ -d /build/Public ] && { mv /build/Public ./Public && chmod -R a-w ./Public; } || true 40 | RUN [ -d /build/Resources ] && { mv /build/Resources ./Resources && chmod -R a-w ./Resources; } || true 41 | 42 | # ================================ 43 | # Run image 44 | # ================================ 45 | FROM ubuntu:jammy 46 | 47 | # Make sure all system packages are up to date, and install only essential packages. 48 | RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ 49 | && apt-get -q update \ 50 | && apt-get -q dist-upgrade -y \ 51 | && apt-get -q install -y \ 52 | ca-certificates \ 53 | tzdata \ 54 | # If your app or its dependencies import FoundationNetworking, also install `libcurl4`. 55 | # libcurl4 \ 56 | # If your app or its dependencies import FoundationXML, also install `libxml2`. 57 | # libxml2 \ 58 | && rm -r /var/lib/apt/lists/* 59 | 60 | # Create a vapor user and group with /app as its home directory 61 | RUN useradd --user-group --create-home --system --skel /dev/null --home-dir /app vapor 62 | 63 | # Switch to the new home directory 64 | WORKDIR /app 65 | 66 | # Copy built executable and any staged resources from builder 67 | COPY --from=build --chown=vapor:vapor /staging /app 68 | 69 | # Ensure all further commands run as the vapor user 70 | USER vapor:vapor 71 | 72 | # Let Docker bind to port 8080 73 | EXPOSE 8080 74 | 75 | # Start the Vapor service when the image is run, default to listening on 8080 in production environment 76 | ENTRYPOINT ["./Run"] 77 | CMD ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"] 78 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Vapor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "async-http-client", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/swift-server/async-http-client.git", 7 | "state" : { 8 | "revision" : "7f05a8da46cc2a4ab43218722298b81ac7a08031", 9 | "version" : "1.13.2" 10 | } 11 | }, 12 | { 13 | "identity" : "async-kit", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/vapor/async-kit.git", 16 | "state" : { 17 | "revision" : "9acea4c92f51a5885c149904f0d11db4712dda80", 18 | "version" : "1.16.0" 19 | } 20 | }, 21 | { 22 | "identity" : "console-kit", 23 | "kind" : "remoteSourceControl", 24 | "location" : "https://github.com/vapor/console-kit.git", 25 | "state" : { 26 | "revision" : "447f1046fb4e9df40973fe426ecb24a6f0e8d3b4", 27 | "version" : "4.6.0" 28 | } 29 | }, 30 | { 31 | "identity" : "multipart-kit", 32 | "kind" : "remoteSourceControl", 33 | "location" : "https://github.com/vapor/multipart-kit.git", 34 | "state" : { 35 | "revision" : "0d55c35e788451ee27222783c7d363cb88092fab", 36 | "version" : "4.5.2" 37 | } 38 | }, 39 | { 40 | "identity" : "routing-kit", 41 | "kind" : "remoteSourceControl", 42 | "location" : "https://github.com/vapor/routing-kit.git", 43 | "state" : { 44 | "revision" : "ffac7b3a127ce1e85fb232f1a6271164628809ad", 45 | "version" : "4.6.0" 46 | } 47 | }, 48 | { 49 | "identity" : "swift-algorithms", 50 | "kind" : "remoteSourceControl", 51 | "location" : "https://github.com/apple/swift-algorithms.git", 52 | "state" : { 53 | "revision" : "b14b7f4c528c942f121c8b860b9410b2bf57825e", 54 | "version" : "1.0.0" 55 | } 56 | }, 57 | { 58 | "identity" : "swift-atomics", 59 | "kind" : "remoteSourceControl", 60 | "location" : "https://github.com/apple/swift-atomics.git", 61 | "state" : { 62 | "revision" : "ff3d2212b6b093db7f177d0855adbc4ef9c5f036", 63 | "version" : "1.0.3" 64 | } 65 | }, 66 | { 67 | "identity" : "swift-backtrace", 68 | "kind" : "remoteSourceControl", 69 | "location" : "https://github.com/swift-server/swift-backtrace.git", 70 | "state" : { 71 | "revision" : "f25620d5d05e2f1ba27154b40cafea2b67566956", 72 | "version" : "1.3.3" 73 | } 74 | }, 75 | { 76 | "identity" : "swift-collections", 77 | "kind" : "remoteSourceControl", 78 | "location" : "https://github.com/apple/swift-collections.git", 79 | "state" : { 80 | "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2", 81 | "version" : "1.0.4" 82 | } 83 | }, 84 | { 85 | "identity" : "swift-crypto", 86 | "kind" : "remoteSourceControl", 87 | "location" : "https://github.com/apple/swift-crypto.git", 88 | "state" : { 89 | "revision" : "75ec60b8b4cc0f085c3ac414f3dca5625fa3588e", 90 | "version" : "2.2.4" 91 | } 92 | }, 93 | { 94 | "identity" : "swift-log", 95 | "kind" : "remoteSourceControl", 96 | "location" : "https://github.com/apple/swift-log.git", 97 | "state" : { 98 | "revision" : "32e8d724467f8fe623624570367e3d50c5638e46", 99 | "version" : "1.5.2" 100 | } 101 | }, 102 | { 103 | "identity" : "swift-metrics", 104 | "kind" : "remoteSourceControl", 105 | "location" : "https://github.com/apple/swift-metrics.git", 106 | "state" : { 107 | "revision" : "9b39d811a83cf18b79d7d5513b06f8b290198b10", 108 | "version" : "2.3.3" 109 | } 110 | }, 111 | { 112 | "identity" : "swift-nio", 113 | "kind" : "remoteSourceControl", 114 | "location" : "https://github.com/apple/swift-nio.git", 115 | "state" : { 116 | "revision" : "45167b8006448c79dda4b7bd604e07a034c15c49", 117 | "version" : "2.48.0" 118 | } 119 | }, 120 | { 121 | "identity" : "swift-nio-extras", 122 | "kind" : "remoteSourceControl", 123 | "location" : "https://github.com/apple/swift-nio-extras.git", 124 | "state" : { 125 | "revision" : "91dd2d61fb772e1311bb5f13b59266b579d77e42", 126 | "version" : "1.15.0" 127 | } 128 | }, 129 | { 130 | "identity" : "swift-nio-http2", 131 | "kind" : "remoteSourceControl", 132 | "location" : "https://github.com/apple/swift-nio-http2.git", 133 | "state" : { 134 | "revision" : "d6656967f33ed8b368b38e4b198631fc7c484a40", 135 | "version" : "1.23.1" 136 | } 137 | }, 138 | { 139 | "identity" : "swift-nio-ssl", 140 | "kind" : "remoteSourceControl", 141 | "location" : "https://github.com/apple/swift-nio-ssl.git", 142 | "state" : { 143 | "revision" : "4fb7ead803e38949eb1d6fabb849206a72c580f3", 144 | "version" : "2.23.0" 145 | } 146 | }, 147 | { 148 | "identity" : "swift-nio-transport-services", 149 | "kind" : "remoteSourceControl", 150 | "location" : "https://github.com/apple/swift-nio-transport-services.git", 151 | "state" : { 152 | "revision" : "c0d9a144cfaec8d3d596aadde3039286a266c15c", 153 | "version" : "1.15.0" 154 | } 155 | }, 156 | { 157 | "identity" : "swift-numerics", 158 | "kind" : "remoteSourceControl", 159 | "location" : "https://github.com/apple/swift-numerics", 160 | "state" : { 161 | "revision" : "0a5bc04095a675662cf24757cc0640aa2204253b", 162 | "version" : "1.0.2" 163 | } 164 | }, 165 | { 166 | "identity" : "vapor", 167 | "kind" : "remoteSourceControl", 168 | "location" : "https://github.com/vapor/vapor.git", 169 | "state" : { 170 | "revision" : "7b76fe01a8eb02aa7f61d9ca10624f98b25a5735", 171 | "version" : "4.69.2" 172 | } 173 | }, 174 | { 175 | "identity" : "websocket-kit", 176 | "kind" : "remoteSourceControl", 177 | "location" : "https://github.com/vapor/websocket-kit.git", 178 | "state" : { 179 | "revision" : "2d9d2188a08eef4a869d368daab21b3c08510991", 180 | "version" : "2.6.1" 181 | } 182 | } 183 | ], 184 | "version" : 2 185 | } 186 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.6 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "HelloVapor", 6 | platforms: [ 7 | .macOS(.v12) 8 | ], 9 | dependencies: [ 10 | // 💧 A server-side Swift web framework. 11 | .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"), 12 | ], 13 | targets: [ 14 | .target( 15 | name: "App", 16 | dependencies: [ 17 | .product(name: "Vapor", package: "vapor") 18 | ], 19 | swiftSettings: [ 20 | // Enable better optimizations when building in Release configuration. Despite the use of 21 | // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release 22 | // builds. See for details. 23 | .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release)) 24 | ] 25 | ), 26 | .executableTarget(name: "Run", dependencies: [.target(name: "App")]), 27 | .testTarget(name: "AppTests", dependencies: [ 28 | .target(name: "App"), 29 | .product(name: "XCTVapor", package: "vapor"), 30 | ]) 31 | ] 32 | ) 33 | -------------------------------------------------------------------------------- /Public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vapor/swift-getting-started-web-server/fa1c0819c5d75f3bf6863d200d3a68ecbd240bbf/Public/.gitkeep -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vapor's Getting Started Guide 2 | 3 | This repo contains the code for the Getting Started Guide on Vapor on [swift.org](https://swift.org). 4 | 5 | (Link to guide to follow) 6 | -------------------------------------------------------------------------------- /Sources/App/Controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vapor/swift-getting-started-web-server/fa1c0819c5d75f3bf6863d200d3a68ecbd240bbf/Sources/App/Controllers/.gitkeep -------------------------------------------------------------------------------- /Sources/App/configure.swift: -------------------------------------------------------------------------------- 1 | import Vapor 2 | 3 | // configures your application 4 | public func configure(_ app: Application) throws { 5 | // uncomment to serve files from /Public folder 6 | // app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory)) 7 | 8 | // register routes 9 | try routes(app) 10 | } 11 | -------------------------------------------------------------------------------- /Sources/App/routes.swift: -------------------------------------------------------------------------------- 1 | import Vapor 2 | 3 | func routes(_ app: Application) throws { 4 | app.get { req async in 5 | "It works!" 6 | } 7 | 8 | app.get("hello") { req async -> String in 9 | "Hello, world!" 10 | } 11 | 12 | app.get("hello", ":name") { req async throws -> String in 13 | let name = try req.parameters.require("name") 14 | return "Hello, \(name.capitalized)!" 15 | } 16 | 17 | app.get("json", ":name") { req async throws -> UserResponse in 18 | let name = try req.parameters.require("name") 19 | let message = "Hello, \(name.capitalized)!" 20 | return UserResponse(message: message) 21 | } 22 | 23 | app.post("user-info") { req async throws -> UserResponse in 24 | let userInfo = try req.content.decode(UserInfo.self) 25 | let message = "Hello, \(userInfo.name.capitalized)! You are \(userInfo.age) years old." 26 | return UserResponse(message: message) 27 | } 28 | } 29 | 30 | struct UserResponse: Content { 31 | let message: String 32 | } 33 | 34 | struct UserInfo: Content { 35 | let name: String 36 | let age: Int 37 | } -------------------------------------------------------------------------------- /Sources/Run/main.swift: -------------------------------------------------------------------------------- 1 | import App 2 | import Vapor 3 | 4 | var env = try Environment.detect() 5 | try LoggingSystem.bootstrap(from: &env) 6 | let app = Application(env) 7 | defer { app.shutdown() } 8 | try configure(app) 9 | try app.run() 10 | -------------------------------------------------------------------------------- /Tests/AppTests/AppTests.swift: -------------------------------------------------------------------------------- 1 | @testable import App 2 | import XCTVapor 3 | 4 | final class AppTests: XCTestCase { 5 | func testHelloWorld() throws { 6 | let app = Application(.testing) 7 | defer { app.shutdown() } 8 | try configure(app) 9 | 10 | try app.test(.GET, "hello", afterResponse: { res in 11 | XCTAssertEqual(res.status, .ok) 12 | XCTAssertEqual(res.body.string, "Hello, world!") 13 | }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Docker Compose file for Vapor 2 | # 3 | # Install Docker on your system to run and test 4 | # your Vapor app in a production-like environment. 5 | # 6 | # Note: This file is intended for testing and does not 7 | # implement best practices for a production deployment. 8 | # 9 | # Learn more: https://docs.docker.com/compose/reference/ 10 | # 11 | # Build images: docker-compose build 12 | # Start app: docker-compose up app 13 | # Stop all: docker-compose down 14 | # 15 | version: '3.7' 16 | 17 | x-shared_environment: &shared_environment 18 | LOG_LEVEL: ${LOG_LEVEL:-debug} 19 | 20 | services: 21 | app: 22 | image: hello-vapor:latest 23 | build: 24 | context: . 25 | environment: 26 | <<: *shared_environment 27 | ports: 28 | - '8080:8080' 29 | # user: '0' # uncomment to run as root for testing purposes even though Dockerfile defines 'vapor' user. 30 | command: ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"] 31 | --------------------------------------------------------------------------------