upgrading section + content advantages

This commit is contained in:
tanner0101 2018-05-04 14:07:40 -04:00
parent 7c8d45905e
commit e3ecf1d3aa
3 changed files with 137 additions and 0 deletions

View File

@ -283,6 +283,68 @@ let flags: Flags ...
try req.query.encode(flags)
```
## Dynamic Properties
One of the most frequently asked questions regarding `Content` is:
> How do I add a property to just this response?
The way Vapor 3 handles `Content` is based entirely on `Codable`. At no point (that is publically accessible) is your data in an arbitrary data structure like `[String: Any]` that you can mutate at will. Because of this, all data structures that your app accepts and returns _must_ be statically defined.
Let's take a look at a common scenario to better understand this. Very often when you are creating a user, there are a couple different data formats required:
- create: password should be supplied twice to check values match
- internal: you should store a hash not the plaintext password
- public: when listing users, the password hash should not be included
To do this, you should create three types.
```swift
// Data required to create a user
struct UserCreate: Content {
var email: String
var password: String
var passwordCheck: String
}
// Our internal User representation
struct User: Model {
var id: Int?
var email: String
var passwordHash: Data
}
// Public user representation
struct PublicUser: Content {
var id: Int
var email: String
}
// Create a router for POST /users
router.post(UserCreate.self, at: "users") { req, userCreate -> PublicUser in
guard userCreate.password == passwordCheck else { /* some error */ }
let hasher = try req.make(/* some hasher */)
let user = try User(
email: userCreate.email,
passwordHash: hasher.hash(userCreate.password)
)
// save user
return try PublicUser(id: user.requireID(), email: user.email)
}
```
For other methods such as `PATCH` and `PUT`, you may want to create additional types to supports the unique semantics.
### Benefits
This method may seem a bit verbose at first when compared to dynamic solutions, but it has a number of key advantages:
- **Statically Typed**: Very little validation is needed on top of what Swift and Codable do automatically.
- **Readability**: No need for Strings and optional chaining when working with Swift types.
- **Maintainable**: Large projects will appreciate having this information separated and clearly stated.
- **Shareable**: Types defining what content your routes accept and return can be used to conform to specifications like OpenAPI or even be shared directly with clients written in Swift.
- **Performance**: Working with native Swift types is much more performant than mutating `[String: Any]` dictionaries.
## JSON
JSON is a very popular encoding format for APIs and the way in which dates, data, floats, etc are encoded is non-standard. Because of this, Vapor makes it easy to use custom [`JSONDecoder`](#fixme)s when you interact with other APIs.

View File

@ -0,0 +1,74 @@
# Upgrading Versions
This document provides information about changes between version and tips for migrating your projects.
## 2.4 to 3.0
Vapor 3 has been rewritten from the ground up to be async and event-driven. This release contains the most changes of any previous release (and most likely any future release).
Because of this, it is recommended that to migrate your projects you start by creating a new, empty template and migrate by copy / pasting code over to the new project.
We recommend reading the [Getting Started → Hello, world!](../getting-started/hello-world.md) section for Vapor 3 to familiarize yourself with the new APIs.
### Async
The biggest change in Vapor 3 is that the framework is now completely asynchronous. When you call methods that need to perform slow work like network requests or disk access instead of blocking they will now return a `Future<T>`.
Futures are values that may not exist yet, so you cannot interact with them directly. Instead, you must use `map`/`flatMap` to access the values.
```swift
// vapor 2
let res = try drop.client.get("http://vapor.codes")
print(res.status) // HTTPStatus
return res.status
// vapor 3
let f = try req.client().get("http://vapor.codes").map { res in
print(res.http.status) // HTTPStatus
return res.http.status
}
print(f) // Future<HTTPStatus>
```
See [Async &rarr; Getting Started](../async/getting-started.md) to learn more.
### Application & Services
`Droplet` has been renamed to `Application` and is now a service-container. In Vapor 2, the `Droplet` had stored properties for things you would need during development (like views, hashers, etc). In Vapor 3, this is all done via services.
While the `Application` _is_ a service-container, you should not use it from your route closures. This is to prevent race conditions since Vapor runs on multiple threads (event loops). Instead, use the `Request` that is supplied to your route closure. This has a _copy_ of all of the application's services for you to use.
```swift
// vapor 2
return try drop.view.make("myView")
// vapor 3
return try req.make(ViewRenderer.self).render("myView")
// shorthand
return try req.view().render("myView")
```
See [Service &rarr; Getting Started](../service/getting-started.md) to learn more.
### Database Connections
In Vapor 3, database connections are no longer statically accessible. This makes doing things like transactions and connection pooling much more predictable and performant.
In order to create a `QueryBuilder` in Fluent 3, you will need access to something `DatabaseConnectable`. Most often you can just use the incoming `Request`, but you can also create connections manually if you need.
```swift
// vapor 2
User.makeQuery().all()
// vapor 3
User.query(on: req).all()
```
See [DatabaseKit &rarr; Getting Started](../database-kit/getting-started.md) to learn more.
### Work in progress
This migration guide is a work in progress. Please feel free to add any migration tips here by submitting a PR.
Join the [#upgrading-to-3](https://discordapp.com/invite/BnXmVGA) in Vapor's team chat to ask questions and get help in real time.

View File

@ -116,6 +116,7 @@ pages:
- '1.5': 'version/1_5.md'
- '2.0': 'version/2_0.md'
- '3.0': 'version/3_0.md'
- 'Upgrading': 'version/upgrading.md'
- 'Support': 'version/support.md'