mirror of https://github.com/vapor/docs.git
Fixed remaining dead links
This commit is contained in:
parent
8bfbe05146
commit
e1d1dbf5df
|
|
@ -1,29 +1,80 @@
|
|||
# Introduction into Promises and Futures
|
||||
# Future basics
|
||||
|
||||
When working with asynchronous APIs, one of the problems you'll face is not knowing when a variable is set.
|
||||
Futures are used throughout Vapor, so it is useful to know some of the available helpers. We explain the reasoning and use cases [here](../concepts/async.md).
|
||||
|
||||
When querying a database synchronously, the thread is blocked until a result has been received. At which point the result will be returned to you and the thread continues from where you left off querying the database.
|
||||
## Adding awaiters to all results
|
||||
|
||||
If you need to handle the results of an operation regardless of success or failure, you can do so by calling the `.addAwaiter` function on a future.
|
||||
|
||||
The awaiter shall be called on completion with a `Result<Expectation>`. This is an enum with either the `Expectation` or an `Error` contained within.
|
||||
|
||||
```swift
|
||||
let user = try database.fetchUser(named: "Admin")
|
||||
let future = Future("Hello world")
|
||||
|
||||
print(user.username)
|
||||
future.addAwaiter { result in
|
||||
switch result {
|
||||
case .expectation(let string):
|
||||
print(string)
|
||||
case .error(let error):
|
||||
print("Error: \(error)")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the asynchronous world, you won't receive a result immediately. Instead, you'll receive a result in a callback.
|
||||
## Flat-Mapping results
|
||||
|
||||
Nested async callbacks can be a pain to unwind. An example of a painfully complex "callback hell" scenario is demonstrated below:
|
||||
|
||||
```swift
|
||||
// Callback `found` will receive the user. If an error occurred, the `onError` callback will be called instead.
|
||||
try database.fetchUser(named: "Admin", found: { user in
|
||||
print(user.username)
|
||||
}, onError: { error in
|
||||
print(error)
|
||||
})
|
||||
app.get("friends") { request in
|
||||
let session = try request.getSessionCookie() as UserSession
|
||||
|
||||
let promise = Promise<View>()
|
||||
|
||||
// Fetch the user
|
||||
try session.user.resolve().then { user in
|
||||
// Returns all the user's friends
|
||||
try user.friends.resolve().then { friends in
|
||||
return try view.make("friends", context: friends, for: request).then { renderedView in
|
||||
promise.complete(renderedView)
|
||||
}.catch(promise.fail)
|
||||
}.catch(promise.fail)
|
||||
}.catch(promise.fail)
|
||||
|
||||
return promise.future
|
||||
}
|
||||
```
|
||||
|
||||
You can imagine code becoming complex. Difficult to read and comprehend.
|
||||
Vapor 3 offers a `flatMap` solution here that will help keep the code readable and maintainable.
|
||||
|
||||
Promises and futures are two types that this library introduces to solve this.
|
||||
```swift
|
||||
app.get("friends") { request in
|
||||
let session = try request.getSessionCookie() as UserSession
|
||||
|
||||
// Fetch the user
|
||||
return try session.user.resolve().flatten { user in
|
||||
// Returns all the user's friends
|
||||
return try user.friends.resolve()
|
||||
}.map { friends in
|
||||
// Flatten replaced this future with
|
||||
return try view.make("friends", context: friends, for: request)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Combining multiple futures
|
||||
|
||||
If you're expecting the same type of result from multiple sources you can group them using the `flatten` function.
|
||||
|
||||
```swift
|
||||
var futures = [Future<String>]()
|
||||
futures.append(Future("Hello"))
|
||||
futures.append(Future("World"))
|
||||
futures.append(Future("Foo"))
|
||||
futures.append(Future("Bar"))
|
||||
|
||||
let futureResults = futures.flatten() // Future<[String]>
|
||||
```
|
||||
|
||||
## Creating a promise
|
||||
|
||||
|
|
@ -157,83 +208,3 @@ If you expect a result with a specified duration, say, 30 seconds:
|
|||
// This will also throw an error if the deadline wasn't met
|
||||
let user = try future.blocked(timeout: .seconds(30))
|
||||
```
|
||||
|
||||
|
||||
|
||||
# Future basics
|
||||
|
||||
Futures are used throughout Vapor, so it is useful to know some of the available helpers.
|
||||
|
||||
## Adding awaiters to all results
|
||||
|
||||
If you need to handle the results of an operation regardless of success or failure, you can do so by calling the `.addAwaiter` function on a future.
|
||||
|
||||
The awaiter shall be called on completion with a `Result<Expectation>`. This is an enum with either the `Expectation` or an `Error` contained within.
|
||||
|
||||
```swift
|
||||
let future = Future("Hello world")
|
||||
|
||||
future.addAwaiter { result in
|
||||
switch result {
|
||||
case .expectation(let string):
|
||||
print(string)
|
||||
case .error(let error):
|
||||
print("Error: \(error)")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Flat-Mapping results
|
||||
|
||||
Nested async callbacks can be a pain to unwind. An example of a painfully complex "callback hell" scenario is demonstrated below:
|
||||
|
||||
```swift
|
||||
app.get("friends") { request in
|
||||
let session = try request.getSessionCookie() as UserSession
|
||||
|
||||
let promise = Promise<View>()
|
||||
|
||||
// Fetch the user
|
||||
try session.user.resolve().then { user in
|
||||
// Returns all the user's friends
|
||||
try user.friends.resolve().then { friends in
|
||||
return try view.make("friends", context: friends, for: request).then { renderedView in
|
||||
promise.complete(renderedView)
|
||||
}.catch(promise.fail)
|
||||
}.catch(promise.fail)
|
||||
}.catch(promise.fail)
|
||||
|
||||
return promise.future
|
||||
}
|
||||
```
|
||||
|
||||
Vapor 3 offers a `flatMap` solution here that will help keep the code readable and maintainable.
|
||||
|
||||
```swift
|
||||
app.get("friends") { request in
|
||||
let session = try request.getSessionCookie() as UserSession
|
||||
|
||||
// Fetch the user
|
||||
return try session.user.resolve().flatten { user in
|
||||
// Returns all the user's friends
|
||||
return try user.friends.resolve()
|
||||
}.map { friends in
|
||||
// Flatten replaced this future with
|
||||
return try view.make("friends", context: friends, for: request)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Combining multiple futures
|
||||
|
||||
If you're expecting the same type of result from multiple sources you can group them using the `flatten` function.
|
||||
|
||||
```swift
|
||||
var futures = [Future<String>]()
|
||||
futures.append(Future("Hello"))
|
||||
futures.append(Future("World"))
|
||||
futures.append(Future("Foo"))
|
||||
futures.append(Future("Bar"))
|
||||
|
||||
let futureResults = futures.flatten() // Future<[String]>
|
||||
```
|
||||
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
Async is a library revolving around two main concepts:
|
||||
|
||||
- [Streams Introduction](streams-introduction.md)
|
||||
- [Streams Basics](streams-basics.md)
|
||||
- [Promises and Futures](promise-future-introduction.md)
|
||||
- [Promises and Futures](futures.md)
|
||||
- [Streams](streams.md)
|
||||
- [Workers](worker.md)
|
||||
|
||||
Together they form the foundation of Vapor 3's data flow.
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
# Introduction into Promises and Futures
|
||||
|
||||
When working with asynchronous APIs, one of the problems you'll face is not knowing when a variable is set.
|
||||
|
||||
When querying a database synchronously, the thread is blocked until a result has been received. At which point the result will be returned to you and the thread continues from where you left off querying the database.
|
||||
|
||||
```swift
|
||||
let user = try database.fetchUser(named: "Admin")
|
||||
|
||||
print(user.username)
|
||||
```
|
||||
|
||||
In the asynchronous world, you won't receive a result immediately. Instead, you'll receive a result in a callback.
|
||||
|
||||
```swift
|
||||
// Callback `found` will receive the user. If an error occurred, the `onError` callback will be called instead.
|
||||
try database.fetchUser(named: "Admin", found: { user in
|
||||
print(user.username)
|
||||
}, onError: { error in
|
||||
print(error)
|
||||
})
|
||||
```
|
||||
|
||||
You can imagine code becoming complex. Difficult to read and comprehend.
|
||||
|
||||
Promises and futures are two types that this library introduces to solve this.
|
||||
|
||||
## Creating a promise
|
||||
|
||||
Promises are important if you're implementing a function that returns a result in the future, such as the database shown above.
|
||||
|
||||
Promises need to be created without a result. They can then be completed with the expectation or an error at any point.
|
||||
|
||||
You can extract a `future` from the `promise` that you can hand to the API consumer.
|
||||
|
||||
```swift
|
||||
// the example `fetchUser` implementation
|
||||
func fetchUser(named name: String) -> Future<User> {
|
||||
// Creates a promise that can be fulfilled in the future
|
||||
let promise = Promise<User>()
|
||||
|
||||
do {
|
||||
// TODO: Run a query asynchronously, looking for the user
|
||||
|
||||
// Initialize the user using the datbase result
|
||||
// This can throw an error if the result is empty or invalid
|
||||
let user = try User(decodingFrom: databaseResult)
|
||||
|
||||
// If initialization is successful, complete the promise.
|
||||
//
|
||||
// Completing the promise will notify the promise's associated future with this user
|
||||
promise.complete(user)
|
||||
} catch {
|
||||
// If initialization is successful, fail the promise.
|
||||
//
|
||||
// Failing the promise will notify the promise's associated future with an error
|
||||
promise.fail(error)
|
||||
}
|
||||
|
||||
// After spawning the asynchronous operation, return the promise's associated future
|
||||
//
|
||||
// The future can then be used by the API consumer
|
||||
return promise.future
|
||||
}
|
||||
```
|
||||
|
||||
## On future completion
|
||||
|
||||
When a promise completes, you can chain the result/error into a closure:
|
||||
|
||||
```swift
|
||||
// The future provided by the above function will be used
|
||||
let future: Future<User> = fetchUser(named: "Admin")
|
||||
|
||||
// `.then`'s closure will be executed on success
|
||||
future.then { user in
|
||||
print(user.username)
|
||||
// `.catch` will catch any error on failure
|
||||
}.catch { error in
|
||||
print(error)
|
||||
}
|
||||
```
|
||||
|
||||
## Catching specific errors
|
||||
|
||||
Sometimes you only care for specific errors, for example, for logging.
|
||||
|
||||
```swift
|
||||
// The future provided by the above function will be used
|
||||
let future: Future<User> = fetchUser(named: "Admin")
|
||||
|
||||
// `.then`'s closure will be executed on success
|
||||
future.then { user in
|
||||
print(user.username)
|
||||
// This `.catch` will only catch `DatabaseError`s
|
||||
}.catch(DatabaseError.self) { databaseError in
|
||||
print(databaseError)
|
||||
// `.catch` will catch any error on failure, including `DatabaseError` types
|
||||
}.catch { error in
|
||||
print(error)
|
||||
}
|
||||
```
|
||||
|
||||
## Mapping results
|
||||
|
||||
Futures can be mapped to different results asynchronously.
|
||||
|
||||
```swift
|
||||
// The future provided by the above function will be used
|
||||
let future: Future<User> = fetchUser(named: "Admin")
|
||||
|
||||
// Maps the user to it's username
|
||||
let futureUsername: Future<String> = future.map { user in
|
||||
return user.username
|
||||
}
|
||||
|
||||
// Mapped futures can be mapped and chained, too
|
||||
futureUsername.then { username in
|
||||
print(username)
|
||||
}
|
||||
```
|
||||
|
||||
## Futures without promise
|
||||
|
||||
In some scenarios you're required to return a `Future` where a `Promise` isn't necessary as you already have the result.
|
||||
|
||||
In these scenarios you can initialize a future with the already completed result.
|
||||
|
||||
```swift
|
||||
// Already completed on initialization
|
||||
let future = Future("Hello world!")
|
||||
|
||||
future.then { string in
|
||||
print(string)
|
||||
}
|
||||
```
|
||||
|
||||
## Synchronous APIs
|
||||
|
||||
Sometimes, an API needs to be used synchronously in a synchronous envinronment.
|
||||
|
||||
Rather than using a synchronous API with all edge cases involved, we recommend using the `try future.blockingAwait()` function.
|
||||
|
||||
```swift
|
||||
// The future provided by the above function will be used
|
||||
let future: Future<User> = fetchUser(named: "Admin")
|
||||
|
||||
// This will either receive the user if the promise was completed or throw an error if the promise was failed.
|
||||
let user: User = try future.blockingAwait()
|
||||
```
|
||||
|
||||
This will wait for a result indefinitely, blocking the thread.
|
||||
|
||||
If you expect a result with a specified duration, say, 30 seconds:
|
||||
|
||||
```swift
|
||||
// This will also throw an error if the deadline wasn't met
|
||||
let user = try future.blocked(timeout: .seconds(30))
|
||||
```
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
# Stream Basics
|
||||
|
||||
Streams are a set of events that occur asynchronously in time. Some events may dispatch immediately, and others may have a delay in nanoseconds, milliseconds, minutes or any other duration. Streams can be linked together to create simple, performant and maintainable software.
|
||||
|
||||
## Chaining streams
|
||||
|
||||
If an `OutputStream`'s Output is the same as an `InputStream`'s input, you can "chain" these streams together to create really performant and readable solutions.
|
||||
|
||||
This doesn't work for all situation, but let's look at an example that *does* accept [base64](../crypto/base64.md).
|
||||
|
||||
```swift
|
||||
client.stream(to: base64encoder).stream(to: client)
|
||||
```
|
||||
|
||||
The result is an "echo" server that base64-encodes incoming data, and replies it back in base64-encoded format.
|
||||
|
||||
Another good example is how Vapor processes requests internally.
|
||||
|
||||
```swift
|
||||
let server = try TCPServer(port: 8080, worker: Worker(queue: myDispatchQueue))
|
||||
|
||||
// Servers are a stream of accepted web client connections
|
||||
// Clients are an input and output stream of bytes
|
||||
server.drain { client in
|
||||
let parser = RequestParser()
|
||||
let router = try yourApplication.make(Router.self)
|
||||
let serialiser = ResponseSerializer()
|
||||
|
||||
// Parses client-sent bytes into the RequestParser
|
||||
let requestStream = client.stream(to: parser)
|
||||
|
||||
// Parses requests to the Vapor router, creating a response
|
||||
let responseStream = requestStream.stream(to: router)
|
||||
|
||||
// Serializes the responses, creating a byte stream
|
||||
let serializedResponseStream = responseStream.stream(to: serializer)
|
||||
|
||||
// Drains the serialized responses back into the client socket
|
||||
serializedResponseStream.drain(into: client)
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, the output of one stream is inputted into another. This ends up taking care of the entire HTTP [Request](../http/request.md)/[Response](../http/response.md) process.
|
||||
|
||||
`Socket -> Request-Data -> Request -> Processing -> Response -> Response-Data -> Socket`
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
# Introduction into Streams
|
||||
|
||||
Streams is a mechanism that you can implement on objects that process any information efficiently and asynchronously without bloat.
|
||||
|
||||
There are three primary stream protocols:
|
||||
|
|
@ -125,3 +123,15 @@ let stringStream = tcpStream.flatMap(utf8String)
|
|||
// `optionalStringStream` is a stream outputting `String?`
|
||||
let optionalStringStream = tcpStream.map(utf8String)
|
||||
```
|
||||
|
||||
## Chaining streams
|
||||
|
||||
If an `OutputStream`'s Output is the same as an `InputStream`'s input, you can "chain" these streams together to create really performant and readable solutions.
|
||||
|
||||
This doesn't work for all situation, but let's look at an example that *does* accept [base64](../crypto/base64.md).
|
||||
|
||||
```swift
|
||||
client.stream(to: base64encoder).stream(to: client)
|
||||
```
|
||||
|
||||
The result is an "echo" server that base64-encodes incoming data, and replies it back in base64-encoded format.
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
# Worker
|
||||
|
||||
Workers are any type that keep track of the current [EventLoop](../concepts/async.md#multi-reactor).
|
||||
Workers are any type that keep track of the current [EventLoop](../concepts/async.md).
|
||||
|
||||
Worker is a simple protocol and can be conformed to if the context can return (by means of a computed property) or contain an EventLoop.
|
||||
|
||||
There are three primary Workers that you can use to access the eventloop and it's queue or context.
|
||||
There are three primary Workers that you can use to access the `EventLoop` and it's queue or context.
|
||||
|
||||
- [Request](../http/request.md) can be used, usually from within a [Route](../routing/basics.md)
|
||||
- [EventLoop](../concepts/eventloop.md#multi-reactor) is itself a worker to ensure the extensions and consumers of Worker can be used, too.
|
||||
- [EventLoop](../concepts/async.md) is itself a worker to ensure the extensions and consumers of Worker can be used, too.
|
||||
- `DispatchQueue` is a worker that will return a *new and clean* EventLoop based on the current DispatchQueue
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ At the heart of HTTP lie the [Request](../http/request.md) and [Response](../htt
|
|||
|
||||
HTTP clients connect to an HTTP server. The clients can send a request to which the server will send a response.
|
||||
|
||||
Bodies contain the concrete information being transferred. Think of the web-page, images, videos, [JSON](../vapor/json.md) and [forms](../http/multipart.md).
|
||||
Bodies contain the concrete information being transferred. Think of the web-page, images, videos, [JSON](../getting-started/json.md) and [forms](../http/multipart.md).
|
||||
|
||||
Headers contain metadata.
|
||||
|
||||
[Cookies](../http/cookies.md) are metadataabout the client that, for example, can be used for identifying users after they've (successfully) logged in. One of these methods are [session tokens](../jwt/index.md).
|
||||
[Cookies](../http/cookies.md) are metadataabout the client that, for example, can be used for identifying users after they've (successfully) logged in. One of these methods are [session tokens](../jwt/package.md).
|
||||
|
||||
Another type of metadata can be related to the content. For example, defining the type of content transferred in the body.
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ Responses have one additional property in addition to the message's properties.
|
|||
|
||||
## Handling requests
|
||||
|
||||
Requests in Vapor will be handled by a [router](../vapor/routing.md). This allows registering a path to a method. For example, registering `.get("users")` will register the path `/users/` to the method `GET`. The responder/closure associated with this route can then handle requests sent to `/users/` with the `GET` method.
|
||||
Requests in Vapor will be handled by a [router](../getting-started/routing.md). This allows registering a path to a method. For example, registering `.get("users")` will register the path `/users/` to the method `GET`. The responder/closure associated with this route can then handle requests sent to `/users/` with the `GET` method.
|
||||
|
||||
## Types of endpoints
|
||||
|
||||
|
|
@ -50,12 +50,12 @@ iOS and Android apps usually communicate with an API, where a web browser such a
|
|||
|
||||
Websites come in two major flavours. Server and client rendered pages. "Rendering" in this context doesn't mean the graphical rendering on your monitor, but instead the way information is injected into the HTML DOM to display the information to the users.
|
||||
|
||||
Server rendered pages make use of a templating system such as [leaf](../leaf/index.md) whereas client rendered pages communicate with an API.
|
||||
Server rendered pages make use of a templating system such as [leaf](../leaf/package.md) whereas client rendered pages communicate with an API.
|
||||
|
||||
### API
|
||||
|
||||
APIs are endpoints that sometimes receive but always reply with raw data. The raw data can be in any format. Most commonly, APIs communicate with [JSON](../vapor/json.md). Sometimes, they communicate with XML or other data types. Vapor can flexibly switch between supported formats, both by official or by community made libraries.
|
||||
APIs are endpoints that sometimes receive but always reply with raw data. The raw data can be in any format. Most commonly, APIs communicate with [JSON](../getting-started/json.md). Sometimes, they communicate with XML or other data types. Vapor can flexibly switch between supported formats, both by official or by community made libraries.
|
||||
|
||||
APIs in Vapor are (almost) always creating using a "MVC" or "Model View Controller" model [which we explain here.](controllers.md)
|
||||
APIs in Vapor are (almost) always creating using a "MVC" or "Model View Controller" model [which we explain here.](../getting-started/controllers.md)
|
||||
|
||||
Designing an API in Vapor is really simple. [We dive into this more here.](application-design.md)
|
||||
Designing an API in Vapor is really simple. [We dive into this from here.](../getting-started/application.md)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ struct User {
|
|||
|
||||
### Streams
|
||||
|
||||
Streams, [as described on this page](../async/streams-introduction.md), are a source of information that calls a single reader's callback. Streams are best used in larger datasets to prevent the query from consuming a large amount of memory. The downside of a stream is that you cannot return all results in a single future. You'll need to stream the results to the other endpoint, too. For HTTP [this is described here.](../http/streaming-response.md)
|
||||
Streams, [as described on this page](../../async/streams.md), are a source of information that calls a single reader's callback. Streams are best used in larger datasets to prevent the query from consuming a large amount of memory. The downside of a stream is that you cannot return all results in a single future. You'll need to stream the results to the other endpoint, too. For HTTP [this is described here.](../../http/body-stream.md)
|
||||
|
||||
Querying a database for a stream of results is achieved through the `stream` function and requires specifying the `Decodable` type that the results need to be deserialized into.
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ This stream will return all results in the ModelStream's output callback which y
|
|||
|
||||
### Futures
|
||||
|
||||
Futures are often easier to use but significantly heavier on your memory and thus performance. [They are thoroughly described here](../async/promise-future-introduction.md)
|
||||
Futures are often easier to use but significantly heavier on your memory and thus performance. [They are thoroughly described here](../../async/futures.md)
|
||||
|
||||
Querying a database for a future is achieved through the `all` function and requires specifying the `Decodable` type that the results need to be deserialized into.
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ The following code creates a new connectionpool to `localhost` and the default M
|
|||
|
||||
The database is the database that is selected and authenticated to. Any future queries will be sent to this database.
|
||||
|
||||
The `worker` is defined in [the async documentation](../async/worker.md).
|
||||
The `worker` is defined in [the async documentation](../../async/worker.md).
|
||||
|
||||
```swift
|
||||
let connectionPool = ConnectionPool(hostname: "localhost", user: "root", password: nil, database: "test-db", worker: worker)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# SQLite Overview
|
||||
|
||||
Let's dive into the [vapor/sqlite](https://github.com/vapor/sqlite) package and
|
||||
Let's dive into the [vapor/sqlite](https://github.com/vapor/sqlite) package and
|
||||
see how to connect to and query a database.
|
||||
|
||||
!!! warning
|
||||
This documentation provides an overview for the SQLite API.
|
||||
This documentation provides an overview for the SQLite API.
|
||||
If you are using SQLite with Fluent, you will likely never need to use
|
||||
this API. Use [Fluent's APIs](../fluent/overview.md) instead.
|
||||
this API. Use [Fluent's APIs](../../fluent/getting-started/package.md) instead.
|
||||
|
||||
Follow the instructions in the [package](package.md) section to add the SQLite package to your project. Once its added, you should be able to use `import SQLite.`
|
||||
|
||||
|
|
@ -46,11 +46,11 @@ let conn = try db.makeConnection(on: .global())
|
|||
```
|
||||
|
||||
!!! note
|
||||
Pay special attention to which `DispatchQueue` you pass to `makeConnection(on:)`.
|
||||
Pay special attention to which `DispatchQueue` you pass to `makeConnection(on:)`.
|
||||
This will be the queue SQLite calls you back on.
|
||||
|
||||
!!! tip
|
||||
If you are using SQLite with Vapor, make sure to pass the [worker](async/worker.md)'s queue here.
|
||||
If you are using SQLite with Vapor, make sure to pass the [worker](../../async/worker.md)'s queue here.
|
||||
|
||||
## Query
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ let query = conn.query("SELECT * FROM users")
|
|||
|
||||
### Binding Values
|
||||
|
||||
If you are executing a query that has input values, you should bind these using parameters.
|
||||
If you are executing a query that has input values, you should bind these using parameters.
|
||||
|
||||
```swift
|
||||
let query = conn.query("INSERT INTO users (name, age) VALUES (?, ?)")
|
||||
|
|
@ -130,7 +130,7 @@ let name = row["name"]?.text // String
|
|||
|
||||
### Run
|
||||
|
||||
Once your query is ready to execute, you simply call `.execute()`. This returns a `Future<Void>`
|
||||
Once your query is ready to execute, you simply call `.execute()`. This returns a `Future<Void>`
|
||||
that will be completed when the query is done executing.
|
||||
|
||||
```swift
|
||||
|
|
@ -162,7 +162,7 @@ let rows = try conn.query("SELECT * FROM users").sync()
|
|||
|
||||
### Example
|
||||
|
||||
Now for the complete example:
|
||||
Now for the complete example:
|
||||
|
||||
```swift
|
||||
import SQLite
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ On top of [vapor/sqlite](https://github.com/vapor/sqlite), we have built [vapor/
|
|||
|
||||
## With Fluent
|
||||
|
||||
SQLite works great with Fluent, you just need to make sure to add the [vapor/fluent-sqlite](https://github.com/vapor/fluent-sqlite)
|
||||
SQLite works great with Fluent, you just need to make sure to add the [vapor/fluent-sqlite](https://github.com/vapor/fluent-sqlite)
|
||||
package to your project.
|
||||
|
||||
To do this, add the Fluent SQLite package to your Package manifest.
|
||||
|
|
@ -27,7 +27,7 @@ let package = Package(
|
|||
)
|
||||
```
|
||||
|
||||
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../spm/manfiest.md).
|
||||
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../../getting-started/spm.md).
|
||||
|
||||
Use `import FluentSQLite` to access SQLite's Fluent compatible APIs.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# Adding Fluent to your Project
|
||||
|
||||
Fluent ([vapor/fluent](https://github.com/vapor/fluent)) is a type-safe, fast, and easy-to-use ORM built for Swift.
|
||||
Fluent ([vapor/fluent](https://github.com/vapor/fluent)) is a type-safe, fast, and easy-to-use ORM built for Swift.
|
||||
It takes advantage of Swift's strong type system to provide an elegant API for your database.
|
||||
|
||||
## Database
|
||||
|
||||
In addition to adding Fluent to your project, you must also add a Fluent compatible database.
|
||||
Fluent does not include any databases by default. All official databases have a getting started guide similar to this one.
|
||||
In addition to adding Fluent to your project, you must also add a Fluent compatible database.
|
||||
Fluent does not include any databases by default. All official databases have a getting started guide similar to this one.
|
||||
|
||||
| database | library | driver | guide |
|
||||
|------------|-------------------------|--------------------------|------------------------------------------------------------------|
|
||||
|
|
@ -16,7 +16,7 @@ Fluent does not include any databases by default. All official databases have a
|
|||
| MongoDB | mongokitten/mongokitten | vapor/fluent-mongokitten | [README.md](http://github.com/vapor/fluent-mongokitten/readme.md)|
|
||||
|
||||
!!! tip
|
||||
Any database can be made to work with Fluent by conforming to its [Database](database-protocol.md) protocol.
|
||||
Any database can be made to work with Fluent by conforming to its [Database](../database.md) protocol.
|
||||
For a list of all compatible database types, search GitHub for the [fluent-driver](https://github.com/topics/fluent-driver) topic.
|
||||
|
||||
## Fluent
|
||||
|
|
@ -39,9 +39,9 @@ let package = Package(
|
|||
)
|
||||
```
|
||||
|
||||
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../spm/manfiest.md).
|
||||
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../../getting-started/spm.md).
|
||||
|
||||
!!! note
|
||||
!!! note
|
||||
Use `import Fluent` to access Fluent's APIs.
|
||||
|
||||
Once you have Fluent added to your project, you are ready to [configure your database(s)](provider.md).
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Configuring Fluent
|
||||
|
||||
Fluent integrates seamlessly into your Vapor project using [services](../getting-started/application.md#services).
|
||||
Fluent integrates seamlessly into your Vapor project using [services](../getting-started/application.md#services).
|
||||
In this section we will add the Fluent service provider to your application and configure your databases.
|
||||
|
||||
!!! warning
|
||||
|
|
@ -18,21 +18,21 @@ import Fluent
|
|||
try services.register(FluentProvider())
|
||||
```
|
||||
|
||||
Register the `FluentProvider` in the [configure section](../getting-started/structure.md#configure) of your application.
|
||||
Register the `FluentProvider` in the [configure section](../../getting-started/structure.md#configure) of your application.
|
||||
|
||||
!!! question
|
||||
Learn more about how service providers work in [Getting Started: Application](../getting-started/application.md#providers)
|
||||
and [Concepts: Services](../concepts/services.md#providers).
|
||||
Learn more about how service providers work in [Getting Started: Application](../../getting-started/application.md#providers)
|
||||
and [Concepts: Services](../../concepts/services.md#providers).
|
||||
|
||||
|
||||
## Config
|
||||
|
||||
Once the service provider has been added, we can configure one or more databases
|
||||
Once the service provider has been added, we can configure one or more databases
|
||||
to be used with Fluent.
|
||||
|
||||
### Identifier
|
||||
|
||||
Each database you use with Fluent must have a unique identifier. The easiest way to
|
||||
Each database you use with Fluent must have a unique identifier. The easiest way to
|
||||
keep track of this identifier is to add it as static extension to `DatabaseIdentifier`.
|
||||
|
||||
```swift
|
||||
|
|
@ -57,13 +57,13 @@ Now we can use the identifier anywhere in our project:
|
|||
req.database(.foo) { ... }
|
||||
```
|
||||
|
||||
The [configure section](../getting-started/structure.md#configure) of your project is a good place to put this extension.
|
||||
The [configure section](../../getting-started/structure.md#configure) of your project is a good place to put this extension.
|
||||
|
||||
### Databases
|
||||
|
||||
Now that we have created a unique identifier for our database, we can register it
|
||||
to our application using `DatabaseConfig`. A good place to do this is in the
|
||||
[configure section](../getting-started/structure.md#configure) of your project.
|
||||
[configure section](../../getting-started/structure.md#configure) of your project.
|
||||
|
||||
You can add databases to the `DatabaseConfig` using either a type (`.self`) or an instance.
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ services.register(databaseConfig)
|
|||
|
||||
#### Instance
|
||||
|
||||
You can also register a pre-initialized database. This is especially useful if you'd
|
||||
You can also register a pre-initialized database. This is especially useful if you'd
|
||||
like to configure two instances of the same database type.
|
||||
|
||||
```swift
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
# Futures
|
||||
|
||||
You may have noticed some APIs in Vapor expect or return a `Future<T>` type.
|
||||
You may have noticed some APIs in Vapor expect or return a `Future<T>` type.
|
||||
If this is your first time hearing about futures, they might seem a little confusing at first.
|
||||
But don't worry, Vapor makes them easy to use.
|
||||
|
||||
## Callbacks
|
||||
|
||||
Futures are a way of representing an object that you don't have yet. You have probably seen
|
||||
APIs that use callbacks before (especially if you have developed iOS apps). Futures are just a
|
||||
APIs that use callbacks before (especially if you have developed iOS apps). Futures are just a
|
||||
way to make APIs with callbacks more user-friendly.
|
||||
|
||||
This is a what a common callback API looks like.
|
||||
|
|
@ -27,7 +27,7 @@ print(users) // Future<[User]>
|
|||
```
|
||||
|
||||
!!! info
|
||||
Futures use fewer system resources than synchronous APIs. This is part of
|
||||
Futures use fewer system resources than synchronous APIs. This is part of
|
||||
what makes Vapor so fast!
|
||||
|
||||
## Chaining
|
||||
|
|
@ -77,5 +77,4 @@ four.do { four in
|
|||
```
|
||||
|
||||
Learn more about Vapor's async architecture in [Concepts → Async](../concepts/async.md).
|
||||
Or take a deeper look at Futures and Promises in [Async → Getting Started](../async/getting-started.md).
|
||||
|
||||
Or take a deeper look at Futures and Promises in [Async → Getting Started](../async/futures.md).
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Request
|
||||
|
||||
When a client connects with an HTTP Server it sends a `Request`. This HTTP request will be processed [as discussed here](../getting-started/http.md) and resolved into a [`Response`](response.md). This is the response in the http [Request/Response model](../getting-started/http.md).
|
||||
When a client connects with an HTTP Server it sends a `Request`. This HTTP request will be processed [as discussed here](../concepts/http.md) and resolved into a [`Response`](response.md). This is the response in the http [Request/Response model](../concepts/http.md).
|
||||
|
||||
Requests consist of a [Method](method.md), [URI](uri.md) and [Headers](../web/headers.md).
|
||||
Requests consist of a [Method](method.md), [URI](uri.md) and [Headers](headers.md).
|
||||
|
||||
Requests can optionally also contain a [Body](body.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Responders are a type capable of [responding](response.md) to a [Request](request.md).
|
||||
|
||||
Responders are always [async](../async/promise-future-introduction.md) by returning a `Future<Response>` by either transforming/mapping an existing future or creating it's own promise.
|
||||
Responders are always [async](../async/futures.md) by returning a `Future<Response>` by either transforming/mapping an existing future or creating it's own promise.
|
||||
|
||||
## Implementing a static Responder
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# HTTP Response
|
||||
|
||||
When a client connects with an HTTP Server it sends a [`Request`](request.md). This HTTP request will be processed [as discussed here](../getting-started/http.md) and resolved into a `Response`. This is the response in the http Request/Response model.
|
||||
When a client connects with an HTTP Server it sends a [`Request`](request.md). This HTTP request will be processed [as discussed here](../concepts/http.md) and resolved into a `Response`. This is the response in the http Request/Response model.
|
||||
|
||||
HTTP's Response object contains a [Status](status.md), [Headers](../web/headers.md) and a [Body](body.md). Before further reading this page, you must have read and understood the previous pages for Status, Headers and Body.
|
||||
HTTP's Response object contains a [Status](status.md), [Headers](headers.md) and a [Body](body.md). Before further reading this page, you must have read and understood the previous pages for Status, Headers and Body.
|
||||
|
||||
Responses are Extensible using the `extend` property. This allow storing additional data for use by integrating libraries.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ This requires a hostname, port and [worker](../async/worker.md). The worker's Di
|
|||
let client = try RedisClient<TCPClient>.connect(worker: worker) // Future<RedisClient<TCPClient>>
|
||||
```
|
||||
|
||||
The `connect` method will return a [Future](../async/promise-future-introduction.md) containing the TCP based Redis Client.
|
||||
The `connect` method will return a [Future](../async/futures.md) containing the TCP based Redis Client.
|
||||
|
||||
## Redis Data Types
|
||||
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ let future = client.run(command: "GET", arguments: ["my-key"]) // Future<RedisDa
|
|||
|
||||
This future will contain the result as specified in the article on the redis command page or an error.
|
||||
|
||||
The future can be used as described in the [Async API](../async/index.md).
|
||||
The future can be used as described in the [Async API](../async/package.md).
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ let notifications = client.subscribe(to: ["some-notification-channel", "other-no
|
|||
|
||||
If you try to use the client after subscribing, all operations will fail. These errors are usually emitted through the Future.
|
||||
|
||||
This stream will receive messages asynchronously from the point of `draining`. This works like [any other async stream](../async/stream-basics.md)
|
||||
This stream will receive messages asynchronously from the point of `draining`. This works like [any other async stream](../async/streams.md)
|
||||
|
||||
Notifications consist of the channel and payload.
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ The `.get` represents the [Method](../http/method.md) you want to use. `to: "hel
|
|||
|
||||
For variable path components you can use [parameters](parameters.md).
|
||||
|
||||
The trailing closure receives a [Request](../http/request.md). The route can throw errors and needs to return a [`Future<ResponseRepresentable>`](../vapor/response.md) conforming type.
|
||||
The trailing closure receives a [Request](../http/request.md). The route can throw errors and needs to return a [`Future<ResponseRepresentable>`](../http/response.md) conforming type.
|
||||
|
||||
## Registering a route using Vapor
|
||||
|
||||
|
|
|
|||
|
|
@ -6,17 +6,17 @@ Router is a protocol that you can conform your own routers to.
|
|||
|
||||
First, create a [Route](route.md) using a [Method](../http/method.md), path and a responder.
|
||||
|
||||
The following example shows an [async route](async.md) with a constant path.
|
||||
The following example shows a route with a constant path.
|
||||
|
||||
```swift
|
||||
let responder = BasicAsyncResponder { request in
|
||||
return Future("Hello world")
|
||||
return "Hello world"
|
||||
}
|
||||
|
||||
let route = Route(method: .get, path: [.constant("hello"), .constant("world")], responder: responder)
|
||||
```
|
||||
|
||||
The following example shows a [synchronous route](sync.md) with a [Parameter](parameters.md):
|
||||
The following example shows a with a [Parameter](parameters.md):
|
||||
|
||||
```swift
|
||||
let responder = BasicSyncResponder { request in
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Both are (partially) prevented by default using the `PeerValidator` which is par
|
|||
|
||||
### How PeerValidator works
|
||||
|
||||
As part of Vapor 3's design goals, all notification-like I/O is implemented using [Streams](../async/streams-basics.md). This also includes the [TCP Server](../sockets/tcp-server.md). The TCP server is seen as a stream of clients/peers that are accepted and then sent to the client. It has a hook called `willAccept`. This closure's input is a `TCPClient`, and the output is a `Bool`. If the returned boolean is `true`, the peer will be accepted where `false` will deny the peer and will close the connection.
|
||||
As part of Vapor 3's design goals, all notification-like I/O is implemented using [Streams](../async/streams.md). This also includes the [TCP Server](../sockets/tcp-server.md). The TCP server is seen as a stream of clients/peers that are accepted and then sent to the client. It has a hook called `willAccept`. This closure's input is a `TCPClient`, and the output is a `Bool`. If the returned boolean is `true`, the peer will be accepted where `false` will deny the peer and will close the connection.
|
||||
|
||||
`PeerValidator` hooks into this capability by looking the peer's address up in it's cache and keeps track of the amount of connections this peer has currently opened to this server. If the counter exceeds a threshold as specified in the `PeerValidator` initializer, the connection will be rejected.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
## Configure
|
||||
|
||||
You configure the application in the [`configure.swift`](structure.md#configureswift) file. Here you can
|
||||
You configure the application in the [`configure.swift`](structure.md) file. Here you can
|
||||
register your own services, or override the ones provided by default.
|
||||
|
||||
```swift
|
||||
|
|
@ -21,4 +21,3 @@ let foo = try app.make(FooService.self)
|
|||
```
|
||||
|
||||
### Providers
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ let client = TCPClient(socket: socket, worker: worker)
|
|||
|
||||
Now that your socket is connected you can start communicating. First, you'll need to start by setting up the handlers for incoming data.
|
||||
|
||||
Since `TCPClient` is a stream, you can use [the introduction](../async/streams-introduction.md) and [the basics](../async/streams-basics.md) of streams for reading the socket's output (incoming data).
|
||||
Since `TCPClient` is a stream, you can use [the introduction](../async/streams.md) of streams for reading the socket's output (incoming data).
|
||||
|
||||
Sending data is done through the `inputStream` function.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,10 @@
|
|||
# MySQL
|
||||
# TLS
|
||||
|
||||
This package is a driver for the [MySQL Database](https://en.wikipedia.org/wiki/MySQL), an [RDBMS](https://en.wikipedia.org/wiki/Relational_database_management_system) oriented towards stability and robustness.
|
||||
|
||||
This driver supports both MySQL and MariaDB. These two databases are almost identical towards the user.
|
||||
|
||||
!!! warning
|
||||
This documentation provides an overview for the MySQL API.
|
||||
If you are using MySQL with Fluent, you will likely never need to use
|
||||
this API. Use [Fluent's APIs](../fluent/overview.md) instead.
|
||||
The TLS packages allow writing TLS/SSL clients and servers.
|
||||
|
||||
### Index
|
||||
|
||||
- [Setup](setup.md)
|
||||
- [Basics](basics.md)
|
||||
- [Client](client.md)
|
||||
|
||||
## Package.swift
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ pages:
|
|||
- 'Folder Structure': 'getting-started/structure.md'
|
||||
- 'Application': 'getting-started/application.md'
|
||||
- 'Controllers': 'getting-started/controllers.md'
|
||||
- 'JSON': 'getting-started/json.md'
|
||||
- 'Routing': 'getting-started/routing.md'
|
||||
- 'Content': 'getting-started/content.md'
|
||||
- 'Futures': 'getting-started/futures.md'
|
||||
|
|
@ -26,9 +27,8 @@ pages:
|
|||
- 'HTTP': 'concepts/http.md'
|
||||
- 'Async':
|
||||
- 'Package': 'async/package.md'
|
||||
- 'Future Basics': 'async/futures-basics.md'
|
||||
- 'Streams Introduction': 'async/streams-introduction.md'
|
||||
- 'Stream Basics': 'async/streams-basics.md'
|
||||
- 'Futures': 'async/futures.md'
|
||||
- 'Streams': 'async/streams.md'
|
||||
- 'Worker': 'async/worker.md'
|
||||
- 'HTTP':
|
||||
- 'Index': 'http/index.md'
|
||||
|
|
@ -43,6 +43,7 @@ pages:
|
|||
- 'Response': 'http/response.md'
|
||||
- 'Responder': 'http/responder.md'
|
||||
- 'Status codes': 'http/status.md'
|
||||
- 'Streaming Body': 'http/body-stream.md'
|
||||
- 'URI': 'http/uri.md'
|
||||
- 'Fluent':
|
||||
- 'Getting Started':
|
||||
|
|
@ -67,6 +68,10 @@ pages:
|
|||
- 'Package': 'databases/mysql/package.md'
|
||||
- 'Setup': 'databases/mysql/setup.md'
|
||||
- 'Basics': 'databases/mysql/basics.md'
|
||||
- 'PostgreSQL':
|
||||
- 'Package': 'databases/postgres/package.md'
|
||||
- 'Setup': 'databases/postgres/setup.md'
|
||||
- 'Basics': 'databases/postgres/basics.md'
|
||||
- 'Redis':
|
||||
- 'Package': 'redis/package.md'
|
||||
- 'Basics': 'redis/basics.md'
|
||||
|
|
@ -99,6 +104,7 @@ pages:
|
|||
- 'Random': 'crypto/random.md'
|
||||
- 'TLS':
|
||||
- 'Package': 'tls/package.md'
|
||||
- 'Client': 'tls/client.md'
|
||||
- 'Sockets':
|
||||
- 'Package': 'sockets/package.md'
|
||||
- 'TCP Client': 'sockets/tcp-client.md'
|
||||
|
|
|
|||
Loading…
Reference in New Issue