mirror of https://github.com/vapor/docs.git
additional docs
This commit is contained in:
parent
e6a63cf6a7
commit
c7225b92c8
|
|
@ -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.
|
||||
|
|
@ -1 +0,0 @@
|
|||
TODO
|
||||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
)
|
||||
```
|
||||
|
|
@ -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 → Getting Started](../testing/getting-started.md).
|
||||
|
||||
## Package.swift
|
||||
|
||||
Finally is SPM's [package manifest](spm.md#package-manifest).
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Testing
|
||||
|
||||
TODO
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
Loading…
Reference in New Issue