Swiftpack.co - Package - vapor/vapor

Vapor

Documentation Team Chat MIT License Continuous Integration Swift 5.1 Twitter


Vapor is a web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website, API, or cloud project.

Take a look at some of the awesome stuff created with Vapor.

💧 Community

Join the welcoming community of fellow Vapor developers on Discord.

🚀 Contributing

To contribute a feature or idea to Vapor, create an issue explaining your idea or bring it up on Discord.

If you find a bug, please create an issue.

If you find a security vulnerability, please contact security@vapor.codes as soon as possible.

💛 Sponsors

Support Vapor's development by becoming a sponsor.

Nodes Skelpo Gwynne Raskind Autimatisering Kyle Browning

💚 Backers

Support Vapor's development by becoming a backer.

analytics

Github

link
Stars: 17983

Releases

Prevent a crash when decoding a single value from the query container - 2020-01-29 10:21:48

Before this change, decoding a wrapped value from a non-existing query parameter would crash the application.

Environment-Specific DotEnv Files - 2020-01-24 02:12:09

Application now attempts to load an environment-specific dotenv file during init. Any values in this file will override values from the default dotenv (#2159, fixes #2156).

For example, say you have the following files:

.env

FOO=bar

.env.development

FOO=baz

If you run your app with --env development (or -e dev), the app will load both files and use FOO=baz. If you run your app with --env production (or -e prod), then only .env will be loaded and you will get FOO=bar.

This allows for a useful pattern where .env files are committed to git, but .env.* files are not. On new machines, or during deployment, you can run the following command to generate a new, local dotenv and easily fill out the required values.

$ cp .env .env.production

Note: Variables already existing in the process environment will not be overridden by dotenv files. For example, the following command will always use FOO=qux regardless of dotenv file values.

$ FOO=qux vapor run serve
// or
$ export FOO=qux
$ vapor run serve

Add LOG_LEVEL Environment Variable - 2020-01-23 03:45:57

Allows log level to be set by exporting the LOG_LEVEL environment variable.

export LOG_LEVEL=trace

This supports the same values that can be passed to --log:

  • trace
  • debug
  • info
  • notice
  • warning
  • error
  • critical

Smal Auth/c Patch - 2020-01-21 03:18:06

Removes UserTokenAuthenticator since this is redundant with what Fluent now offers. Adds base64 convenience to [UInt8] for generating base-64 encoded strings.

[UInt8].random(count: 16).base64

Improve Validation Errors - 2020-01-20 21:01:28

Improves the output of some validation errors as well as conforms ValidationsError to AbortError so that the errors can be seen.

Add EventLoopGroupProvider - 2020-01-19 18:37:32

Application.init now accepts an optional EventLoopGroupProvider. This allows you to embed a Vapor Application inside other NIO based projects and share one EventLoopGroup. Fixes #2128. (#2137)

Add swift-backtrace - 2020-01-18 18:59:53

Adds backtraces to error output from SIGILL interrupts on Linux.

Fatal error: 'try!' expression unexpectedly raised an error: Development.Test(): file /home/vapor/vapor/Sources/Development/routes.swift, line 13
Current stack trace:
0    libswiftCore.so                    0x00007f83289f0ea0 swift_reportError + 50
1    libswiftCore.so                    0x00007f8328a61f40 _swift_stdlib_reportFatalErrorInFile + 115
2    libswiftCore.so                    0x00007f8328985eee <unavailable> + 3514094
3    libswiftCore.so                    0x00007f8328986067 <unavailable> + 3514471
4    libswiftCore.so                    0x00007f832877b7cd <unavailable> + 1374157
5    libswiftCore.so                    0x00007f832895ce48 <unavailable> + 3345992
6    libswiftCore.so                    0x00007f83287b3741 <unavailable> + 1603393
7    Development                        0x0000560f8239a4c0 <unavailable> + 3138752
8    Development                        0x0000560f82396204 <unavailable> + 3121668
9    Development                        0x0000560f82397292 <unavailable> + 3125906
10   libc.so.6                          0x00007f8325f01ab0 __libc_start_main + 231
11   Development                        0x0000560f8214c6ea <unavailable> + 722666
+ 0x560f8219b349, closure #1 (Swift.Int32) -> () in static Backtrace.Backtrace.install() -> () at /home/vapor/vapor/.build/checkouts/swift-backtrace/Sources/Backtrace/Backtrace.swift:53
+ 0x560f8219b368, @objc closure #1 (Swift.Int32) -> () in static Backtrace.Backtrace.install() -> () at /home/vapor/vapor/<compiler-generated>:0
+ 0x7f832841f88f
+ 0x7f832895ce55
+ 0x7f83287b3740
+ 0x560f8239a4bf, Development.routes(Vapor.Application) throws -> () at /home/vapor/vapor/Sources/Development/routes.swift:13
+ 0x560f82396203, Development.configure(Vapor.Application) throws -> () at /home/vapor/vapor/Sources/Development/configure.swift:24
+ 0x560f82397291, main at /home/vapor/vapor/Sources/Development/main.swift:5
+ 0x7f8325f01b96
+ 0x560f8214c6e9
+ 0xffffffffffffffff
Illegal instruction (core dumped)

You may need to pass -Xswiftc -g to swift build for the names to demangle correctly.

Backtraces can be printed at any time using the Backtrace type:

import Vapor

Backtrace.print()

req.query.decode fix + test res.content.decode added - 2020-01-16 22:58:49

  • Fixed a bug where req.query.decode would fail if the query string was empty.
  • Adds res.content.decode to the test responses used by app.test.

Fix HTTP request decoder race - 2019-12-26 16:33:22

Fixes an issue where incomplete request bodies could be delivered to route handlers when the body arrives in two or more chunks. HTTPServerRequestDecoder was improved to better handle output from NIO's HTTPRequestDecoder, especially when implementing back pressure.

Vapor 4.0.0 Beta 3 - 2019-12-14 03:43:04

Example output:

# TYPE http_requests_total counter
http_requests_total 0
http_requests_total{status="200", path="GET /hello/:name", method="GET"} 7
http_requests_total{status="200", path="GET /metrics", method="GET"} 3
# TYPE http_request_duration_seconds summary
http_request_duration_seconds{quantile="0.01"} 0.000115894
http_request_duration_seconds{quantile="0.05"} 0.000115894
...
  • Updates to RoutingKit beta 3 (#2126)

  • ClientResponse, HTTPStatus, and HTTPHeaders are now Codable (#2124)

  • Environment variables loaded from .env files can now be accessed immediately after Application has initialized (#2125)

.env (in same folder as Package.swift)

FOO=BAR

main.swift

let app = Application(...)
defer { app.shutdown() } 

let foo = Environment.get("FOO") 
print(foo) // BAR

Vapor 4.0.0 Beta 2.1 - 2019-12-11 02:41:33

  • Access to shared HTTPClient is now available via app.client.http (#2120)

  • HTTPClient.Configuration can now be modified via app.client.configuration (#2120)

let app = Application(.testing)
defer { app.shutdown() }

app.client.configuration.redirectConfiguration = .disallow

app.get("redirect") {
    $0.redirect(to: "foo")
}

let server = try app.server.start(hostname: "localhost", port: 8080)
defer { server.shutdown() }

let res = try app.client.get("http://localhost:8080/redirect").wait()

XCTAssertEqual(res.status, .seeOther)
  • app.http has been removed. Each component is now separate (#2120)

  • New app.locks helper for managing synchronization of app extensions (#2120)

  • app.storage now supports registering an onShutdown closure that will run when the app shuts down (#2120)

Vapor 4.0.0 Beta 2 - 2019-12-09 16:19:42

  • Services has been refactored to be more type-safe. (#2098)

Services, Container, and Provider have been replaced by a pattern built on Swift extensions. This is best explained with examples.

Telling Vapor to use Leaf:

import Leaf
import Vapor

// beta.1
s.register(ViewRenderer.self) { c in
    return c.make(LeafRenderer.self)
}

// beta.2
app.views.use(.leaf)

Registering a factory service:

// beta.1
s.register(Foo.self) { c in
    return Foo(...)
}
app.make(Foo.self).bar()

// beta.2
extension Application {
    var foo: Foo { 
        return Foo(...)
    }
}
app.foo.bar()

Registering a singleton service:

// beta.1
s.register(Foo.self) { c in
    return Foo(...)
}
app.make(Foo.self).bar = 0
app.make(Foo.self).bar += 1
print(app.make(Foo.self).bar) // 1

// beta.2
extension Application {
    var foo: Foo { 
        if let existing = self.storage[FooKey.self] as? Foo {
            return existing
        } else {
            let new = Foo()
            self.storage[FooKey.self] = new
            return new
        }
    }

    private struct FooKey: StorageKey { 
        typealias Value = Foo
    }
}

app.foo.bar = 0
app.foo.bar += 1
print(app.foo.bar) // 1

This new pattern of extending Application also works with Request:

extension Application {
    var foo: Foo { ... }
}

extension Request {
    var bar: Bar { 
        return self.application.foo.bar(for: self)
    }
}
  • Validations has been refactored to yield better and more type-safe errors (#2071)

  • Authentication methods are now grouped under a new req.auth helper (#2111)

  • All authenticators now accept Request in their authenticate methods (#2111)

  • Added new ErrorSource struct to the AbortError protocol (#2093)

This new struct makes it easier to pass around information about where an error came from. It also makes it easier to indicate that a given error has no source information. This helps the logger avoid muddying logs with useless error source information.

  • RouteBuilder HTTP method helpers now support an array of [PathComponent] (#2097)

  • User-provided HTTP headers are no longer ignored when using XCTVapor test methods (#2108)

  • Enabled test discovery on Linux (#2118)

Vapor 4.0.0 Beta 1 - 2019-10-24 20:12:56

  • Application is now a global container. (#2079)

See below for a more in-depth explanation of this change, but these code examples explain it best:

Vapor 4 alpha:

let app = Application { s in 
    s.register(Foo.self) { ...}
}
defer { app.shutdown() }

let container = try app.makeContainer().wait()
defer { container.shutdown() }

let foo = try container.make(Foo.self)

Vapor 4 beta:

let app = Application()
defer { app.shutdown() }

app.register(Foo.self) { ... }
let foo = app.make(Foo.self)

In Vapor 3 and Vapor 4 alpha, Vapor's service architecture enforced a 1:1 relation between containers and event loops. This is a useful pattern for minimizing the amount of effort that needs to be spent on synchronizing concurrent access to services.

However, as Swift on the server continues to evolve it is becoming evident that this pattern may hinder Vapor's ability to take full advantage of packages designed to be thread-safe. For example, the swift-server/async-http-client package which recently saw a 1.0.0 release. This HTTP client supports being used across many event loops on a given event loop group. To avoid inefficiencies caused by hopping between event loops, the API allows users to specify event loop preferences with varying degrees of strictness. By giving the API consumer the ability to communicate exactly what they need, the HTTP client can choose the most performant option. This decision may change depending on the current state of its internal connection pool and other parameters.

For example, imagine the following scenario: Request A comes into your application on event loop 1 resulting in an external API request to foo.com. Request A completes and the HTTP client stores event loop 1's connection to foo.com into its pool. Request B comes into your application on event loop 2 and also wants to make an external API request to foo.com. In this situation, two things could happen:

1: Request B could make a new connection to foo.com on event loop 2 at the cost of TCP handshake. 2: Request B could re-use event loop 1's connection to foo.com at the cost of thread-hopping.

In this case option 2 is the clear winner as establishing new TCP connections is much more time consuming.

Requiring distinct copies of each service on a per event loop basis prevents these kinds of optimizations. Exceptions could be made for things like HTTP client, but that would mean adding extra complexity to the already complex services architecture. Additionally, async-http-client was built as a model for what good server-side Swift packages should look like.

  • Application is now a RoutesBuilder. (#2079)

Now that services are global, Application can be a routes builder. This makes single-file Vapor applications a lot more concise and is more similar to Vapor 3.

import Vapor

let app = try Application(environment: .detect())

app.get("hello") { req in
    return "Hello, world!"
}

try app.run()
  • Request now has access to the application. (#2079)

Since all reference type, singleton services are now expected to be thread-safe, Request can safely use the Application to create services as needed. Where this is especially useful is in creating request-specific contexts into services. A good example of this is how Database works in Fluent 4 beta.

app.get("todos") { req in
    return Todo.query(on: req.db).all()
}

This is powered by the following extension to Request:

extension Request {
    var db: Database { 
        return self.application.make(Database.self).with(req)
    }
}

The key here is that Database is passed a reference to the current Request for context. This enables database operations to do things like:

  • Delegate callbacks to the request event loop
  • Log query information and errors to the request's logger
  • Report metrics information on a per-request basis

Having explicit access to the context provided by request may be critical in production use cases. (See http://asim-malik.com/the-perils-of-node-continuation-local-storage/)

  • Service creation methods no longer throw. (#2079)

Errors thrown during service creation indicate a configuration failure. These errors should only happen during development-time and to make them easier to track down, they will now result in fatalError. This also makes it easier to use providers to extend Application in ways that feel native.

For example, this is how Application now conforms to RoutesBuilder:

extension Application: RoutesBuilder {
    public var routes: Routes { self.make() }
    public func add(route: Route) {
        self.routes.add(route: route)
    }
}
  • macOS 10.14+ and Linux are now the only officially supported platforms. (#2067, #2070)
  • Multipart parsing and serialization was broken out into vapor/multipart-kit (#2080)
  • WebSocket client module was broken out into vapor/websocket-kit (#2074)
  • UUID is now LosslessStringConvertible.
  • XCTVapor is now exported as a product.
  • Vapor repos are moving to GitHub actions for CI. (#2072)
  • HTTP body stream strategy .collect now supports an optional max size. (#2076)

Vapor 4.0.0 Alpha 3.2 - 2019-10-01 15:24:57

  • Added onStop future to Application.Running. (#2061)

Application.runCommands() was renamed to Application.start() and no longer blocks until complete. Application.run() now calls Application.start() and waits for Application.running's onStop future to complete.

Note: This change makes it easier to test Application since you can boot and start without needing a background thread.

  • Added new Services.global method for registering application-wide singletons. (#2062)

With this addition, there are now three ways to register a service:

  • register: This factory will be called each time the service is made.
  • singleton: This factory will be called only once per container.
  • global: This factory will be called only once per application.

Note that global services will be shared across event loops and must be thread-safe.

Below is an example usage of Services.global for creating an application-wide memory cache.

final class MemoryCache {
    var storage: [String: String]
    var lock: Lock

    init() {
        self.storage = [:]
        self.lock = .init()
    }

    func get(_ key: String) -> String? {
        self.lock.lock()
        defer { self.lock.unlock() }
        return self.storage[key]
    }

    func set(_ key: String, to value: String?) {
        self.lock.lock()
        defer { self.lock.unlock() }
        self.storage[key] = value
    }
}

The service can be registered globally:

public func configure(_ s: inout Services) {
    ...
    s.global(MemoryCache.self) { _ in
        return .init()
    }
}

The same instance of MemoryCache is now shared across containers:

public func routes(_ r: Routes, _ c: Container) throws {
    ...
    let cache = try c.make(MemoryCache.self)
    r.get("cache", "get", ":key") { req -> String in
        guard let key = req.parameters.get("key") else {
            throw Abort(.internalServerError)
        }
        return "\(key) = \(cache.get(key) ?? "nil")"
    }
    r.get("cache", "set", ":key", ":value") { req -> String in
        guard let key = req.parameters.get("key") else {
            throw Abort(.internalServerError)
        }
        guard let value = req.parameters.get("value") else {
            throw Abort(.internalServerError)
        }
        cache.set(key, to: value)
        return "\(key) = \(value)"
    }
}

This results in the cached values being accessible from across event loops.

Vapor 3.3.1 - 2019-09-18 11:41:14

Semver Patch release

  • Fix: Identifier not used in Abort init (#1923 @mikkelu)
  • Use FoundationNetworking on Linux for Swift 5.1 (#2028 @joscdk)

Vapor 4.0.0 Alpha 3.1.1 - 2019-08-29 16:01:56

  • Fixes a bug preventing --port, --host, and --bind overrides from being passed to the HTTP server. (#2043)

Notes: Prior to this fix, the console output would display the correct hostname and port, but the server would still bind to default hostname and port.

Vapor 4.0.0 Alpha 3.1 - 2019-08-27 22:27:32

Notes: This updated ConsoleKit release includes property wrapper support and internal cleanup. Vapor's ServeCommand, BootCommand, RouteCommand, and --env and --log flags have been updated to use the new syntax.

Vapor 4.0.0 Alpha 3 - 2019-08-26 15:12:15

  • Request no longer requires a Channel to init. Now, it requires an EventLoop and SocketAddress?. (#2037)

Note: This makes testing a Request easier since you can pass in either EmbeddedEventLoop or a real event loop from a group used elsewhere in your tests. Prior to this change, you were required to use EmbeddedChannel and EmbeddedEventLoop both of which are incompatible with real event loops.

  • Fixed a data race accessing Application.running. (#2027, #2037)

Note: Application.running allows you to programmatically shutdown your HTTP server from anywhere that you can access Application. This includes routes, commands, etc. Because this can be accessed from any thread, it required synchronization (NSLock) to access.

  • XCTApplication test helpers now require explicit start / shutdown. (#2037)

Note: Although a bit more verbose, explicitly starting and shutting down test helpers gives the user more options for how they test their application. It also cuts down on edge cases in the testing implementation.

Example from Vapor's tests with explicit start / shutdown:

let app = Application.create(routes: { r, c in
    r.get("hello", ":a") { req in
        return req.parameters.get("a") ?? ""
    }

    r.get("hello", ":a", ":b") { req in
        return [req.parameters.get("a") ?? "", req.parameters.get("b") ?? ""]
    }
})
defer { app.shutdown() }

let server = try app.testable().start()
defer { server.shutdown() }

try server.test(.GET, "/hello/vapor") { res in
    XCTAssertEqual(res.status, .ok)
    XCTAssertContains(res.body.string, "vapor")
}.test(.POST, "/hello/vapor") { res in
    XCTAssertEqual(res.status, .notFound)
}.test(.GET, "/hello/vapor/development") { res in
    XCTAssertEqual(res.status, .ok)
    XCTAssertEqual(res.body.string, #"["vapor","development"]"#)
}
  • Fixed an issue causing Request.query.get / Request.query.subscript to crash. (#2018)

Vapor 4.0.0 Alpha 2.1 - 2019-08-22 20:03:00

Fixed:

  • Fixed a compile time error due to changes in AsyncHTTPClient alpha 2.

Vapor 4.0.0 Alpha 2 - 2019-08-02 17:42:16

  • Updated to OpenCrypto alpha 2 (#2031)
  • Updated to SSWG's official AsyncHTTPClient package (#2031)
  • Merged server and client websocket code into WebSocket (#2031)
// client 
return WebSocket.connect(
    to: "ws://echo.websocket.org/",
    on: req.eventLoop
) { ws in
    ws.send("Hello, world!")
    ws.onText { ws, text in
        promise.succeed(text)
        ws.close().cascadeFailure(to: promise)
    }
}

// server
router.webSocket("bar") { req, ws in
    ws.send("Hello, world!")
    ws.onText { ws, text in
        promise.succeed(text)
        ws.close().cascadeFailure(to: promise)
    }
}
  • BCrypt renamed to Bcrypt and included in Vapor (#2031)
let hash = try Bcrypt.hash("vapor")
print(hash) // $2b$12$Lmw/Zx2jSXgxE.r/8uipROCoh64KdPL7/mdEz38EqEFZDEu5JsAH2
try Bcrypt.verify("vapor", created: hash) // true
try Bcrypt.verify("foo", created: hash) // false

Vapor 4.0.0 Alpha 1.5.1 - 2019-07-29 18:30:18

  • Make MiddlewareConfiguration.use non-generic.

Vapor 4.0.0 Alpha 1.5 - 2019-06-25 00:26:15

Fixed:

  • Fixes a channel handler assertion when using WebSocket server (#2010, #2009)

Vapor 4.0.0 Alpha 1.4 - 2019-06-22 00:17:37

New:

  • Adds a new overload to services.singleton that accepts a shutdown closure for cleanup (#2007)

Vapor 4.0.0 Alpha 1.3 - 2019-06-19 17:35:35

Fixed:

  • Client can now modify ClientRequest in a beforeSend closure (.POST can add data)
  • RoutesBuilder now has a delete method

Vapor 4.0.0 Alpha 1.2 - 2019-06-11 23:42:18

New:

  • AsyncKit is now included and exported by default.
  • New application lifecycle hooks added to Provider (#1995)

Fixed:

  • Session authentication middleware now short-circuits if a user has already been authenticated
  • DirectoryConfiguration now uses Custom Working Directory set in the Edit Scheme menu in Xcode
  • Fixed an an assertion failure when attempting to connect via WebSocket to an unknown route (#1997, #2000)

Vapor 4.0.0 Alpha 1.1 - 2019-06-06 17:38:02

New:

  • Updated minimum Swift version to 5.1
  • Temporarily disabled FoundationClient due to SR-10848

Vapor 4.0.0 Alpha 1 - 2019-05-31 21:18:09

More information on Vapor 4 alpha releases:

https://medium.com/@codevapor/vapor-4-alpha-1-releases-begin-94a4bc79dd9a

API Docs:

https://api.vapor.codes/vapor/master/Vapor/index.html

Vapor 3.3.0 - 2019-02-28 18:03:10

New:

  • Adds new supportCompression member to NIOServerConfig. When true, HTTP server will support gzip and deflate. (#1909)
  • ErrorMiddleware now checks for and utilizes ResponseEncodable conformance on errors. (#1875)

Vapor 3.2.2 - 2019-02-15 14:06:12

New:

  • Vapor Compatibility Suite: All PRs to vapor/vapor:3 will run against our new compatibility suite that verifies projects built on Vapor continue to compile and pass tests.
  • Submit your project here: https://forums.swift.org/t/vapor-compatibility-test-suite/20500

Fixed:

  • Reverted retain cycle fix that could cause Application to not deinit. (#1898).

Vapor 3.2.1 - 2019-02-14 14:35:59

Edit: The changes in this tag have been reverted in 3.2.2.

Fixed:

  • Fixes a crash introduced in 3.2.0 when using req.client(). (#1896, #1895, #1894)

⚠️ This patch removes the Container.client() method.

Use the following instead:

let client = try container.make(Client.self)
let client: Client = try container.make()

Application, Request, and Response still have the client() method.

let res = try req.client().get("https://vapor.codes")