mirror of https://github.com/vapor/docs.git
upgrading section + content advantages
This commit is contained in:
parent
7c8d45905e
commit
e3ecf1d3aa
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 → 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 → 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 → 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.
|
||||
|
||||
|
|
@ -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'
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue