additional docs

This commit is contained in:
Tanner Nelson 2017-11-16 20:36:19 -05:00
parent e6a63cf6a7
commit c7225b92c8
10 changed files with 264 additions and 35 deletions

View File

@ -0,0 +1,32 @@
### This document
This document covers both `Codable` and `Async`, the two primary concepts in Vapor 3. Understanding these 2 concepts is essential, even for existing Vapor 1 and 2 users.
# Codable
Codable is any type that's both `Encodable` and `Decodable`. Encodable types can be serialized to a format, and Decodable types can be deserialized from a format.
If you only want your type to be serializable to another type, then you conform to `Encodable`. This will allow serializing this type to other formats such as JSON, XML, MySQL rows, MongoDB/BSON and more. But not backwards.
If you want to be able to construct your type from the raw data, you can conform your type to `Decodable`. This will allow converting serialized data to your model (the reverse of `Encodable`), allowing JSON, XML, MySQL and MongoDB data to construct your model. This will not allow serialization.
If you want both serialization and deserialization, you can conform to `Codable`.
For the best experience you should conform one of the above protocols in the *definition* of your `struct` or `class`. This way the compiler can infer the protocol requirements automatically. Conforming to these protocols in an extension will require you to manually implement the protocol requirements.
```swift
struct User: Codable {
var username: String
var age: Int
}
```
With this addition, the above struct can now be (de-)serialized between JSON, XML, MongoDB BSON, MySQL and more!
# Async
To understand asynchronous code you must first understand what synchronous code does.
Synchronous code is code that writes top to bottom and executes exactly in that order independent of your use case. It does not use callbacks, it does not use futures and it does not use streams. Many information is not immediately available. The internet has a delay between any communication traffic. Querying a database requires sending the query to the database, waiting for the database to process and execute the request, and then receiving the requested information. To keep code synchronous you need to "block" the thread. This results in rendering the thread unusable until the response has been received. This is, naturally, inefficient. You're wasting a thread and much performance.
The only clean solution here is to do nonblocking operations. This means that once you send the query, you continue to the next line of code immediately without waiting/blocking. The problem that arises is that the next lines of code are dependent on the result of the previous query's results. For this reason, Vapor 3 introduces [Futures](../async/promise-future-introduction.md). Futures are very common in many (high performance) ecosystems.

View File

@ -1 +0,0 @@
TODO

View File

@ -1,41 +1,55 @@
# Application
Every application in Vapor starts as an `Application`. Application is a open class, meaning it _can_ be subclassed to add extra properties, but that's usually not necessary.
Every Vapor project has an `Application`. You use the the application to create any services
you might need while developing.
Application has a [`Config`](../service/config.md) and [`Services`](../service/services.md).
Application may behave differently depending on it's [`Environment`](../service/environment.md).
## Creating a basic application
If not overridden, `Application` comes with it's own set of default services. This makes setting up an empty (basic) application extremely simple.
The best place to access the application is in your project's [`boot.swift`](structure.md#bootswift) file.
```swift
import Vapor
let application = Application()
// Set up your routes etc..
try application.run()
public func boot(_ app: Application) throws {
// your code here
}
```
You can override the default config, environment and services like so:
You can also access the application from your [`routes.swift`](structure.md#routesswift) file. It's stored
as a property there.
```swift
let config: Config = ...
let environment = Environment.production
var services = Services.default()
import Vapor
// configure services, otherwise there's no Server and Router
final class Routes: RouteCollection {
let app: Application
...
}
```
## Application routing services
Unlike some other web frameworks, Vapor doesn't support statically accessing the application.
If you need to access it from another class or struct, you should pass through a method or initializer.
In order to add routes to an `Application` you need to get a router first.
!!! info
Avoiding static access to variables helps make Vapor performant by preventing
the need for thread-safe locks or semaphores.
## Services
The application's main function is to make services. For example, you might need a `BCryptHasher` to hash
some passwords before storing them in a database. You can use the application to create one.
```swift
let router = try app.make(Router.self)
import BCrypt
let bcryptHasher = try app.make(BCryptHasher.self)
```
From here you can start writing [your application's routes](routing.md).
Or you might use the application to create an HTTP client.
```swift
let client = try app.make(Client.self)
let res = client.get("http://vapor.codes")
```
Learn more about services in [Services → Getting Started](../services/getting-started.md).

View File

@ -0,0 +1,56 @@
# Controllers
Controllers are a great way to organization your code. They are collections of methods that accept
a request and return a response.
A good place to put your controllers is in the [Controllers](structure.md#controllers) folder.
## Methods
Let's take a look at an example controller.
```swift
import Vapor
final class HelloController {
func greet(_ req: Request) throws -> String {
return "Hello!"
}
}
```
Controller methods should always accept a `Request` and return something `ResponseRepresentable`.
This also includes [futures](futures.md) whose expectations are `ResponseRepresentable` (i.e, `Future<String>`).
To use this controller, we can simply initialize it, then pass the method to a router.
```swift
let helloController = HelloController()
router.get("greet", use: helloController.greet)
```
## Use Services
You will probably want to access your [application's services](application.md#services) from within your controllers.
Luckily this is easy to do. First, declare what services your controller needs in its init method. Then store them
as properties on the controller.
```swift
final class HelloController {
let hasher: BCryptHasher
init(hasher: BCryptHasher) {
self.hasher = hasher
}
...
}
```
Next, use the [application](application.md) to create these services when you initialize your controller.
```swift
let helloController = try HelloController(
hasher: app.make()
)
```

View File

@ -1,3 +1,101 @@
# Structure
TODO: talk about the structure of vapor applications including folder, module, etc
This section explains the structure of a typical Vapor application to help get
you familiar with where things go.
## Folder Structure
Vapor's folder structure builds on top of [SPM's folder structure](spm#folder-structure).
```
.
├── Public
├── Sources
│ ├── App
│ │ ├── Controllers
│ │ ├── Models
│ │ ├── boot.swift
│ │ ├── configure.swift
│ │ └── routes.swift
│ └── Run
│ └── main.swift
├── Tests
│ └── AppTests
└── Package.swift
```
Let's take a look at what each of these folders and files does.
## Public
This folder contains any public files that will be served by your app.
This is usually images, style sheets, and browser scripts.
Whenever Vapor responds to a request, it will first check if the requested
item is in this folder. If it is, it skips your application logic and returns
the file immediately.
For example, a request to `localhost:8080/favicon.ico` will check to see
if `Public/favicon.ico` exists. If it does, Vapor will return it.
## Sources
This folder contains all of the Swift source files for your project.
The top level folders (`App` and `Run`) reflect your package's modules,
as declared in the [package manifest](spm#targets).
### App
This is the most important folder in your application, it's where all of
the application logic goes!
#### Controllers
Controllers are great way of grouping together application logic. Most controllers
have many functions that accept a request and return some sort of response.
!!! tip
Vapor supports, but does not enforce the MVC pattern
#### Models
The `Models` folder is a great place to store your [`Content`](content.md) structs or
Fluent [`Model`](../fluent/getting-started/models.md)s.
#### boot.swift
This file contains a function that will be called _after_ your application has booted,
but _before_ it has started running. This is a great place do things that should happen
every time your application starts.
You have access to the [`Application`](application.md) here which you can use to create
any [services](application.md#services) you might need.
#### configure.swift
This file contains a function that receives the config, environment, and services for your
application as input. This is a great place to make changes to your config or register
[services](application.md#services) to your application.
#### routes.swift
This file contains the main route collection for your app. This is where you should assign routes
for your controller methods.
You'll notice there's one example route in there that returns the "hello, world" response we saw earlier.
You can create as many route collections as you want to further organize your code. Just make sure
to register them in this main route collection.
## Tests
Each non-executable module in your `Sources` folder should have a corresponding `...Tests` folder.
### AppTests
This folder contains the unit tests for code in your `App` module.
Learn more about testing in [Testing &rarr; Getting Started](../testing/getting-started.md).
## Package.swift
Finally is SPM's [package manifest](spm.md#package-manifest).

View File

@ -0,0 +1,24 @@
## Configure
You configure the application in the [`configure.swift`](structure.md#configureswift) file. Here you can
register your own services, or override the ones provided by default.
```swift
import Vapor
public func configure(...) throws {
services.register {
let foo = FooService(...)
return foo
}
}
```
Later, after your application has booted, you can then request your registered service.
```swift
let foo = try app.make(FooService.self)
```
### Providers

View File

@ -0,0 +1,3 @@
# Testing
TODO

View File

@ -11,19 +11,18 @@ pages:
- 'Toolbox': 'getting-started/toolbox.md'
- 'SPM': 'getting-started/spm.md'
- 'Xcode': 'getting-started/xcode.md'
- 'Structure': 'getting-started/structure.md'
- 'Folder Structure': 'getting-started/structure.md'
- 'Application': 'getting-started/application.md'
- 'Futures': 'getting-started/futures.md'
- 'Controllers': 'getting-started/controllers.md'
- 'Routing': 'getting-started/routing.md'
- 'Content': 'getting-started/content.md'
- 'Futures': 'getting-started/futures.md'
- 'Deployment': 'getting-started/cloud.md'
- 'Concepts':
- 'What is Vapor?': 'concepts/vapor.md'
- 'Overview': 'concepts/overview.md'
- 'Async': 'concepts/async.md'
- 'Codable': 'concepts/codable.md'
- 'HTTP': 'concepts/http.md'
- 'Async and Codable': 'concepts/async-and-codable.md'
- 'Controllers': 'concepts/controllers.md'
- 'Databases': 'concepts/databases.md'
- 'Async':
- 'Package': 'async/package.md'
- 'Future Basics': 'async/futures-basics.md'
@ -81,6 +80,13 @@ pages:
- 'Client': 'websocket/client.md'
- 'Server': 'websocket/server.md'
- 'Server Upgrades': 'websocket/upgrade.md'
- 'Routing':
- 'Basics': 'routing/basics.md'
- 'Parameters': 'routing/parameters.md'
- 'Route': 'routing/route.md'
- 'Router': 'routing/router.md'
- 'Services':
- 'Getting Started': 'services/getting-started.md'
- 'JWT':
- 'Signed Tokens': 'jwt/jws.md'
- 'Crypto':
@ -90,14 +96,11 @@ pages:
- 'Message authentication': 'crypto/mac.md'
- 'Password hashing': 'crypto/passwords.md'
- 'Random': 'crypto/random.md'
- 'Routing':
- 'Basics': 'routing/basics.md'
- 'Parameters': 'routing/parameters.md'
- 'Route': 'routing/route.md'
- 'Router': 'routing/router.md'
- 'Sockets':
- 'Index': 'sockets/index.md'
- 'TCP Client': 'sockets/tcp-client.md'
- 'Testing':
- 'Getting Started': 'testing/getting-started.md'
- 'Security':
- 'Index': 'security/index.md'
- 'Denial of Service': 'security/dos.md'